Skip to main content

oauth2_test_server/
models.rs

1use chrono::Utc;
2use serde::{Deserialize, Serialize};
3
4/// A registered OAuth2 client (RFC 7591 Dynamic Client Registration).
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct Client {
7    pub client_id: String,
8    pub client_secret: Option<String>,
9    pub redirect_uris: Vec<String>,
10    pub grant_types: Vec<String>,
11    pub response_types: Vec<String>,
12    pub scope: String,
13    pub token_endpoint_auth_method: String,
14    pub client_name: Option<String>,
15    pub client_uri: Option<String>,
16    pub logo_uri: Option<String>,
17    pub contacts: Vec<String>,
18    pub policy_uri: Option<String>,
19    pub tos_uri: Option<String>,
20    pub jwks: Option<serde_json::Value>,
21    pub jwks_uri: Option<String>,
22    pub software_id: Option<String>,
23    pub software_version: Option<String>,
24    pub registration_access_token: Option<String>,
25    pub registration_client_uri: Option<String>,
26}
27
28/// A short-lived authorization code used in the authorization code flow.
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct AuthorizationCode {
31    pub code: String,
32    pub client_id: String,
33    pub redirect_uri: String,
34    pub scope: String,
35    pub expires_at: chrono::DateTime<Utc>,
36    pub code_challenge: Option<String>,
37    pub code_challenge_method: Option<String>,
38    pub user_id: String,
39    pub nonce: Option<String>,
40    pub state: Option<String>,
41}
42
43/// An issued access token (JWT) along with its metadata.
44#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct Token {
46    pub access_token: String,
47    pub refresh_token: Option<String>,
48    pub client_id: String,
49    pub scope: String,
50    pub expires_at: chrono::DateTime<Utc>,
51    pub user_id: String,
52    pub revoked: bool,
53}
54
55/// JWT claims structure used for encoding/decoding access tokens.
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct Claims {
58    pub iss: String,
59    pub sub: String,
60    pub aud: String,
61    pub exp: usize,
62    pub iat: usize,
63    pub scope: Option<String>,
64    pub auth_time: Option<usize>,
65    pub typ: String,
66    pub azp: Option<String>,
67    pub sid: Option<String>,
68    pub jti: String,
69}
70
71/// ID Token claims structure per OpenID Connect Core 1.0.
72/// Contains standard claims plus token hash values for security validation.
73#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct IdTokenClaims {
75    /// Issuer identifier (must match the issuer URL)
76    pub iss: String,
77    /// Subject identifier (unique user ID)
78    pub sub: String,
79    /// Audience (client_id)
80    pub aud: String,
81    /// Expiration time
82    pub exp: usize,
83    /// Issued at time
84    pub iat: usize,
85    /// Authentication time
86    #[serde(skip_serializing_if = "Option::is_none")]
87    pub auth_time: Option<usize>,
88    /// Nonce value from authorization request (must be echoed if present)
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub nonce: Option<String>,
91    /// Access token hash (at_hash) - OIDC Core Section 3.2.2.9
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub at_hash: Option<String>,
94    /// Authorization code hash (c_hash) - OIDC Core Section 3.2.2.9
95    #[serde(skip_serializing_if = "Option::is_none")]
96    pub c_hash: Option<String>,
97    /// Authorized party (client_id)
98    #[serde(skip_serializing_if = "Option::is_none")]
99    pub azp: Option<String>,
100    /// Token type
101    #[serde(skip_serializing_if = "Option::is_none")]
102    pub typ: Option<String>,
103    /// Session ID
104    #[serde(skip_serializing_if = "Option::is_none")]
105    pub sid: Option<String>,
106    /// JWT ID
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub jti: Option<String>,
109    /// User claims (name, email, etc.)
110    #[serde(flatten)]
111    pub user_claims: serde_json::Value,
112}
113
114impl IdTokenClaims {
115    /// Create new ID token claims with standard claims.
116    pub fn new(
117        issuer: &str,
118        subject: &str,
119        audience: &str,
120        expires_at: usize,
121        issued_at: usize,
122    ) -> Self {
123        Self {
124            iss: issuer.to_string(),
125            sub: subject.to_string(),
126            aud: audience.to_string(),
127            exp: expires_at,
128            iat: issued_at,
129            auth_time: Some(issued_at),
130            nonce: None,
131            at_hash: None,
132            c_hash: None,
133            azp: None,
134            typ: Some("IDToken".to_string()),
135            sid: Some(format!("sid-{}", uuid::Uuid::new_v4())),
136            jti: Some(uuid::Uuid::new_v4().to_string()),
137            user_claims: serde_json::json!({}),
138        }
139    }
140
141    /// Set nonce value.
142    pub fn with_nonce(mut self, nonce: &str) -> Self {
143        self.nonce = Some(nonce.to_string());
144        self
145    }
146
147    /// Set access token hash (at_hash).
148    pub fn with_at_hash(mut self, at_hash: &str) -> Self {
149        self.at_hash = Some(at_hash.to_string());
150        self
151    }
152
153    /// Set authorization code hash (c_hash).
154    pub fn with_c_hash(mut self, c_hash: &str) -> Self {
155        self.c_hash = Some(c_hash.to_string());
156        self
157    }
158
159    /// Set authorized party.
160    pub fn with_azp(mut self, azp: &str) -> Self {
161        self.azp = Some(azp.to_string());
162        self
163    }
164
165    /// Set user claims (name, email, picture, etc.).
166    pub fn with_user_claims(mut self, claims: serde_json::Value) -> Self {
167        self.user_claims = claims;
168        self
169    }
170}
171
172/// Device Code flow (RFC 8628) - Request sent by the device
173#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct DeviceCodeRequest {
175    pub client_id: String,
176    pub scope: Option<String>,
177}
178
179/// Device Code flow (RFC 8628) - Response from the authorization server
180#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct DeviceCodeResponse {
182    pub device_code: String,
183    pub user_code: String,
184    pub verification_uri: String,
185    pub verification_uri_complete: Option<String>,
186    pub expires_in: u64,
187    pub interval: u64,
188}
189
190/// Device Code flow (RFC 8628) - Polling token request
191#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct DeviceTokenRequest {
193    pub grant_type: String,
194    pub device_code: String,
195    pub client_id: String,
196    pub client_secret: Option<String>,
197}
198
199/// Device authorization state stored on the server
200#[derive(Debug, Clone)]
201pub struct DeviceAuthorization {
202    pub device_code: String,
203    pub user_code: String,
204    pub client_id: String,
205    pub scope: String,
206    pub expires_at: chrono::DateTime<Utc>,
207    pub user_id: Option<String>,
208    pub approved: bool,
209}