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    pub auth_time: Option<usize>,
87    /// Nonce value from authorization request (must be echoed if present)
88    pub nonce: Option<String>,
89    /// Access token hash (at_hash) - OIDC Core Section 3.2.2.9
90    pub at_hash: Option<String>,
91    /// Authorization code hash (c_hash) - OIDC Core Section 3.2.2.9
92    pub c_hash: Option<String>,
93    /// Authorized party (client_id)
94    pub azp: Option<String>,
95    /// Token type
96    pub typ: Option<String>,
97    /// Session ID
98    pub sid: Option<String>,
99    /// JWT ID
100    pub jti: Option<String>,
101    /// User claims (name, email, etc.)
102    #[serde(flatten)]
103    pub user_claims: serde_json::Value,
104}
105
106impl IdTokenClaims {
107    /// Create new ID token claims with standard claims.
108    pub fn new(
109        issuer: &str,
110        subject: &str,
111        audience: &str,
112        expires_at: usize,
113        issued_at: usize,
114    ) -> Self {
115        Self {
116            iss: issuer.to_string(),
117            sub: subject.to_string(),
118            aud: audience.to_string(),
119            exp: expires_at,
120            iat: issued_at,
121            auth_time: Some(issued_at),
122            nonce: None,
123            at_hash: None,
124            c_hash: None,
125            azp: None,
126            typ: Some("IDToken".to_string()),
127            sid: Some(format!("sid-{}", uuid::Uuid::new_v4())),
128            jti: Some(uuid::Uuid::new_v4().to_string()),
129            user_claims: serde_json::json!({}),
130        }
131    }
132
133    /// Set nonce value.
134    pub fn with_nonce(mut self, nonce: &str) -> Self {
135        self.nonce = Some(nonce.to_string());
136        self
137    }
138
139    /// Set access token hash (at_hash).
140    pub fn with_at_hash(mut self, at_hash: &str) -> Self {
141        self.at_hash = Some(at_hash.to_string());
142        self
143    }
144
145    /// Set authorization code hash (c_hash).
146    pub fn with_c_hash(mut self, c_hash: &str) -> Self {
147        self.c_hash = Some(c_hash.to_string());
148        self
149    }
150
151    /// Set authorized party.
152    pub fn with_azp(mut self, azp: &str) -> Self {
153        self.azp = Some(azp.to_string());
154        self
155    }
156
157    /// Set user claims (name, email, picture, etc.).
158    pub fn with_user_claims(mut self, claims: serde_json::Value) -> Self {
159        self.user_claims = claims;
160        self
161    }
162}
163
164/// Device Code flow (RFC 8628) - Request sent by the device
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct DeviceCodeRequest {
167    pub client_id: String,
168    pub scope: Option<String>,
169}
170
171/// Device Code flow (RFC 8628) - Response from the authorization server
172#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct DeviceCodeResponse {
174    pub device_code: String,
175    pub user_code: String,
176    pub verification_uri: String,
177    pub verification_uri_complete: Option<String>,
178    pub expires_in: u64,
179    pub interval: u64,
180}
181
182/// Device Code flow (RFC 8628) - Polling token request
183#[derive(Debug, Clone, Serialize, Deserialize)]
184pub struct DeviceTokenRequest {
185    pub grant_type: String,
186    pub device_code: String,
187    pub client_id: String,
188    pub client_secret: Option<String>,
189}
190
191/// Device authorization state stored on the server
192#[derive(Debug, Clone)]
193pub struct DeviceAuthorization {
194    pub device_code: String,
195    pub user_code: String,
196    pub client_id: String,
197    pub scope: String,
198    pub expires_at: chrono::DateTime<Utc>,
199    pub user_id: Option<String>,
200    pub approved: bool,
201}