backend/config/auth.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
//! Handles authentication and authorization.
mod claims;
pub mod jwks;
pub mod middleware;
pub mod user_info;
use jsonwebtoken::jwk::JwkSet;
use log::trace;
use serde::Deserialize;
use tokio::sync::OnceCell;
use self::jwks::fetch_keys;
/// Stores the servers [`Config`].
static CONFIG: OnceCell<Config> = OnceCell::const_new();
/// Contains information about the auth server.
#[derive(Debug, Clone)]
pub struct Config {
/// Metadata relevant for Oauth2
pub openid_configuration: OpenIDEndpointConfiguration,
/// The `client_id` the frontend should use to login its users.
pub client_id: String,
/// The [`JwkSet`] that can be used to validate tokens
pub jwk_set: JwkSet,
}
impl Config {
/// Set the [`Config`].
///
/// Needed for tests as static variables are shared by tests.
/// Error is ignored on purpose as this function will be called multiple times.
#[cfg(test)]
pub fn set(config: Self) {
let _ = CONFIG.set(config);
}
/// Initialize the server authorization and authentication.
///
/// # Panics
/// * If it was already initialized.
/// * If the auth server is unreachable or is set up incorrectly.
#[allow(clippy::expect_used)]
pub async fn init(app_config: &crate::config::app::Config) {
trace!("Initializing auth...");
let openid_config =
OpenIDEndpointConfiguration::fetch(app_config.auth_discovery_uri.as_ref()).await;
let config = Self {
client_id: app_config.client_id.clone(),
jwk_set: fetch_keys(&openid_config.jwks_uri).await,
openid_configuration: openid_config,
};
CONFIG.set(config).expect("Already initialized!");
}
/// Get the [`Config`].
///
/// # Panics
/// * If it wasn't initialized.
#[allow(clippy::expect_used)]
pub fn get() -> &'static Self {
CONFIG.get().expect("Not yet initialized!")
}
}
/// Metadata provided by the auth server.
///
/// See [RFC 8414](https://www.rfc-editor.org/rfc/rfc8414.html#section-2) for more detail.
#[derive(Debug, Clone, Default, Deserialize)]
pub struct OpenIDEndpointConfiguration {
/// The base URL of the authorization server
pub issuer: String,
/// URL of the authorization server's authorization endpoint
pub authorization_endpoint: String,
/// URL of the authorization server's token endpoint
pub token_endpoint: String,
/// URL of the authorization server's JWK Set
pub jwks_uri: String,
}
impl OpenIDEndpointConfiguration {
/// Fetch relevant URL endpoints from the auth server.
///
/// # Panics
/// * If the auth server is set up incorrectly. This would always lead to irrecoverable errors.
#[allow(clippy::expect_used)]
async fn fetch(issuer_uri: &str) -> Self {
trace!("Fetching endpoints from discovery endpoint...");
reqwest::get(issuer_uri)
.await
.expect("Error fetching from auth server!")
.json::<Self>()
.await
.expect("Auth server returned invalid keys!")
}
}