backend/config/
auth.rs

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