securitydept-oidc-client 0.2.0-beta.4

OIDC Client of SecurityDept, a layered authentication and authorization toolkit built as reusable Rust crates.
Documentation
use crate::{
    ClaimsCheckResult, IdTokenClaimsWithExtra, OidcError, OidcResult, UserInfoClaimsWithExtra,
};

pub trait ClaimsChecker {
    fn check_claims(
        &self,
        id_token_claims: &IdTokenClaimsWithExtra,
        user_info_claims: Option<&UserInfoClaimsWithExtra>,
    ) -> impl Future<Output = OidcResult<ClaimsCheckResult>>;
}

pub struct DefaultClaimsChecker;

impl ClaimsChecker for DefaultClaimsChecker {
    async fn check_claims(
        &self,
        id_token_claims: &IdTokenClaimsWithExtra,
        user_info_claims: Option<&UserInfoClaimsWithExtra>,
    ) -> OidcResult<ClaimsCheckResult> {
        let name = user_info_claims
            .and_then(|c| c.preferred_username())
            .or_else(|| id_token_claims.preferred_username())
            .map(|v| v.to_string())
            .or_else(|| {
                user_info_claims
                    .and_then(|c| c.nickname())
                    .or_else(|| id_token_claims.nickname())
                    .and_then(|v| v.get(None).map(|v| v.to_string()))
            })
            .unwrap_or_else(|| {
                let sub = user_info_claims
                    .map(|c| c.subject())
                    .unwrap_or_else(|| id_token_claims.subject())
                    .to_string();
                if sub.is_empty() {
                    "Unknown".to_string()
                } else {
                    sub
                }
            });
        let picture = user_info_claims
            .and_then(|c| c.picture())
            .or_else(|| id_token_claims.picture())
            .and_then(|v| v.get(None).map(|v| v.to_string()));
        let id_token_claims_json =
            serde_json::to_value(id_token_claims).map_err(|e| OidcError::Claims {
                message: format!("Failed to convert id token claims to JSON: {e}"),
            })?;

        let transformed_claims = if let Some(user_info_claims) = user_info_claims {
            let user_info_claims_json =
                serde_json::to_value(user_info_claims).map_err(|e| OidcError::Claims {
                    message: format!("Failed to convert user info claims to JSON: {e}"),
                })?;
            if let (
                serde_json::Value::Object(mut id_token_claims_obj),
                serde_json::Value::Object(user_info_claims_obj),
            ) = (id_token_claims_json, user_info_claims_json)
            {
                id_token_claims_obj.extend(user_info_claims_obj);
                id_token_claims_obj
            } else {
                return Err(OidcError::Claims {
                    message: "Failed to convert mixed claims to JSON".to_string(),
                });
            }
        } else {
            if let serde_json::Value::Object(id_token_claims_obj) = id_token_claims_json {
                id_token_claims_obj
            } else {
                return Err(OidcError::Claims {
                    message: "Failed to convert id token claims to JSON".to_string(),
                });
            }
        };
        Ok(ClaimsCheckResult {
            display_name: name,
            picture,
            claims: transformed_claims.into_iter().collect(),
        })
    }
}