loco_keycloak_auth/
lib.rs

1pub mod initializer;
2pub mod settings;
3
4pub use axum_keycloak_auth::decode::KeycloakToken;
5use axum_keycloak_auth::{
6    Url,
7    instance::{KeycloakAuthInstance, KeycloakConfig},
8    layer::KeycloakAuthLayer,
9};
10use loco_rs::prelude::*;
11use settings::{KeycloakSettings, Settings};
12
13/// Keycloak is a struct that holds the Keycloak authentication layer.
14/// ## Usage
15/// ```rust
16/// let keycloak = Keycloak::from_context(ctx).expect("Failed to create Keycloak layer");
17/// let router = Router::new()
18///     .route("/protected", get(protected_handler))
19///    .layer(keycloak.layer);
20/// ```
21pub struct Keycloak {
22    pub layer: KeycloakAuthLayer<String>,
23}
24
25impl Keycloak {
26    pub fn from_context(ctx: &AppContext) -> Result<Self> {
27        build_keycloak_layer(ctx).map(|layer| Keycloak { layer })
28    }
29}
30
31/// This function builds the Keycloak authentication layer using the settings
32/// from the application context.
33pub fn build_keycloak_layer(ctx: &AppContext) -> Result<KeycloakAuthLayer<String>> {
34    let full_settings: Settings = serde_json::from_value(
35        ctx.config
36            .settings
37            .clone()
38            .ok_or_else(|| Error::Message("Missing `settings` in config".into()))?,
39    )
40    .map_err(|err| Error::Message(format!("Invalid settings: {}", err)))?;
41
42    let settings: KeycloakSettings = full_settings.keycloak_settings;
43
44    let instance =
45        KeycloakAuthInstance::new(
46            KeycloakConfig::builder()
47                .server(Url::parse(&settings.url).map_err(|err| {
48                    Error::Message(format!("Invalid Keycloak server URL: {}", err))
49                })?)
50                .realm(settings.realm)
51                .build(),
52        );
53
54    Ok(KeycloakAuthLayer::<String>::builder()
55        .instance(instance)
56        .passthrough_mode(settings.passthrough_mode.0)
57        .persist_raw_claims(settings.persist_raw_claims)
58        .expected_audiences(settings.expected_audiences)
59        .build())
60}