ones-oidc 0.3.4

ONES OpenID Connect client for Rust
Documentation
use crate::identifier::{IdentifierType, NewIdentifier};
use openidconnect::ClientId;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct AuthenticatedEntity {
    pub entity: AuthenticatedEntityKind,
    pub method: AuthenticationMethod,
    /**
     *  - https://idp.example.com/oidc
     */
    pub iss: String,
    /**
     * - User: User ID (IDP)
     * - Device: Device ID (IDP)
     * - Mobile: User ID (IDP)
     */
    pub subject: Uuid,
    /**
     * - User: User ID (IDP, local user record)
     * - Device: Relay ID (local relay record)
     */
    pub entity_id: Uuid,
    pub aud: Option<Uuid>,
    pub scope: Option<String>,
    pub has_idp_master_role: bool,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AuthenticationResult {
    pub entity: AuthenticatedEntityKind,
    pub iss: String,
    pub sub: Uuid,
    pub aud: Option<Uuid>,
    pub scope: Option<String>,
    pub username: Option<String>,
    pub client_id: Option<String>,
    pub method: AuthenticationMethod,
    /** User role claim */
    pub idp_role: Option<String>,
}

impl AuthenticationResult {
    pub fn get_user_identifiers(&self) -> Vec<NewIdentifier> {
        let mut user_identifier = Vec::new();
        let identifier = NewIdentifier {
            kind: IdentifierType::UserId,
            value: self.sub.to_string(),
            issuer: self.iss.clone(),
            user_id: self.sub,
        };
        user_identifier.push(identifier);
        if let Some(username) = &self.username {
            let identifier = NewIdentifier {
                kind: IdentifierType::Username,
                value: username.clone(),
                issuer: self.iss.clone(),
                user_id: self.sub,
            };
            user_identifier.push(identifier);
        }
        if let Some(client_id) = &self.client_id {
            let identifier = NewIdentifier {
                kind: IdentifierType::UserClientId,
                value: client_id.to_string(),
                issuer: self.iss.clone(),
                user_id: self.sub,
            };
            user_identifier.push(identifier);
        }
        user_identifier
    }
}

/**
 * - UserJWT: Best case
 * - UserDevice: only client_id (user mobile), exp, iat, iss, token_type
 * - UserIdp: exp, iat, iss, token_type, scope, sub
 */
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub enum AuthenticationMethod {
    UserJwt,
    UserDevice,
    UserIdp,
    IdpJwt,
    Device,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct JwtPayload {
    pub iss: String,
    // #[serde(skip_serializing_if = "Option::is_none")]
    pub sub: Uuid,
    pub aud: Option<String>,
    pub jti: Option<String>,
    pub iat: u64,
    pub exp: u64,
    pub nbf: Option<u64>,
    pub scope: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub binding_message: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub login_hint: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub login_hint_token: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub resource: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub client_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub username: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub user_client_id: Option<String>,
    /** User role claim */
    #[serde(skip_serializing_if = "Option::is_none")]
    pub idp_role: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub qr_session_id: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
#[serde(rename_all = "snake_case")]
pub enum LoginHintKind {
    LoginHint,
    LoginHintToken,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct LoginHint {
    pub kind: LoginHintKind,
    pub value: String,
}

#[derive(Deserialize, Debug)]
pub struct OidcErrorResponse {
    pub error: String,
    pub error_description: String,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum AuthenticatedEntityKind {
    Device,
    User,
}

// Mobile {"active":true,"client_id":"RJq3ENpGMioYZetmY01r4","exp":1721239533,"iat":1721238933,"iss":"http://127.0.0.1:4000/oidc","token_type":"Bearer"}
// User {"active":true,"client_id":"d4cb9c70-ce95-4ebd-9238-3809da722564","exp":1721243491,"iat":1721239891,"iss":"http://127.0.0.1:4000/oidc","scope":"openid offline_access profile","sub":"a9fa6083-10a2-4128-829e-df20ed9b43af","token_type":"Bearer"}

#[derive(Serialize, Deserialize, Debug)]
pub struct ClientCredentialsIntrospection {
    /** Mobile, User - always present */
    pub active: bool,
    /** Mobile, User - only present when active */
    pub client_id: Option<ClientId>,
    /** Mobile, User - only present when active (per RFC 7662) */
    pub exp: Option<u64>,
    /** Only present when active */
    pub iat: Option<u64>,
    /** Mobile, User - only present when active */
    pub iss: Option<String>,
    /** Mobile, User - only present when active */
    pub token_type: Option<String>,
    /** User - only present when active */
    pub scope: Option<String>,
    /** User - only present when active */
    pub sub: Option<String>,
    /** User role claim - only present when active */
    pub idp_role: Option<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct CibaResponse {
    // #[serde(rename = "authRequestId")]
    pub auth_req_id: String,
    pub expires_in: u64,
    pub interval: Option<u64>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct CibaStatusResponse {
    access_token: String,
    expires_in: u64,
    id_token: Option<String>,
    refresh_token: Option<String>,
    scope: String,
    token_type: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct SubjectIdentity {
    pub subject_type: AuthenticatedEntityKind,
    pub subject: Uuid,
    pub username: Option<String>,
    pub client_id: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub enum LoginStrategy {
    #[serde(rename = "bc")]
    Ciba,
    #[serde(rename = "qr")]
    Qr,
    #[serde(rename = "qr_client")]
    QrClient,
    #[serde(rename = "qr_legacy")]
    QrLegacy,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct QrAuthSessionIdp {
    pub kind: LoginStrategy,
    // transform from sessionId
    #[serde(rename = "sessionId")]
    pub session_id: String,
    // transform from cbUrl
    #[serde(rename = "cbUrl")]
    pub cb_url: String,
    // transform from expIn
    #[serde(rename = "expIn")]
    pub exp_in: u64,
    // transform from exp
    pub exp: Option<u64>,
    pub interval: u64,
    pub aud: Option<String>,
    // transform from authRequestId
    #[serde(rename = "authRequestId")]
    pub auth_request_id: Option<String>,
    // transform from cibaRequestInvoked
    #[serde(rename = "cibaRequestInvoked")]
    pub ciba_request_invoked: bool,
    // transform from loginHintToken
    #[serde(rename = "loginHintToken")]
    pub login_hint_token: Option<String>,
}