WIP: feat: support OIDC id_token
This commit is contained in:
parent
ca84a0f99f
commit
af6904002b
23 changed files with 167 additions and 60 deletions
1
.env
1
.env
|
|
@ -1 +0,0 @@
|
||||||
APP_JWT_SECRET=bc1996ea-5464-424a-9a38-5604f2bc865a
|
|
||||||
BIN
.swp
Normal file
BIN
.swp
Normal file
Binary file not shown.
3
TODO.md
3
TODO.md
|
|
@ -1,5 +1,8 @@
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
|
- [ ] better OIDC support
|
||||||
|
- [ ] better support of `profile` `openid` `email` `roles` scopes
|
||||||
|
|
||||||
- [ ] i18n strings in the http website.
|
- [ ] i18n strings in the http website.
|
||||||
|
|
||||||
- [ ] Instance customization support
|
- [ ] Instance customization support
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
signing_key = "tmp/secrets/signing.key"
|
||||||
|
|
||||||
[instance]
|
[instance]
|
||||||
base_uri = "https://auth.fictive.org"
|
base_uri = "https://auth.fictive.org"
|
||||||
name = "Fictive's auth"
|
name = "Fictive's auth"
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use kernel::{models::authorization::Authorization, repositories::users::get_user_by_id};
|
use kernel::{models::authorization::Authorization, repositories::users::get_user_by_id};
|
||||||
use crate::{
|
use crate::{
|
||||||
services::{app_session::AppClientSession, session::create_token}, token_claims::{AppUserTokenClaims, OAuth2AccessTokenClaims, OIDCIdTokenClaims}, AppState
|
services::{app_session::AppClientSession, session::create_token}, token_claims::{OAuth2AccessTokenClaims, OIDCIdTokenClaims}, AppState
|
||||||
};
|
};
|
||||||
|
|
||||||
const AUTHORIZATION_CODE_TTL_SECONDS: i64 = 120;
|
const AUTHORIZATION_CODE_TTL_SECONDS: i64 = 120;
|
||||||
|
|
@ -81,16 +81,17 @@ pub async fn get_access_token(
|
||||||
|
|
||||||
// 3.1. Generate JWT for OAuth2 client user session
|
// 3.1. Generate JWT for OAuth2 client user session
|
||||||
let access_token_jwt = create_token(
|
let access_token_jwt = create_token(
|
||||||
|
&app_state.config,
|
||||||
&app_state.secrets,
|
&app_state.secrets,
|
||||||
OAuth2AccessTokenClaims::new(
|
OAuth2AccessTokenClaims::new(
|
||||||
&app_state.config,
|
&app_state.config,
|
||||||
&app_client_session.client_id,
|
|
||||||
&user,
|
&user,
|
||||||
authorization.scopes.to_vec()
|
authorization.scopes.to_vec()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
// 3.2. Generate id_token for OIDC client
|
// 3.2. Generate id_token for OIDC client
|
||||||
let id_token_jwt = create_token(
|
let id_token_jwt = create_token(
|
||||||
|
&app_state.config,
|
||||||
&app_state.secrets,
|
&app_state.secrets,
|
||||||
OIDCIdTokenClaims::new(
|
OIDCIdTokenClaims::new(
|
||||||
&app_state.config,
|
&app_state.config,
|
||||||
|
|
|
||||||
50
lib/http_server/src/controllers/api/openid/keys.rs
Normal file
50
lib/http_server/src/controllers/api/openid/keys.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
|
||||||
|
use axum::{extract::State, response::IntoResponse, Json};
|
||||||
|
use fully_pub::fully_pub;
|
||||||
|
use kernel::models::authorization::AuthorizationScope;
|
||||||
|
use serde::Serialize;
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
|
use crate::AppState;
|
||||||
|
|
||||||
|
/// JSON Web Key
|
||||||
|
/// @See https://www.rfc-editor.org/rfc/rfc7517.html
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[fully_pub]
|
||||||
|
struct JWK {
|
||||||
|
kty: String,
|
||||||
|
crv: String,
|
||||||
|
alg: String,
|
||||||
|
key_ops: Vec<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// JSON Web Key set
|
||||||
|
/// @See https://www.rfc-editor.org/rfc/rfc7517.html
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[fully_pub]
|
||||||
|
struct JWKs {
|
||||||
|
keys: Vec<JWK>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_signing_public_key(
|
||||||
|
State(app_state): State<AppState>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let base_url = app_state.config.instance.base_uri;
|
||||||
|
Json(vec![
|
||||||
|
JWK {
|
||||||
|
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
// {"kty":"RSA",
|
||||||
|
// "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx
|
||||||
|
// 4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMs
|
||||||
|
// tn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2
|
||||||
|
// QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbI
|
||||||
|
// SD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqb
|
||||||
|
// w0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
|
||||||
|
// "e":"AQAB",
|
||||||
|
// "alg":"RS256",
|
||||||
|
// "kid":"2011-04-29"}
|
||||||
|
}
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
pub mod well_known;
|
pub mod well_known;
|
||||||
|
pub mod keys;
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,8 @@ struct WellKnownOpenIdConfiguration {
|
||||||
scopes_supported: Vec<String>,
|
scopes_supported: Vec<String>,
|
||||||
response_types_supported: Vec<String>,
|
response_types_supported: Vec<String>,
|
||||||
token_endpoint_auth_methods_supported: Vec<String>,
|
token_endpoint_auth_methods_supported: Vec<String>,
|
||||||
id_token_signing_alg_values_supported: Vec<String>
|
id_token_signing_alg_values_supported: Vec<String>,
|
||||||
|
jwks_uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_well_known_openid_configuration(
|
pub async fn get_well_known_openid_configuration(
|
||||||
|
|
@ -33,7 +34,8 @@ pub async fn get_well_known_openid_configuration(
|
||||||
scopes_supported: AuthorizationScope::iter().map(|v| v.to_string()).collect(),
|
scopes_supported: AuthorizationScope::iter().map(|v| v.to_string()).collect(),
|
||||||
response_types_supported: vec!["code".into()],
|
response_types_supported: vec!["code".into()],
|
||||||
token_endpoint_auth_methods_supported: vec!["client_secret_basic".into()],
|
token_endpoint_auth_methods_supported: vec!["client_secret_basic".into()],
|
||||||
id_token_signing_alg_values_supported: vec!["HS256".into()],
|
id_token_signing_alg_values_supported: vec!["RS256".into()],
|
||||||
|
jwks_uri: format!("{}/.well-known/jwks", base_url)
|
||||||
// jwks_uri:
|
// jwks_uri:
|
||||||
// subject_types_supported
|
// subject_types_supported
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use axum::{extract::State, response::IntoResponse, Extension, Json};
|
||||||
use fully_pub::fully_pub;
|
use fully_pub::fully_pub;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::{token_claims::AppUserTokenClaims, AppState};
|
use crate::{token_claims::OAuth2AccessTokenClaims, AppState};
|
||||||
use kernel::models::user::User;
|
use kernel::models::user::User;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
|
@ -18,11 +18,11 @@ struct ReadUserBasicExtract {
|
||||||
|
|
||||||
pub async fn read_user_basic(
|
pub async fn read_user_basic(
|
||||||
State(app_state): State<AppState>,
|
State(app_state): State<AppState>,
|
||||||
Extension(token_claims): Extension<AppUserTokenClaims>,
|
Extension(token_claims): Extension<OAuth2AccessTokenClaims>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
// 1. This handler require app user authentification (JWT)
|
// 1. This handler require app user authentification (JWT)
|
||||||
let user_res = sqlx::query_as::<_, User>("SELECT * FROM users WHERE id = $1")
|
let user_res = sqlx::query_as::<_, User>("SELECT * FROM users WHERE id = $1")
|
||||||
.bind(&token_claims.user_id)
|
.bind(&token_claims.sub)
|
||||||
.fetch_one(&app_state.db.0)
|
.fetch_one(&app_state.db.0)
|
||||||
.await
|
.await
|
||||||
.expect("To get user from claim");
|
.expect("To get user from claim");
|
||||||
|
|
|
||||||
|
|
@ -91,8 +91,8 @@ pub async fn perform_login(
|
||||||
.await.unwrap();
|
.await.unwrap();
|
||||||
|
|
||||||
let jwt_max_age = Duration::days(15);
|
let jwt_max_age = Duration::days(15);
|
||||||
let claims = UserTokenClaims::new(&user.id, jwt_max_age);
|
let claims = UserTokenClaims::new(&app_state.config, &user.id, jwt_max_age);
|
||||||
let jwt = create_token(&app_state.secrets, claims);
|
let jwt = create_token(&app_state.config, &app_state.secrets, claims);
|
||||||
|
|
||||||
// TODO: handle keep_session boolean from form and specify cookie max age only if this setting
|
// TODO: handle keep_session boolean from form and specify cookie max age only if this setting
|
||||||
// is true
|
// is true
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use utils::parse_basic_auth;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
services::{app_session::AppClientSession, session::verify_token},
|
services::{app_session::AppClientSession, session::verify_token},
|
||||||
token_claims::AppUserTokenClaims,
|
token_claims::OAuth2AccessTokenClaims,
|
||||||
AppState
|
AppState
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -102,14 +102,16 @@ pub async fn enforce_jwt_auth_middleware(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let token_claims: AppUserTokenClaims = match verify_token(&app_state.secrets, jwt) {
|
let token_claims: OAuth2AccessTokenClaims =
|
||||||
Ok(val) => val,
|
match verify_token(&app_state.config, &app_state.secrets, jwt) {
|
||||||
Err(_e) => {
|
Ok(val) => val,
|
||||||
return Err(
|
Err(_e) => {
|
||||||
(StatusCode::UNAUTHORIZED, Html("Unauthorized: The provided JWT is invalid."))
|
dbg!(_e);
|
||||||
);
|
return Err(
|
||||||
}
|
(StatusCode::UNAUTHORIZED, Html("Unauthorized: The provided JWT is invalid."))
|
||||||
};
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
req.extensions_mut().insert(token_claims);
|
req.extensions_mut().insert(token_claims);
|
||||||
Ok(next.run(req).await)
|
Ok(next.run(req).await)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,18 +28,20 @@ pub async fn auth_middleware(
|
||||||
return Ok(next.run(req).await)
|
return Ok(next.run(req).await)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let token_claims: UserTokenClaims = match verify_token(&app_state.secrets, jwt) {
|
let token_claims: UserTokenClaims =
|
||||||
Ok(val) => val,
|
match verify_token(&app_state.config, &app_state.secrets, jwt) {
|
||||||
Err(_e) => {
|
Ok(val) => val,
|
||||||
// UserWebGUI: delete invalid JWT cookie
|
Err(_e) => {
|
||||||
return Err(
|
dbg!(&_e);
|
||||||
(
|
// UserWebGUI: delete invalid JWT cookie
|
||||||
cookies.remove(WEB_GUI_JWT_COOKIE_NAME),
|
return Err(
|
||||||
Redirect::to(&original_uri.to_string())
|
(
|
||||||
)
|
cookies.remove(WEB_GUI_JWT_COOKIE_NAME),
|
||||||
);
|
Redirect::to(&original_uri.to_string())
|
||||||
}
|
)
|
||||||
};
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
req.extensions_mut().insert(token_claims);
|
req.extensions_mut().insert(token_claims);
|
||||||
Ok(next.run(req).await)
|
Ok(next.run(req).await)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,8 @@ pub fn build_router(server_config: &ServerConfig, app_state: AppState) -> Router
|
||||||
.route("/api/user-assets/:asset_id", get(api::public_assets::get_user_asset));
|
.route("/api/user-assets/:asset_id", get(api::public_assets::get_user_asset));
|
||||||
|
|
||||||
let well_known_routes = Router::new()
|
let well_known_routes = Router::new()
|
||||||
.route("/.well-known/openid-configuration", get(api::openid::well_known::get_well_known_openid_configuration));
|
.route("/.well-known/openid-configuration", get(api::openid::well_known::get_well_known_openid_configuration))
|
||||||
|
.route("/.well-known/jwks", get(api::openid::well_known::get_signing_public_key));
|
||||||
|
|
||||||
Router::new()
|
Router::new()
|
||||||
.merge(public_routes)
|
.merge(public_routes)
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,37 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use jsonwebtoken::{encode, decode, Header, Algorithm, Validation, EncodingKey, DecodingKey};
|
use jsonwebtoken::{encode, decode, Header, Algorithm, Validation, EncodingKey, DecodingKey};
|
||||||
use kernel::context::AppSecrets;
|
use kernel::{context::AppSecrets, models::config::Config};
|
||||||
|
|
||||||
|
|
||||||
pub fn create_token<T: Serialize>(secrets: &AppSecrets, claims: T) -> String {
|
pub fn create_token<T: Serialize>(
|
||||||
|
_config: &Config,
|
||||||
|
secrets: &AppSecrets,
|
||||||
|
claims: T
|
||||||
|
) -> String {
|
||||||
let token = encode(
|
let token = encode(
|
||||||
&Header::default(),
|
&Header::new(Algorithm::RS256),
|
||||||
&claims,
|
&claims,
|
||||||
&EncodingKey::from_secret(secrets.jwt_secret.as_bytes())
|
&EncodingKey::from_rsa_pem(&secrets.signing_keypair.1)
|
||||||
|
.expect("To build encoding key from signing key.")
|
||||||
).expect("Create token");
|
).expect("Create token");
|
||||||
|
|
||||||
token
|
token
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_token<T: DeserializeOwned>(secrets: &AppSecrets, jwt: &str) -> Result<T> {
|
pub fn verify_token<T: DeserializeOwned>(
|
||||||
|
config: &Config,
|
||||||
|
secrets: &AppSecrets,
|
||||||
|
jwt: &str
|
||||||
|
) -> Result<T> {
|
||||||
|
let mut validation = Validation::new(Algorithm::RS256);
|
||||||
|
validation.set_issuer(&[config.instance.base_uri.clone()]);
|
||||||
|
validation.set_audience(&[config.instance.base_uri.clone()]);
|
||||||
let token_data = decode::<T>(
|
let token_data = decode::<T>(
|
||||||
jwt,
|
jwt,
|
||||||
&DecodingKey::from_secret(secrets.jwt_secret.as_bytes()),
|
&DecodingKey::from_rsa_pem(&secrets.signing_keypair.0)
|
||||||
&Validation::new(Algorithm::HS256)
|
.expect("To build decoding key from signing key."),
|
||||||
|
&validation
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(token_data.claims)
|
Ok(token_data.claims)
|
||||||
|
|
|
||||||
|
|
@ -12,16 +12,19 @@ struct UserTokenClaims {
|
||||||
/// token expiration
|
/// token expiration
|
||||||
exp: u64,
|
exp: u64,
|
||||||
/// token issuer
|
/// token issuer
|
||||||
iss: String
|
iss: String,
|
||||||
// TODO: add roles
|
// TODO: add roles
|
||||||
|
/// token audience
|
||||||
|
aud: String
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserTokenClaims {
|
impl UserTokenClaims {
|
||||||
pub fn new(user_id: &str, max_age: Duration) -> Self {
|
pub fn new(config: &Config, user_id: &str, max_age: Duration) -> Self {
|
||||||
UserTokenClaims {
|
UserTokenClaims {
|
||||||
sub: user_id.into(),
|
sub: user_id.into(),
|
||||||
exp: get_current_timestamp() + max_age.whole_seconds() as u64,
|
exp: get_current_timestamp() + max_age.whole_seconds() as u64,
|
||||||
iss: "Minauthator".into()
|
iss: config.instance.base_uri.clone(),
|
||||||
|
aud: config.instance.base_uri.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +36,7 @@ impl UserTokenClaims {
|
||||||
struct OAuth2AccessTokenClaims {
|
struct OAuth2AccessTokenClaims {
|
||||||
/// Token issuer (URI to the issuer)
|
/// Token issuer (URI to the issuer)
|
||||||
iss: String,
|
iss: String,
|
||||||
/// Audiance (client_id)
|
/// Audiance (In this case, the audiance is equal to the issuer)
|
||||||
aud: String,
|
aud: String,
|
||||||
/// End-user id assigned by the issuer (user_id)
|
/// End-user id assigned by the issuer (user_id)
|
||||||
sub: String,
|
sub: String,
|
||||||
|
|
@ -44,10 +47,10 @@ struct OAuth2AccessTokenClaims {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OAuth2AccessTokenClaims {
|
impl OAuth2AccessTokenClaims {
|
||||||
pub fn new(config: &Config, client_id: &str, user: &User, scopes: Vec<AuthorizationScope>) -> Self {
|
pub fn new(config: &Config, user: &User, scopes: Vec<AuthorizationScope>) -> Self {
|
||||||
OAuth2AccessTokenClaims {
|
OAuth2AccessTokenClaims {
|
||||||
iss: config.instance.base_uri.clone(),
|
iss: config.instance.base_uri.clone(),
|
||||||
aud: client_id.into(),
|
aud: config.instance.base_uri.clone(),
|
||||||
sub: user.id.clone(),
|
sub: user.id.clone(),
|
||||||
exp: get_current_timestamp() + 86_000,
|
exp: get_current_timestamp() + 86_000,
|
||||||
scopes,
|
scopes,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
pub const DEFAULT_DB_PATH: &str = "/var/lib/minauthator/minauthator.db";
|
pub const DEFAULT_DB_PATH: &str = "/var/lib/minauthator/minauthator.db";
|
||||||
pub const DEFAULT_ASSETS_PATH: &str = "/usr/local/lib/minauthator/assets";
|
pub const DEFAULT_ASSETS_PATH: &str = "/usr/local/lib/minauthator/assets";
|
||||||
pub const DEFAULT_CONFIG_PATH: &str = "/etc/minauthator/config.toml";
|
pub const DEFAULT_CONFIG_PATH: &str = "/etc/minauthator/config.toml";
|
||||||
|
pub const DEFAULT_SIGNING_KEY_PATH: &str = "/etc/minauthator/secrets/jwt.key.pem";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
use std::{env, fs};
|
use std::{env, fs, path::Path};
|
||||||
use anyhow::{Result, Context, anyhow};
|
use anyhow::{Result, Context, anyhow};
|
||||||
use fully_pub::fully_pub;
|
use fully_pub::fully_pub;
|
||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
use sqlx::{Pool, Sqlite};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consts::{DEFAULT_CONFIG_PATH, DEFAULT_DB_PATH}, database::prepare_database, models::config::Config, repositories::storage::Storage
|
consts::{DEFAULT_CONFIG_PATH, DEFAULT_DB_PATH, DEFAULT_SIGNING_KEY_PATH},
|
||||||
|
database::prepare_database,
|
||||||
|
models::config::Config,
|
||||||
|
repositories::storage::Storage
|
||||||
};
|
};
|
||||||
|
|
||||||
/// get server config
|
/// get server config
|
||||||
|
|
@ -26,9 +28,18 @@ struct StartKernelConfig {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[fully_pub]
|
#[fully_pub]
|
||||||
struct AppSecrets {
|
struct AppSecrets {
|
||||||
jwt_secret: String
|
/// RSA keypair (public, private) used to signed the JWT issued by minauthator in PEM conainer format
|
||||||
|
signing_keypair: (Vec<u8>, Vec<u8>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[fully_pub]
|
||||||
|
struct ComputedConfig {
|
||||||
|
signing_public_key: Vec<u8>,
|
||||||
|
signing_private_key: Vec<u8>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[fully_pub]
|
#[fully_pub]
|
||||||
struct KernelContext {
|
struct KernelContext {
|
||||||
|
|
@ -37,6 +48,19 @@ struct KernelContext {
|
||||||
storage: Storage
|
storage: Storage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_signing_keypair(key_path: &str) -> Result<(Vec<u8>, Vec<u8>)> {
|
||||||
|
let key_path = Path::new(key_path);
|
||||||
|
let pub_key_path = key_path.with_extension("pub");
|
||||||
|
let private_key: Vec<u8> = fs::read_to_string(&key_path)
|
||||||
|
.context(format!("Failed to read private key from path {:?}.", key_path))?
|
||||||
|
.as_bytes().to_vec();
|
||||||
|
let public_key: Vec<u8> = fs::read_to_string(&pub_key_path)
|
||||||
|
.context(format!("Failed to read public key from path {:?}.", pub_key_path))?
|
||||||
|
.as_bytes().to_vec();
|
||||||
|
|
||||||
|
Ok((public_key, private_key))
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_kernel_context(start_config: StartKernelConfig) -> Result<KernelContext> {
|
pub async fn get_kernel_context(start_config: StartKernelConfig) -> Result<KernelContext> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
let _ = dotenvy::dotenv();
|
let _ = dotenvy::dotenv();
|
||||||
|
|
@ -50,10 +74,13 @@ pub async fn get_kernel_context(start_config: StartKernelConfig) -> Result<Kerne
|
||||||
let config: Config = get_config(config_path)
|
let config: Config = get_config(config_path)
|
||||||
.expect("Cannot get config.");
|
.expect("Cannot get config.");
|
||||||
|
|
||||||
|
let signing_key_path = config.signing_key.clone().unwrap_or(DEFAULT_SIGNING_KEY_PATH.to_string());
|
||||||
|
|
||||||
|
// optionally load dotenv file
|
||||||
let _ = dotenvy::dotenv();
|
let _ = dotenvy::dotenv();
|
||||||
|
|
||||||
let secrets = AppSecrets {
|
let secrets = AppSecrets {
|
||||||
jwt_secret: env::var("APP_JWT_SECRET")
|
signing_keypair: get_signing_keypair(&signing_key_path)?
|
||||||
.context("Expected APP_JWT_SECRET environment variable to exists.")?
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(KernelContext {
|
Ok(KernelContext {
|
||||||
|
|
|
||||||
|
|
@ -66,11 +66,6 @@ struct Role {
|
||||||
struct Config {
|
struct Config {
|
||||||
instance: InstanceConfig,
|
instance: InstanceConfig,
|
||||||
applications: Vec<Application>,
|
applications: Vec<Application>,
|
||||||
roles: Vec<Role>
|
roles: Vec<Role>,
|
||||||
}
|
signing_key: Option<String>
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
#[fully_pub]
|
|
||||||
struct AppSecrets {
|
|
||||||
jwt_secret: String
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
signing_key = "tmp/secrets/signing.key"
|
||||||
|
|
||||||
[instance]
|
[instance]
|
||||||
base_uri = "http://localhost:8086"
|
base_uri = "http://localhost:8086"
|
||||||
name = "Example org"
|
name = "Example org"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
POST {{ base_url }}/login
|
POST {{ base_url }}/login
|
||||||
[FormParams]
|
[FormParams]
|
||||||
login: root
|
login: john.doe
|
||||||
password: root
|
password: root
|
||||||
HTTP 303
|
HTTP 303
|
||||||
[Captures]
|
[Captures]
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
signing_key = "tmp/secrets/signing.key"
|
||||||
|
|
||||||
[instance]
|
[instance]
|
||||||
base_uri = "http://localhost:8086"
|
base_uri = "http://localhost:8086"
|
||||||
name = "Example org"
|
name = "Example org"
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ handle: root
|
||||||
email: root@johndoe.net
|
email: root@johndoe.net
|
||||||
full_name: John Doe
|
full_name: John Doe
|
||||||
website: https://johndoe.net
|
website: https://johndoe.net
|
||||||
picture: file,john_doe_profile_pic.jpg; image/jpeg
|
avatar: file,john_doe_profile_pic.jpg; image/jpeg
|
||||||
HTTP 200
|
HTTP 200
|
||||||
|
|
||||||
GET {{ base_url }}/me/authorizations
|
GET {{ base_url }}/me/authorizations
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
signing_key = "tmp/secrets/signing.key"
|
||||||
applications = []
|
applications = []
|
||||||
|
|
||||||
roles = []
|
roles = []
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue