ones_oidc/
oidc_types.rs

1use crate::identifier::{IdentifierType, NewIdentifier};
2use openidconnect::ClientId;
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6#[derive(Debug, Clone)]
7#[allow(dead_code)]
8pub struct AuthenticatedEntity {
9    pub entity: AuthenticatedEntityKind,
10    pub method: AuthenticationMethod,
11    /**
12     *  - https://idp.example.com/oidc
13     */
14    pub iss: String,
15    /**
16     * - User: User ID (IDP)
17     * - Device: Device ID (IDP)
18     * - Mobile: User ID (IDP)
19     */
20    pub subject: Uuid,
21    /**
22     * - User: User ID (IDP, local user record)
23     * - Device: Relay ID (local relay record)
24     */
25    pub entity_id: Uuid,
26    pub aud: Option<Uuid>,
27    pub scope: Option<String>,
28    pub has_idp_master_role: bool,
29}
30
31#[derive(Debug, Serialize, Deserialize, Clone)]
32pub struct AuthenticationResult {
33    pub entity: AuthenticatedEntityKind,
34    pub iss: String,
35    pub sub: Uuid,
36    pub aud: Option<Uuid>,
37    pub scope: Option<String>,
38    pub username: Option<String>,
39    pub client_id: Option<String>,
40    pub method: AuthenticationMethod,
41    /** User role claim */
42    pub idp_role: Option<String>,
43}
44
45impl AuthenticationResult {
46    pub fn get_user_identifiers(&self) -> Vec<NewIdentifier> {
47        let mut user_identifier = Vec::new();
48        let identifier = NewIdentifier {
49            kind: IdentifierType::UserId,
50            value: self.sub.to_string(),
51            issuer: self.iss.clone(),
52            user_id: self.sub,
53        };
54        user_identifier.push(identifier);
55        if let Some(username) = &self.username {
56            let identifier = NewIdentifier {
57                kind: IdentifierType::Username,
58                value: username.clone(),
59                issuer: self.iss.clone(),
60                user_id: self.sub,
61            };
62            user_identifier.push(identifier);
63        }
64        if let Some(client_id) = &self.client_id {
65            let identifier = NewIdentifier {
66                kind: IdentifierType::UserClientId,
67                value: client_id.to_string(),
68                issuer: self.iss.clone(),
69                user_id: self.sub,
70            };
71            user_identifier.push(identifier);
72        }
73        user_identifier
74    }
75}
76
77/**
78 * - UserJWT: Best case
79 * - UserDevice: only client_id (user mobile), exp, iat, iss, token_type
80 * - UserIdp: exp, iat, iss, token_type, scope, sub
81 */
82#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
83pub enum AuthenticationMethod {
84    UserJwt,
85    UserDevice,
86    UserIdp,
87    IdpJwt,
88    Device,
89}
90
91#[derive(Debug, Serialize, Deserialize, Clone)]
92pub struct JwtPayload {
93    pub iss: String,
94    // #[serde(skip_serializing_if = "Option::is_none")]
95    pub sub: Uuid,
96    pub aud: Option<String>,
97    pub jti: Option<String>,
98    pub iat: u64,
99    pub exp: u64,
100    pub nbf: Option<u64>,
101    pub scope: Option<String>,
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub binding_message: Option<String>,
104    #[serde(skip_serializing_if = "Option::is_none")]
105    pub login_hint: Option<String>,
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub login_hint_token: Option<String>,
108    #[serde(skip_serializing_if = "Option::is_none")]
109    pub resource: Option<String>,
110    #[serde(skip_serializing_if = "Option::is_none")]
111    pub client_id: Option<String>,
112    #[serde(skip_serializing_if = "Option::is_none")]
113    pub username: Option<String>,
114    #[serde(skip_serializing_if = "Option::is_none")]
115    pub user_client_id: Option<String>,
116    /** User role claim */
117    #[serde(skip_serializing_if = "Option::is_none")]
118    pub idp_role: Option<String>,
119    #[serde(skip_serializing_if = "Option::is_none")]
120    pub qr_session_id: Option<String>,
121}
122
123#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
124#[serde(rename_all = "snake_case")]
125pub enum LoginHintKind {
126    LoginHint,
127    LoginHintToken,
128}
129
130#[derive(Serialize, Deserialize, Debug, Clone)]
131pub struct LoginHint {
132    pub kind: LoginHintKind,
133    pub value: String,
134}
135
136#[derive(Deserialize, Debug)]
137pub struct OidcErrorResponse {
138    pub error: String,
139    pub error_description: String,
140}
141
142#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
143#[serde(rename_all = "lowercase")]
144pub enum AuthenticatedEntityKind {
145    Device,
146    User,
147}
148
149// Mobile {"active":true,"client_id":"RJq3ENpGMioYZetmY01r4","exp":1721239533,"iat":1721238933,"iss":"http://127.0.0.1:4000/oidc","token_type":"Bearer"}
150// 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"}
151
152#[derive(Serialize, Deserialize, Debug)]
153pub struct ClientCredentialsIntrospection {
154    /** Mobile, User - always present */
155    pub active: bool,
156    /** Mobile, User - only present when active */
157    pub client_id: Option<ClientId>,
158    /** Mobile, User - only present when active (per RFC 7662) */
159    pub exp: Option<u64>,
160    /** Only present when active */
161    pub iat: Option<u64>,
162    /** Mobile, User - only present when active */
163    pub iss: Option<String>,
164    /** Mobile, User - only present when active */
165    pub token_type: Option<String>,
166    /** User - only present when active */
167    pub scope: Option<String>,
168    /** User - only present when active */
169    pub sub: Option<String>,
170    /** User role claim - only present when active */
171    pub idp_role: Option<String>,
172}
173
174#[derive(Serialize, Deserialize, Debug)]
175pub struct CibaResponse {
176    // #[serde(rename = "authRequestId")]
177    pub auth_req_id: String,
178    pub expires_in: u64,
179    pub interval: Option<u64>,
180}
181
182#[derive(Serialize, Deserialize, Debug)]
183pub struct CibaStatusResponse {
184    access_token: String,
185    expires_in: u64,
186    id_token: Option<String>,
187    refresh_token: Option<String>,
188    scope: String,
189    token_type: String,
190}
191
192#[derive(Serialize, Deserialize, Debug)]
193pub struct SubjectIdentity {
194    pub subject_type: AuthenticatedEntityKind,
195    pub subject: Uuid,
196    pub username: Option<String>,
197    pub client_id: String,
198}
199
200#[derive(Serialize, Deserialize, Debug)]
201pub enum LoginStrategy {
202    #[serde(rename = "bc")]
203    Ciba,
204    #[serde(rename = "qr")]
205    Qr,
206    #[serde(rename = "qr_client")]
207    QrClient,
208    #[serde(rename = "qr_legacy")]
209    QrLegacy,
210}
211
212#[derive(Serialize, Deserialize, Debug)]
213pub struct QrAuthSessionIdp {
214    pub kind: LoginStrategy,
215    // transform from sessionId
216    #[serde(rename = "sessionId")]
217    pub session_id: String,
218    // transform from cbUrl
219    #[serde(rename = "cbUrl")]
220    pub cb_url: String,
221    // transform from expIn
222    #[serde(rename = "expIn")]
223    pub exp_in: u64,
224    // transform from exp
225    pub exp: Option<u64>,
226    pub interval: u64,
227    pub aud: Option<String>,
228    // transform from authRequestId
229    #[serde(rename = "authRequestId")]
230    pub auth_request_id: Option<String>,
231    // transform from cibaRequestInvoked
232    #[serde(rename = "cibaRequestInvoked")]
233    pub ciba_request_invoked: bool,
234    // transform from loginHintToken
235    #[serde(rename = "loginHintToken")]
236    pub login_hint_token: Option<String>,
237}