auth_framework/tokens/mod.rs
1//! Token management and validation for the authentication framework.
2use crate::errors::{AuthError, Result, TokenError};
3use crate::providers::{OAuthProvider, ProfileExtractor, ProviderProfile};
4use base64::Engine as _;
5use chrono::{DateTime, Utc};
6use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, Validation, decode, encode};
7use rsa::pkcs1::DecodeRsaPublicKey;
8use rsa::pkcs8::DecodePublicKey;
9use rsa::traits::PublicKeyParts;
10use serde::{Deserialize, Serialize};
11use sha2::{Digest, Sha256};
12#[cfg(feature = "postgres-storage")]
13use sqlx::FromRow;
14use std::collections::HashMap;
15use std::time::Duration;
16use uuid::Uuid;
17
18/// An issued authentication token with all associated metadata.
19///
20/// Created by [`TokenManager`] and returned from
21/// [`AuthFramework::authenticate`](crate::auth::AuthFramework::authenticate).
22/// Contains the encoded `access_token` string, optional `refresh_token`,
23/// granted scopes, and contextual [`TokenMetadata`].
24#[cfg_attr(feature = "postgres-storage", derive(FromRow))]
25#[derive(Clone, Serialize, Deserialize)]
26pub struct AuthToken {
27 /// Unique token identifier
28 pub token_id: String,
29
30 /// User identifier this token belongs to
31 pub user_id: String,
32
33 /// Access token value
34 pub access_token: String,
35
36 /// Token type (e.g., "bearer")
37 pub token_type: Option<String>,
38
39 /// Subject claim
40 pub subject: Option<String>,
41
42 /// Token issuer
43 pub issuer: Option<String>,
44
45 /// Optional refresh token
46 pub refresh_token: Option<String>,
47
48 /// When the token was issued
49 pub issued_at: DateTime<Utc>,
50
51 /// When the token expires
52 pub expires_at: DateTime<Utc>,
53
54 /// Scopes granted to this token
55 pub scopes: crate::types::Scopes,
56
57 /// Authentication method used to obtain this token
58 pub auth_method: String,
59
60 /// Client ID that requested this token
61 pub client_id: Option<String>,
62
63 /// User profile data (optional)
64 pub user_profile: Option<ProviderProfile>,
65
66 /// User's permissions
67 pub permissions: crate::types::Permissions,
68
69 /// User's roles
70 pub roles: crate::types::Roles,
71
72 /// Additional token metadata
73 pub metadata: TokenMetadata,
74}
75
76impl std::fmt::Debug for AuthToken {
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 f.debug_struct("AuthToken")
79 .field("token_id", &self.token_id)
80 .field("user_id", &self.user_id)
81 .field("access_token", &"[REDACTED]")
82 .field("token_type", &self.token_type)
83 .field("subject", &self.subject)
84 .field("issuer", &self.issuer)
85 .field(
86 "refresh_token",
87 if self.refresh_token.is_some() {
88 &"Some([REDACTED])"
89 } else {
90 &"None"
91 },
92 )
93 .field("issued_at", &self.issued_at)
94 .field("expires_at", &self.expires_at)
95 .field("scopes", &self.scopes)
96 .field("auth_method", &self.auth_method)
97 .field("client_id", &self.client_id)
98 .field("permissions", &self.permissions)
99 .field("roles", &self.roles)
100 .field("metadata", &self.metadata)
101 .finish()
102 }
103}
104
105/// Builder for creating `AuthToken` instances with fluent API.
106///
107/// Reduces cognitive load when constructing tokens with many optional fields.
108/// Required fields are set in `new()`, optional fields via builder methods.
109///
110/// # Example
111///
112/// ```rust
113/// use auth_framework::tokens::{AuthToken, TokenMetadata};
114/// use auth_framework::types::{Scopes, Permissions, Roles};
115/// use chrono::{Utc, Duration};
116///
117/// let token = AuthToken::builder("user123", "token456", "access_token_here")
118/// .scopes(Scopes::new(vec!["read".to_string(), "write".to_string()]))
119/// .permissions(Permissions::new(vec!["admin".to_string()]))
120/// .roles(Roles::new(vec!["user".to_string()]))
121/// .expires_at(Utc::now() + Duration::hours(1))
122/// .client_id("client123")
123/// .build();
124/// ```
125#[derive(Debug, Clone)]
126pub struct AuthTokenBuilder {
127 token_id: String,
128 user_id: String,
129 access_token: String,
130 token_type: Option<String>,
131 subject: Option<String>,
132 issuer: Option<String>,
133 refresh_token: Option<String>,
134 issued_at: DateTime<Utc>,
135 expires_at: DateTime<Utc>,
136 scopes: crate::types::Scopes,
137 auth_method: String,
138 client_id: Option<String>,
139 user_profile: Option<ProviderProfile>,
140 permissions: crate::types::Permissions,
141 roles: crate::types::Roles,
142 metadata: TokenMetadata,
143}
144
145impl AuthTokenBuilder {
146 /// Create a new builder with required fields.
147 ///
148 /// Sets sensible defaults for optional fields:
149 /// - `issued_at`: current time
150 /// - `expires_at`: 1 hour from now
151 /// - `scopes`, `permissions`, `roles`: empty collections
152 /// - `auth_method`: "unknown"
153 /// - `metadata`: default (empty)
154 pub fn new(
155 token_id: impl Into<String>,
156 user_id: impl Into<String>,
157 access_token: impl Into<String>,
158 ) -> Self {
159 let now = Utc::now();
160 Self {
161 token_id: token_id.into(),
162 user_id: user_id.into(),
163 access_token: access_token.into(),
164 token_type: None,
165 subject: None,
166 issuer: None,
167 refresh_token: None,
168 issued_at: now,
169 expires_at: now + chrono::Duration::hours(1),
170 scopes: crate::types::Scopes::empty(),
171 auth_method: "unknown".to_string(),
172 client_id: None,
173 user_profile: None,
174 permissions: crate::types::Permissions::empty(),
175 roles: crate::types::Roles::empty(),
176 metadata: TokenMetadata::default(),
177 }
178 }
179
180 /// Set the token type (e.g., "bearer").
181 ///
182 /// # Example
183 ///
184 /// ```rust
185 /// use auth_framework::tokens::AuthToken;
186 ///
187 /// let token = AuthToken::builder("t1", "u1", "access")
188 /// .token_type("bearer")
189 /// .build();
190 /// assert_eq!(token.token_type.as_deref(), Some("bearer"));
191 /// ```
192 pub fn token_type(mut self, token_type: impl Into<String>) -> Self {
193 self.token_type = Some(token_type.into());
194 self
195 }
196
197 /// Set the subject claim.
198 ///
199 /// # Example
200 ///
201 /// ```rust
202 /// use auth_framework::tokens::AuthToken;
203 ///
204 /// let token = AuthToken::builder("t1", "u1", "access")
205 /// .subject("user@example.com")
206 /// .build();
207 /// assert_eq!(token.subject.as_deref(), Some("user@example.com"));
208 /// ```
209 pub fn subject(mut self, subject: impl Into<String>) -> Self {
210 self.subject = Some(subject.into());
211 self
212 }
213
214 /// Set the token issuer.
215 ///
216 /// # Example
217 ///
218 /// ```rust
219 /// use auth_framework::tokens::AuthToken;
220 ///
221 /// let token = AuthToken::builder("t1", "u1", "access")
222 /// .issuer("auth.example.com")
223 /// .build();
224 /// assert_eq!(token.issuer.as_deref(), Some("auth.example.com"));
225 /// ```
226 pub fn issuer(mut self, issuer: impl Into<String>) -> Self {
227 self.issuer = Some(issuer.into());
228 self
229 }
230
231 /// Set the refresh token.
232 ///
233 /// # Example
234 ///
235 /// ```rust
236 /// use auth_framework::tokens::AuthToken;
237 ///
238 /// let token = AuthToken::builder("t1", "u1", "access")
239 /// .refresh_token("refresh_token_value")
240 /// .build();
241 /// assert!(token.refresh_token.is_some());
242 /// ```
243 pub fn refresh_token(mut self, refresh_token: impl Into<String>) -> Self {
244 self.refresh_token = Some(refresh_token.into());
245 self
246 }
247
248 /// Set the issued timestamp.
249 ///
250 /// # Example
251 ///
252 /// ```rust
253 /// use auth_framework::tokens::AuthToken;
254 /// use chrono::Utc;
255 ///
256 /// let now = Utc::now();
257 /// let token = AuthToken::builder("t1", "u1", "access")
258 /// .issued_at(now)
259 /// .build();
260 /// assert_eq!(token.issued_at, now);
261 /// ```
262 pub fn issued_at(mut self, issued_at: DateTime<Utc>) -> Self {
263 self.issued_at = issued_at;
264 self
265 }
266
267 /// Set the expiration timestamp.
268 ///
269 /// # Example
270 ///
271 /// ```rust
272 /// use auth_framework::tokens::AuthToken;
273 /// use chrono::{Utc, Duration};
274 ///
275 /// let expires = Utc::now() + Duration::hours(2);
276 /// let token = AuthToken::builder("t1", "u1", "access")
277 /// .expires_at(expires)
278 /// .build();
279 /// ```
280 pub fn expires_at(mut self, expires_at: DateTime<Utc>) -> Self {
281 self.expires_at = expires_at;
282 self
283 }
284
285 /// Set the granted scopes.
286 ///
287 /// # Example
288 ///
289 /// ```rust
290 /// use auth_framework::tokens::AuthToken;
291 /// use auth_framework::types::Scopes;
292 ///
293 /// let token = AuthToken::builder("t1", "u1", "access")
294 /// .scopes(Scopes::new(vec!["read".into(), "write".into()]))
295 /// .build();
296 /// ```
297 pub fn scopes(mut self, scopes: crate::types::Scopes) -> Self {
298 self.scopes = scopes;
299 self
300 }
301
302 /// Set the authentication method.
303 ///
304 /// # Example
305 ///
306 /// ```rust
307 /// use auth_framework::tokens::AuthToken;
308 ///
309 /// let token = AuthToken::builder("t1", "u1", "access")
310 /// .auth_method("password")
311 /// .build();
312 /// assert_eq!(token.auth_method, "password");
313 /// ```
314 pub fn auth_method(mut self, auth_method: impl Into<String>) -> Self {
315 self.auth_method = auth_method.into();
316 self
317 }
318
319 /// Set the client ID.
320 ///
321 /// # Example
322 ///
323 /// ```rust
324 /// use auth_framework::tokens::AuthToken;
325 ///
326 /// let token = AuthToken::builder("t1", "u1", "access")
327 /// .client_id("client-app")
328 /// .build();
329 /// assert_eq!(token.client_id.as_deref(), Some("client-app"));
330 /// ```
331 pub fn client_id(mut self, client_id: impl Into<String>) -> Self {
332 self.client_id = Some(client_id.into());
333 self
334 }
335
336 /// Set the user profile.
337 ///
338 /// # Example
339 ///
340 /// ```rust,ignore
341 /// use auth_framework::tokens::AuthToken;
342 /// use auth_framework::providers::ProviderProfile;
343 ///
344 /// let profile = ProviderProfile { /* ... */ };
345 /// let token = AuthToken::builder("t1", "u1", "access")
346 /// .user_profile(profile)
347 /// .build();
348 /// ```
349 pub fn user_profile(mut self, user_profile: ProviderProfile) -> Self {
350 self.user_profile = Some(user_profile);
351 self
352 }
353
354 /// Set the user permissions.
355 ///
356 /// # Example
357 ///
358 /// ```rust
359 /// use auth_framework::tokens::AuthToken;
360 /// use auth_framework::types::Permissions;
361 ///
362 /// let token = AuthToken::builder("t1", "u1", "access")
363 /// .permissions(Permissions::new(vec!["admin".into()]))
364 /// .build();
365 /// ```
366 pub fn permissions(mut self, permissions: crate::types::Permissions) -> Self {
367 self.permissions = permissions;
368 self
369 }
370
371 /// Set the user roles.
372 ///
373 /// # Example
374 ///
375 /// ```rust
376 /// use auth_framework::tokens::AuthToken;
377 /// use auth_framework::types::Roles;
378 ///
379 /// let token = AuthToken::builder("t1", "u1", "access")
380 /// .roles(Roles::new(vec!["editor".into()]))
381 /// .build();
382 /// ```
383 pub fn roles(mut self, roles: crate::types::Roles) -> Self {
384 self.roles = roles;
385 self
386 }
387
388 /// Set the token metadata.
389 ///
390 /// # Example
391 ///
392 /// ```rust
393 /// use auth_framework::tokens::{AuthToken, TokenMetadata};
394 ///
395 /// let meta = TokenMetadata::builder().issued_ip("10.0.0.1").build();
396 /// let token = AuthToken::builder("t1", "u1", "access")
397 /// .metadata(meta)
398 /// .build();
399 /// ```
400 pub fn metadata(mut self, metadata: TokenMetadata) -> Self {
401 self.metadata = metadata;
402 self
403 }
404
405 /// Build the `AuthToken` instance.
406 ///
407 /// # Example
408 ///
409 /// ```rust
410 /// use auth_framework::tokens::AuthToken;
411 ///
412 /// let token = AuthToken::builder("t1", "u1", "access").build();
413 /// assert_eq!(token.user_id, "u1");
414 /// ```
415 pub fn build(self) -> AuthToken {
416 AuthToken {
417 token_id: self.token_id,
418 user_id: self.user_id,
419 access_token: self.access_token,
420 token_type: self.token_type,
421 subject: self.subject,
422 issuer: self.issuer,
423 refresh_token: self.refresh_token,
424 issued_at: self.issued_at,
425 expires_at: self.expires_at,
426 scopes: self.scopes,
427 auth_method: self.auth_method,
428 client_id: self.client_id,
429 user_profile: self.user_profile,
430 permissions: self.permissions,
431 roles: self.roles,
432 metadata: self.metadata,
433 }
434 }
435}
436
437impl AuthToken {
438 /// Start building an `AuthToken` with fluent setters.
439 ///
440 /// # Example
441 ///
442 /// ```rust
443 /// use auth_framework::tokens::AuthToken;
444 ///
445 /// let token = AuthToken::builder("token123", "user456", "access_token")
446 /// .expires_at(chrono::Utc::now() + chrono::Duration::hours(2))
447 /// .build();
448 /// ```
449 pub fn builder(
450 token_id: impl Into<String>,
451 user_id: impl Into<String>,
452 access_token: impl Into<String>,
453 ) -> AuthTokenBuilder {
454 AuthTokenBuilder::new(token_id, user_id, access_token)
455 }
456}
457
458/// Contextual information attached to a token at issuance time.
459///
460/// Tracks revocation state, usage counters, and client fingerprints
461/// (IP, user-agent, device). Defaults to an empty / non-revoked state.
462#[derive(Debug, Clone, Serialize, Deserialize, Default)]
463pub struct TokenMetadata {
464 /// IP address where the token was issued
465 pub issued_ip: Option<String>,
466
467 /// User agent of the client
468 pub user_agent: Option<String>,
469
470 /// Device identifier
471 pub device_id: Option<String>,
472
473 /// Session identifier
474 pub session_id: Option<String>,
475
476 /// Whether this token has been revoked
477 pub revoked: bool,
478
479 /// When the token was revoked (if applicable)
480 pub revoked_at: Option<DateTime<Utc>>,
481
482 /// Reason for revocation
483 pub revoked_reason: Option<String>,
484
485 /// Last time this token was used
486 pub last_used: Option<DateTime<Utc>>,
487
488 /// Number of times this token has been used
489 pub use_count: u64,
490
491 /// Custom metadata
492 pub custom: HashMap<String, serde_json::Value>,
493}
494
495impl TokenMetadata {
496 /// Start building a `TokenMetadata` with fluent setters.
497 ///
498 /// # Example
499 ///
500 /// ```rust
501 /// use auth_framework::tokens::TokenMetadata;
502 /// let meta = TokenMetadata::builder()
503 /// .issued_ip("10.0.0.1")
504 /// .user_agent("curl/8.0")
505 /// .device_id("device-abc")
506 /// .build();
507 /// assert_eq!(meta.issued_ip.as_deref(), Some("10.0.0.1"));
508 /// ```
509 pub fn builder() -> TokenMetadataBuilder {
510 TokenMetadataBuilder::default()
511 }
512}
513
514/// Fluent builder for [`TokenMetadata`].
515#[derive(Debug, Clone, Default)]
516pub struct TokenMetadataBuilder {
517 inner: TokenMetadata,
518}
519
520impl TokenMetadataBuilder {
521 /// Set the IP address that issued the token.
522 ///
523 /// # Example
524 ///
525 /// ```rust
526 /// use auth_framework::tokens::TokenMetadata;
527 ///
528 /// let meta = TokenMetadata::builder().issued_ip("10.0.0.1").build();
529 /// assert_eq!(meta.issued_ip.as_deref(), Some("10.0.0.1"));
530 /// ```
531 pub fn issued_ip(mut self, ip: impl Into<String>) -> Self {
532 self.inner.issued_ip = Some(ip.into());
533 self
534 }
535
536 /// Set the user-agent string of the issuing client.
537 ///
538 /// # Example
539 ///
540 /// ```rust
541 /// use auth_framework::tokens::TokenMetadata;
542 ///
543 /// let meta = TokenMetadata::builder().user_agent("curl/8.0").build();
544 /// assert_eq!(meta.user_agent.as_deref(), Some("curl/8.0"));
545 /// ```
546 pub fn user_agent(mut self, ua: impl Into<String>) -> Self {
547 self.inner.user_agent = Some(ua.into());
548 self
549 }
550
551 /// Set the device identifier.
552 ///
553 /// # Example
554 ///
555 /// ```rust
556 /// use auth_framework::tokens::TokenMetadata;
557 ///
558 /// let meta = TokenMetadata::builder().device_id("phone-123").build();
559 /// assert_eq!(meta.device_id.as_deref(), Some("phone-123"));
560 /// ```
561 pub fn device_id(mut self, id: impl Into<String>) -> Self {
562 self.inner.device_id = Some(id.into());
563 self
564 }
565
566 /// Set the associated session identifier.
567 ///
568 /// # Example
569 ///
570 /// ```rust
571 /// use auth_framework::tokens::TokenMetadata;
572 ///
573 /// let meta = TokenMetadata::builder().session_id("sess-abc").build();
574 /// assert_eq!(meta.session_id.as_deref(), Some("sess-abc"));
575 /// ```
576 pub fn session_id(mut self, id: impl Into<String>) -> Self {
577 self.inner.session_id = Some(id.into());
578 self
579 }
580
581 /// Insert a custom metadata entry.
582 ///
583 /// # Example
584 ///
585 /// ```rust
586 /// use auth_framework::tokens::TokenMetadata;
587 ///
588 /// let meta = TokenMetadata::builder()
589 /// .custom("region", serde_json::json!("us-east-1"))
590 /// .build();
591 /// assert_eq!(meta.custom.get("region").unwrap(), "us-east-1");
592 /// ```
593 pub fn custom(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
594 self.inner.custom.insert(key.into(), value);
595 self
596 }
597
598 /// Consume the builder and return the finished [`TokenMetadata`].
599 ///
600 /// # Example
601 ///
602 /// ```rust
603 /// use auth_framework::tokens::TokenMetadata;
604 ///
605 /// let meta = TokenMetadata::builder()
606 /// .issued_ip("127.0.0.1")
607 /// .user_agent("test-client")
608 /// .build();
609 /// assert!(!meta.revoked);
610 /// ```
611 pub fn build(self) -> TokenMetadata {
612 self.inner
613 }
614}
615
616#[cfg(feature = "postgres-storage")]
617use sqlx::{Decode, Postgres, Type, postgres::PgValueRef};
618
619#[cfg(feature = "postgres-storage")]
620impl<'r> Decode<'r, Postgres> for TokenMetadata {
621 fn decode(value: PgValueRef<'r>) -> std::result::Result<Self, sqlx::error::BoxDynError> {
622 let json: serde_json::Value = <serde_json::Value as Decode<Postgres>>::decode(value)?;
623 serde_json::from_value(json).map_err(|e| Box::new(e) as sqlx::error::BoxDynError)
624 }
625}
626
627#[cfg(feature = "postgres-storage")]
628impl Type<Postgres> for TokenMetadata {
629 fn type_info() -> sqlx::postgres::PgTypeInfo {
630 <serde_json::Value as Type<Postgres>>::type_info()
631 }
632 fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
633 <serde_json::Value as Type<Postgres>>::compatible(ty)
634 }
635}
636
637/// Lightweight user information extracted from a validated token.
638///
639/// Returned by [`AuthFramework::get_token_info`](crate::auth::AuthFramework)
640/// after token validation succeeds.
641#[derive(Debug, Clone, Serialize, Deserialize)]
642pub struct TokenInfo {
643 /// User identifier
644 pub user_id: String,
645
646 /// Username or email
647 pub username: Option<String>,
648
649 /// User's email address
650 pub email: Option<String>,
651
652 /// User's display name
653 pub name: Option<String>,
654
655 /// User's roles
656 pub roles: Vec<String>,
657
658 /// User's permissions
659 pub permissions: Vec<String>,
660
661 /// Additional user attributes
662 pub attributes: HashMap<String, serde_json::Value>,
663}
664
665/// Standard and custom JWT claims used by [`TokenManager`].
666///
667/// Fields follow the [JWT RFC 7519](https://tools.ietf.org/html/rfc7519)
668/// registered claim names. Additional claims can be stored in [`custom`](JwtClaims::custom).
669#[derive(Debug, Clone, Serialize, Deserialize)]
670pub struct JwtClaims {
671 /// Subject (user ID)
672 pub sub: String,
673
674 /// Issuer
675 pub iss: String,
676
677 /// Audience
678 pub aud: String,
679
680 /// Expiration time
681 pub exp: i64,
682
683 /// Issued at
684 pub iat: i64,
685
686 /// Not before
687 pub nbf: i64,
688
689 /// JWT ID
690 pub jti: String,
691
692 /// Scopes
693 pub scope: String,
694
695 /// User permissions
696 pub permissions: Option<Vec<String>>,
697
698 /// User roles
699 pub roles: Option<Vec<String>>,
700
701 /// Client ID
702 pub client_id: Option<String>,
703
704 /// Custom claims
705 #[serde(flatten)]
706 pub custom: HashMap<String, serde_json::Value>,
707}
708
709/// Central token lifecycle manager: creation, validation, refresh, and
710/// revocation.
711///
712/// Constructed internally by [`AuthFramework`](crate::auth::AuthFramework)
713/// — most users interact with token operations through the
714/// [`TokenOperations`](crate::auth::TokenOperations) facade instead.
715pub struct TokenManager {
716 /// JWT encoding key
717 encoding_key: EncodingKey,
718
719 /// JWT decoding key (current)
720 decoding_key: DecodingKey,
721
722 /// Optional previous decoding key retained during key-rotation grace period.
723 ///
724 /// Tokens signed with the previous key are still accepted until they expire
725 /// naturally. Once operators are satisfied all pre-rotation tokens have
726 /// expired, they can call [`TokenManager::retire_previous_key`] to remove it.
727 previous_decoding_key: Option<DecodingKey>,
728
729 /// Key material for recreating keys during clone
730 key_material: KeyMaterial,
731
732 /// Key material for the previous key (for clone support after rotation)
733 previous_key_material: Option<KeyMaterial>,
734
735 /// JWT algorithm
736 algorithm: Algorithm,
737
738 /// Token issuer
739 issuer: String,
740
741 /// Token audience
742 audience: String,
743
744 /// Default token lifetime
745 default_lifetime: Duration,
746}
747
748/// Key material for cloning TokenManager
749#[derive(Clone)]
750enum KeyMaterial {
751 /// HMAC secret
752 Hmac(Vec<u8>),
753 /// RSA private and public keys
754 Rsa { private: Vec<u8>, public: Vec<u8> },
755}
756
757/// Public key material that can be serialized into a JWKS document.
758///
759/// Contains the RSA key components needed for JWT verification by external
760/// clients. Typically exposed via a `/.well-known/jwks.json` endpoint.
761#[derive(Debug, Clone, Serialize, Deserialize)]
762pub struct JwksPublicKey {
763 /// The JWT signing algorithm (e.g., RS256).
764 pub algorithm: Algorithm,
765 /// Key ID uniquely identifying this key in the JWKS key set.
766 pub kid: String,
767 /// RSA modulus, base64url-encoded.
768 pub n: String,
769 /// RSA public exponent, base64url-encoded.
770 pub e: String,
771}
772
773impl AuthToken {
774 /// Create a new authentication token.
775 pub fn new(
776 user_id: impl Into<String>,
777 access_token: impl Into<String>,
778 expires_in: std::time::Duration,
779 auth_method: impl Into<String>,
780 ) -> Self {
781 let now = Utc::now();
782 let expires_in_chrono =
783 chrono::Duration::from_std(expires_in).unwrap_or(chrono::Duration::hours(1));
784
785 Self {
786 token_id: Uuid::new_v4().to_string(),
787 user_id: user_id.into(),
788 access_token: access_token.into(),
789 refresh_token: None,
790 token_type: Some("Bearer".to_string()),
791 subject: None,
792 issuer: None,
793 issued_at: now,
794 expires_at: now + expires_in_chrono,
795 scopes: crate::types::Scopes::empty(),
796 auth_method: auth_method.into(),
797 client_id: None,
798 user_profile: None,
799 permissions: crate::types::Permissions::empty(),
800 roles: crate::types::Roles::empty(),
801 metadata: TokenMetadata::default(),
802 }
803 }
804
805 /// Get the access token string.
806 ///
807 /// # Example
808 ///
809 /// ```rust
810 /// use auth_framework::tokens::AuthToken;
811 ///
812 /// let token = AuthToken::builder("t1", "u1", "my_token").build();
813 /// assert_eq!(token.access_token(), "my_token");
814 /// ```
815 pub fn access_token(&self) -> &str {
816 &self.access_token
817 }
818
819 /// Get the user ID.
820 ///
821 /// # Example
822 ///
823 /// ```rust
824 /// use auth_framework::tokens::AuthToken;
825 ///
826 /// let token = AuthToken::builder("t1", "user42", "access").build();
827 /// assert_eq!(token.user_id(), "user42");
828 /// ```
829 pub fn user_id(&self) -> &str {
830 &self.user_id
831 }
832
833 /// Get the expiration time.
834 ///
835 /// # Example
836 ///
837 /// ```rust
838 /// use auth_framework::tokens::AuthToken;
839 /// use chrono::Utc;
840 ///
841 /// let token = AuthToken::builder("t1", "u1", "access").build();
842 /// assert!(token.expires_at() > Utc::now());
843 /// ```
844 pub fn expires_at(&self) -> DateTime<Utc> {
845 self.expires_at
846 }
847
848 /// Get the token value.
849 ///
850 /// Alias for [`access_token()`](Self::access_token).
851 ///
852 /// # Example
853 ///
854 /// ```rust
855 /// use auth_framework::tokens::AuthToken;
856 ///
857 /// let token = AuthToken::builder("t1", "u1", "tok_value").build();
858 /// assert_eq!(token.token_value(), "tok_value");
859 /// ```
860 pub fn token_value(&self) -> &str {
861 &self.access_token
862 }
863
864 /// Get the token type.
865 ///
866 /// # Example
867 ///
868 /// ```rust
869 /// use auth_framework::tokens::AuthToken;
870 ///
871 /// let token = AuthToken::builder("t1", "u1", "access")
872 /// .token_type("Bearer")
873 /// .build();
874 /// assert_eq!(token.token_type(), Some("Bearer"));
875 /// ```
876 pub fn token_type(&self) -> Option<&str> {
877 self.token_type.as_deref()
878 }
879
880 /// Get the subject claim.
881 ///
882 /// # Example
883 ///
884 /// ```rust
885 /// use auth_framework::tokens::AuthToken;
886 ///
887 /// let token = AuthToken::builder("t1", "u1", "access")
888 /// .subject("sub-123")
889 /// .build();
890 /// assert_eq!(token.subject(), Some("sub-123"));
891 /// ```
892 pub fn subject(&self) -> Option<&str> {
893 self.subject.as_deref()
894 }
895
896 /// Get the issuer.
897 ///
898 /// # Example
899 ///
900 /// ```rust
901 /// use auth_framework::tokens::AuthToken;
902 ///
903 /// let token = AuthToken::builder("t1", "u1", "access")
904 /// .issuer("my-service")
905 /// .build();
906 /// assert_eq!(token.issuer(), Some("my-service"));
907 /// ```
908 pub fn issuer(&self) -> Option<&str> {
909 self.issuer.as_deref()
910 }
911
912 /// Check if the token has expired.
913 ///
914 /// # Example
915 ///
916 /// ```rust
917 /// use auth_framework::tokens::AuthToken;
918 ///
919 /// let token = AuthToken::builder("t1", "u1", "access").build();
920 /// assert!(!token.is_expired()); // 1-hour default
921 /// ```
922 pub fn is_expired(&self) -> bool {
923 Utc::now() > self.expires_at
924 }
925
926 /// Check if the token is expiring within the given duration.
927 ///
928 /// # Example
929 ///
930 /// ```rust
931 /// use auth_framework::tokens::AuthToken;
932 /// use std::time::Duration;
933 ///
934 /// let token = AuthToken::builder("t1", "u1", "access").build();
935 /// assert!(token.is_expiring(Duration::from_secs(7200))); // within 2 hours
936 /// ```
937 pub fn is_expiring(&self, within: Duration) -> bool {
938 Utc::now() + within > self.expires_at
939 }
940
941 /// Check if the token has been revoked.
942 ///
943 /// # Example
944 ///
945 /// ```rust
946 /// use auth_framework::tokens::AuthToken;
947 ///
948 /// let mut token = AuthToken::builder("t1", "u1", "access").build();
949 /// assert!(!token.is_revoked());
950 /// token.revoke(Some("user request".to_string()));
951 /// assert!(token.is_revoked());
952 /// ```
953 pub fn is_revoked(&self) -> bool {
954 self.metadata.revoked
955 }
956
957 /// Check if the token is valid (not expired and not revoked).
958 ///
959 /// # Example
960 ///
961 /// ```rust
962 /// use auth_framework::tokens::AuthToken;
963 ///
964 /// let token = AuthToken::builder("t1", "u1", "access").build();
965 /// assert!(token.is_valid());
966 /// ```
967 pub fn is_valid(&self) -> bool {
968 !self.is_expired() && !self.is_revoked()
969 }
970
971 /// Check whether this token carries a refresh token.
972 ///
973 /// # Example
974 ///
975 /// ```rust
976 /// use auth_framework::tokens::AuthToken;
977 ///
978 /// let token = AuthToken::builder("t1", "u1", "access")
979 /// .refresh_token("rt-abc")
980 /// .build();
981 /// assert!(token.has_refresh_token());
982 /// ```
983 pub fn has_refresh_token(&self) -> bool {
984 self.refresh_token.is_some()
985 }
986
987 /// Return the refresh token string, if present.
988 ///
989 /// # Example
990 ///
991 /// ```rust
992 /// use auth_framework::tokens::AuthToken;
993 ///
994 /// let token = AuthToken::builder("t1", "u1", "access")
995 /// .refresh_token("rt-xyz")
996 /// .build();
997 /// assert_eq!(token.get_refresh_token(), Some("rt-xyz"));
998 /// ```
999 pub fn get_refresh_token(&self) -> Option<&str> {
1000 self.refresh_token.as_deref()
1001 }
1002
1003 /// Revoke the token.
1004 ///
1005 /// # Example
1006 ///
1007 /// ```rust
1008 /// use auth_framework::tokens::AuthToken;
1009 ///
1010 /// let mut token = AuthToken::builder("t1", "u1", "access").build();
1011 /// token.revoke(Some("compromised".to_string()));
1012 /// assert!(token.is_revoked());
1013 /// ```
1014 pub fn revoke(&mut self, reason: Option<String>) {
1015 self.metadata.revoked = true;
1016 self.metadata.revoked_at = Some(Utc::now());
1017 self.metadata.revoked_reason = reason;
1018 }
1019
1020 /// Update the last used time and increment use count.
1021 ///
1022 /// # Example
1023 ///
1024 /// ```rust
1025 /// use auth_framework::tokens::AuthToken;
1026 ///
1027 /// let mut token = AuthToken::builder("t1", "u1", "access").build();
1028 /// assert_eq!(token.metadata.use_count, 0);
1029 /// token.mark_used();
1030 /// assert_eq!(token.metadata.use_count, 1);
1031 /// ```
1032 pub fn mark_used(&mut self) {
1033 self.metadata.last_used = Some(Utc::now());
1034 self.metadata.use_count += 1;
1035 }
1036
1037 /// Add a scope to the token.
1038 ///
1039 /// Duplicates are ignored.
1040 ///
1041 /// # Example
1042 ///
1043 /// ```rust
1044 /// use auth_framework::tokens::AuthToken;
1045 ///
1046 /// let mut token = AuthToken::builder("t1", "u1", "access").build();
1047 /// token.add_scope("read");
1048 /// assert!(token.has_scope("read"));
1049 /// ```
1050 pub fn add_scope(&mut self, scope: impl Into<String>) {
1051 let scope = scope.into();
1052 if !self.scopes.contains(&scope) {
1053 self.scopes.push(scope);
1054 }
1055 }
1056
1057 /// Check if the token has a specific scope.
1058 ///
1059 /// # Example
1060 ///
1061 /// ```rust
1062 /// use auth_framework::tokens::AuthToken;
1063 ///
1064 /// let mut token = AuthToken::builder("t1", "u1", "access").build();
1065 /// token.add_scope("write");
1066 /// assert!(token.has_scope("write"));
1067 /// assert!(!token.has_scope("admin"));
1068 /// ```
1069 pub fn has_scope(&self, scope: &str) -> bool {
1070 self.scopes.contains(&scope.to_string())
1071 }
1072
1073 /// Set the refresh token.
1074 ///
1075 /// # Example
1076 ///
1077 /// ```rust
1078 /// use auth_framework::tokens::AuthToken;
1079 ///
1080 /// let token = AuthToken::builder("t1", "u1", "access").build()
1081 /// .with_refresh_token("refresh_xyz");
1082 /// assert!(token.refresh_token.is_some());
1083 /// ```
1084 pub fn with_refresh_token(mut self, refresh_token: impl Into<String>) -> Self {
1085 self.refresh_token = Some(refresh_token.into());
1086 self
1087 }
1088
1089 /// Set the client ID.
1090 ///
1091 /// # Example
1092 ///
1093 /// ```rust
1094 /// use auth_framework::tokens::AuthToken;
1095 ///
1096 /// let token = AuthToken::builder("t1", "u1", "access").build()
1097 /// .with_client_id("app-client");
1098 /// assert_eq!(token.client_id.as_deref(), Some("app-client"));
1099 /// ```
1100 pub fn with_client_id(mut self, client_id: impl Into<String>) -> Self {
1101 self.client_id = Some(client_id.into());
1102 self
1103 }
1104
1105 /// Set the token scopes.
1106 ///
1107 /// # Example
1108 ///
1109 /// ```rust
1110 /// use auth_framework::tokens::AuthToken;
1111 /// use auth_framework::types::Scopes;
1112 ///
1113 /// let token = AuthToken::builder("t1", "u1", "access").build()
1114 /// .with_scopes(Scopes::new(vec!["read".into()]));
1115 /// assert!(token.has_scope("read"));
1116 /// ```
1117 pub fn with_scopes(mut self, scopes: impl Into<crate::types::Scopes>) -> Self {
1118 self.scopes = scopes.into();
1119 self
1120 }
1121
1122 /// Add metadata to the token.
1123 ///
1124 /// # Example
1125 ///
1126 /// ```rust
1127 /// use auth_framework::tokens::{AuthToken, TokenMetadata};
1128 ///
1129 /// let meta = TokenMetadata::builder().issued_ip("192.168.1.1").build();
1130 /// let token = AuthToken::builder("t1", "u1", "access").build()
1131 /// .with_metadata(meta);
1132 /// assert_eq!(token.metadata.issued_ip.as_deref(), Some("192.168.1.1"));
1133 /// ```
1134 pub fn with_metadata(mut self, metadata: TokenMetadata) -> Self {
1135 self.metadata = metadata;
1136 self
1137 }
1138
1139 /// Get time until expiration.
1140 ///
1141 /// Returns `Duration::ZERO` if the token has already expired.
1142 ///
1143 /// # Example
1144 ///
1145 /// ```rust
1146 /// use auth_framework::tokens::AuthToken;
1147 /// use std::time::Duration;
1148 ///
1149 /// let token = AuthToken::builder("t1", "u1", "access").build();
1150 /// assert!(token.time_until_expiry() > Duration::ZERO);
1151 /// ```
1152 pub fn time_until_expiry(&self) -> Duration {
1153 let now = Utc::now();
1154 if self.expires_at > now {
1155 (self.expires_at - now).to_std().unwrap_or(Duration::ZERO)
1156 } else {
1157 Duration::ZERO
1158 }
1159 }
1160
1161 /// Add a custom claim to the token metadata.
1162 ///
1163 /// # Example
1164 ///
1165 /// ```rust
1166 /// use auth_framework::tokens::AuthToken;
1167 ///
1168 /// let mut token = AuthToken::builder("t1", "u1", "access").build();
1169 /// token.add_custom_claim("tenant", serde_json::json!("acme"));
1170 /// assert_eq!(token.get_custom_claim("tenant").unwrap(), &serde_json::json!("acme"));
1171 /// ```
1172 pub fn add_custom_claim(&mut self, key: impl Into<String>, value: serde_json::Value) {
1173 self.metadata.custom.insert(key.into(), value);
1174 }
1175
1176 /// Get a custom claim from the token metadata.
1177 ///
1178 /// # Example
1179 ///
1180 /// ```rust
1181 /// use auth_framework::tokens::AuthToken;
1182 ///
1183 /// let token = AuthToken::builder("t1", "u1", "access").build();
1184 /// assert!(token.get_custom_claim("missing").is_none());
1185 /// ```
1186 pub fn get_custom_claim(&self, key: &str) -> Option<&serde_json::Value> {
1187 self.metadata.custom.get(key)
1188 }
1189
1190 /// Check if the token has a specific permission.
1191 ///
1192 /// # Example
1193 ///
1194 /// ```rust
1195 /// use auth_framework::tokens::AuthToken;
1196 ///
1197 /// let mut token = AuthToken::builder("t1", "u1", "access").build();
1198 /// token.add_permission("admin");
1199 /// assert!(token.has_permission("admin"));
1200 /// ```
1201 pub fn has_permission(&self, permission: &str) -> bool {
1202 self.permissions.contains(&permission.to_string())
1203 }
1204
1205 /// Add a permission to the token.
1206 ///
1207 /// Duplicates are ignored.
1208 ///
1209 /// # Example
1210 ///
1211 /// ```rust
1212 /// use auth_framework::tokens::AuthToken;
1213 ///
1214 /// let mut token = AuthToken::builder("t1", "u1", "access").build();
1215 /// token.add_permission("write");
1216 /// assert!(token.has_permission("write"));
1217 /// ```
1218 pub fn add_permission(&mut self, permission: impl Into<String>) {
1219 let permission = permission.into();
1220 if !self.permissions.contains(&permission) {
1221 self.permissions.push(permission);
1222 }
1223 }
1224
1225 /// Add a role to the token.
1226 ///
1227 /// Duplicates are ignored.
1228 ///
1229 /// # Example
1230 ///
1231 /// ```rust
1232 /// use auth_framework::tokens::AuthToken;
1233 ///
1234 /// let mut token = AuthToken::builder("t1", "u1", "access").build();
1235 /// token.add_role("editor");
1236 /// assert!(token.has_role("editor"));
1237 /// ```
1238 pub fn add_role(&mut self, role: impl Into<String>) {
1239 let role = role.into();
1240 if !self.roles.contains(&role) {
1241 self.roles.push(role);
1242 }
1243 }
1244
1245 /// Check if the token has a specific role.
1246 ///
1247 /// # Example
1248 ///
1249 /// ```rust
1250 /// use auth_framework::tokens::AuthToken;
1251 ///
1252 /// let mut token = AuthToken::builder("t1", "u1", "access").build();
1253 /// token.add_role("admin");
1254 /// assert!(token.has_role("admin"));
1255 /// assert!(!token.has_role("guest"));
1256 /// ```
1257 pub fn has_role(&self, role: &str) -> bool {
1258 self.roles.contains(&role.to_string())
1259 }
1260
1261 /// Set the permissions.
1262 ///
1263 /// # Example
1264 ///
1265 /// ```rust
1266 /// use auth_framework::tokens::AuthToken;
1267 /// use auth_framework::types::Permissions;
1268 ///
1269 /// let token = AuthToken::builder("t1", "u1", "access").build()
1270 /// .with_permissions(Permissions::new(vec!["read".into()]));
1271 /// assert!(token.has_permission("read"));
1272 /// ```
1273 pub fn with_permissions(mut self, permissions: impl Into<crate::types::Permissions>) -> Self {
1274 self.permissions = permissions.into();
1275 self
1276 }
1277
1278 /// Set the roles.
1279 ///
1280 /// # Example
1281 ///
1282 /// ```rust
1283 /// use auth_framework::tokens::AuthToken;
1284 /// use auth_framework::types::Roles;
1285 ///
1286 /// let token = AuthToken::builder("t1", "u1", "access").build()
1287 /// .with_roles(Roles::new(vec!["viewer".into()]));
1288 /// assert!(token.has_role("viewer"));
1289 /// ```
1290 pub fn with_roles(mut self, roles: impl Into<crate::types::Roles>) -> Self {
1291 self.roles = roles.into();
1292 self
1293 }
1294}
1295
1296impl Clone for TokenManager {
1297 fn clone(&self) -> Self {
1298 let (previous_decoding_key, previous_key_material) = match &self.previous_key_material {
1299 Some(KeyMaterial::Hmac(secret)) => (
1300 Some(DecodingKey::from_secret(secret)),
1301 Some(KeyMaterial::Hmac(secret.clone())),
1302 ),
1303 Some(KeyMaterial::Rsa { public, .. }) => (
1304 DecodingKey::from_rsa_pem(public).ok(),
1305 self.previous_key_material.clone(),
1306 ),
1307 None => (None, None),
1308 };
1309 match &self.key_material {
1310 KeyMaterial::Hmac(secret) => Self {
1311 encoding_key: EncodingKey::from_secret(secret),
1312 decoding_key: DecodingKey::from_secret(secret),
1313 previous_decoding_key,
1314 key_material: self.key_material.clone(),
1315 previous_key_material,
1316 algorithm: self.algorithm,
1317 issuer: self.issuer.clone(),
1318 audience: self.audience.clone(),
1319 default_lifetime: self.default_lifetime,
1320 },
1321 KeyMaterial::Rsa { private, public } => Self {
1322 // SAFETY: The PEM was already validated when the TokenManager was first
1323 // constructed; re-parsing it here during Clone cannot fail unless the
1324 // bytes were corrupted in memory, which would be a catastrophic bug.
1325 encoding_key: EncodingKey::from_rsa_pem(private).expect("RSA private key PEM re-parse failed during Clone — this indicates memory corruption"),
1326 decoding_key: DecodingKey::from_rsa_pem(public).expect("RSA public key PEM re-parse failed during Clone — this indicates memory corruption"),
1327 previous_decoding_key,
1328 key_material: self.key_material.clone(),
1329 previous_key_material,
1330 algorithm: self.algorithm,
1331 issuer: self.issuer.clone(),
1332 audience: self.audience.clone(),
1333 default_lifetime: self.default_lifetime,
1334 },
1335 }
1336 }
1337}
1338
1339impl TokenManager {
1340 fn jwks_from_public_pem(public_key: &[u8], algorithm: Algorithm) -> Result<JwksPublicKey> {
1341 let pem = std::str::from_utf8(public_key)
1342 .map_err(|e| AuthError::crypto(format!("Invalid RSA public key PEM encoding: {e}")))?;
1343
1344 let public_key = rsa::RsaPublicKey::from_public_key_pem(pem)
1345 .or_else(|_| rsa::RsaPublicKey::from_pkcs1_pem(pem))
1346 .map_err(|e| {
1347 AuthError::crypto(format!(
1348 "Failed to parse RSA public key for JWKS export: {e}"
1349 ))
1350 })?;
1351
1352 let modulus = public_key.n().to_bytes_be();
1353 let exponent = public_key.e().to_bytes_be();
1354 let kid_digest = Sha256::digest(&modulus);
1355
1356 let n = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&modulus);
1357 let e = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&exponent);
1358 let kid = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(kid_digest);
1359
1360 Ok(JwksPublicKey {
1361 algorithm,
1362 kid,
1363 n,
1364 e,
1365 })
1366 }
1367
1368 /// Export the current and previous RSA verification keys as JWKS-compatible material.
1369 pub fn export_public_jwks(&self) -> Result<Vec<JwksPublicKey>> {
1370 let mut keys = Vec::new();
1371
1372 if let KeyMaterial::Rsa { public, .. } = &self.key_material {
1373 keys.push(Self::jwks_from_public_pem(public, self.algorithm)?);
1374 }
1375
1376 if let Some(KeyMaterial::Rsa { public, .. }) = &self.previous_key_material {
1377 let previous = Self::jwks_from_public_pem(public, self.algorithm)?;
1378 if !keys.iter().any(|key| key.kid == previous.kid) {
1379 keys.push(previous);
1380 }
1381 }
1382
1383 Ok(keys)
1384 }
1385
1386 /// Create a new token manager with HMAC key.
1387 pub fn new_hmac(secret: &[u8], issuer: impl Into<String>, audience: impl Into<String>) -> Self {
1388 Self {
1389 encoding_key: EncodingKey::from_secret(secret),
1390 decoding_key: DecodingKey::from_secret(secret),
1391 previous_decoding_key: None,
1392 key_material: KeyMaterial::Hmac(secret.to_vec()),
1393 previous_key_material: None,
1394 algorithm: Algorithm::HS256,
1395 issuer: issuer.into(),
1396 audience: audience.into(),
1397 default_lifetime: Duration::from_secs(3600), // 1 hour
1398 }
1399 }
1400
1401 /// Create a new token manager with RSA keys.
1402 ///
1403 /// ## RSA Key Format Support
1404 ///
1405 /// This method supports RSA keys in both standard PEM formats:
1406 /// - **PKCS#1**: `-----BEGIN RSA PRIVATE KEY-----` (traditional RSA format)
1407 /// - **PKCS#8**: `-----BEGIN PRIVATE KEY-----` (modern standard format, recommended)
1408 ///
1409 /// Both formats are automatically detected and parsed. No format conversion is required.
1410 ///
1411 /// ## Example
1412 ///
1413 /// ```rust,no_run
1414 /// use auth_framework::tokens::TokenManager;
1415 ///
1416 /// // Both PKCS#1 and PKCS#8 formats work; provide PEM bytes from your key store.
1417 /// # let private_key: &[u8] = b"";
1418 /// # let public_key: &[u8] = b"";
1419 ///
1420 /// let manager = TokenManager::new_rsa(
1421 /// private_key,
1422 /// public_key,
1423 /// "my-service",
1424 /// "my-audience"
1425 /// )?;
1426 /// # Ok::<(), auth_framework::errors::AuthError>(())
1427 /// ```
1428 pub fn new_rsa(
1429 private_key: &[u8],
1430 public_key: &[u8],
1431 issuer: impl Into<String>,
1432 audience: impl Into<String>,
1433 ) -> Result<Self> {
1434 let encoding_key = EncodingKey::from_rsa_pem(private_key)
1435 .map_err(|e| AuthError::crypto(format!("Invalid RSA private key: {e}")))?;
1436
1437 let decoding_key = DecodingKey::from_rsa_pem(public_key)
1438 .map_err(|e| AuthError::crypto(format!("Invalid RSA public key: {e}")))?;
1439
1440 Ok(Self {
1441 encoding_key,
1442 decoding_key,
1443 previous_decoding_key: None,
1444 key_material: KeyMaterial::Rsa {
1445 private: private_key.to_vec(),
1446 public: public_key.to_vec(),
1447 },
1448 previous_key_material: None,
1449 algorithm: Algorithm::RS256,
1450 issuer: issuer.into(),
1451 audience: audience.into(),
1452 default_lifetime: Duration::from_secs(3600), // 1 hour
1453 })
1454 }
1455
1456 /// Rotate HMAC key, keeping the current key as the previous decoding key
1457 /// to seamlessly allow verification of tokens signed with the old key.
1458 pub fn rotate_hmac_key(&mut self, new_secret: &[u8]) {
1459 // Move current key to previous
1460 if let KeyMaterial::Hmac(secret) = &self.key_material {
1461 self.previous_decoding_key = Some(DecodingKey::from_secret(secret));
1462 self.previous_key_material = Some(KeyMaterial::Hmac(secret.clone()));
1463 }
1464
1465 // Set new key
1466 self.encoding_key = EncodingKey::from_secret(new_secret);
1467 self.decoding_key = DecodingKey::from_secret(new_secret);
1468 self.key_material = KeyMaterial::Hmac(new_secret.to_vec());
1469 self.algorithm = Algorithm::HS256;
1470 }
1471
1472 /// Rotate RSA key, keeping the current key as the previous decoding key
1473 /// to seamlessly allow verification of tokens signed with the old key.
1474 pub fn rotate_rsa_key(&mut self, private_key: &[u8], public_key: &[u8]) -> Result<()> {
1475 let new_encoding_key = EncodingKey::from_rsa_pem(private_key)
1476 .map_err(|e| AuthError::crypto(format!("Invalid RSA private key: {e}")))?;
1477 let new_decoding_key = DecodingKey::from_rsa_pem(public_key)
1478 .map_err(|e| AuthError::crypto(format!("Invalid RSA public key: {e}")))?;
1479
1480 // Move current key to previous
1481 if let KeyMaterial::Rsa { public, .. } = &self.key_material {
1482 self.previous_decoding_key = DecodingKey::from_rsa_pem(public).ok();
1483 self.previous_key_material = Some(self.key_material.clone());
1484 }
1485
1486 // Set new key
1487 self.encoding_key = new_encoding_key;
1488 self.decoding_key = new_decoding_key;
1489 self.key_material = KeyMaterial::Rsa {
1490 private: private_key.to_vec(),
1491 public: public_key.to_vec(),
1492 };
1493 self.algorithm = Algorithm::RS256;
1494
1495 Ok(())
1496 }
1497
1498 /// Retire the previous key (if any), so tokens signed with it are no longer valid.
1499 pub fn retire_previous_key(&mut self) {
1500 self.previous_decoding_key = None;
1501 self.previous_key_material = None;
1502 }
1503
1504 /// Set the default token lifetime.
1505 pub fn with_default_lifetime(mut self, lifetime: Duration) -> Self {
1506 self.default_lifetime = lifetime;
1507 self
1508 }
1509
1510 /// Create a new JWT token.
1511 pub fn create_jwt_token(
1512 &self,
1513 user_id: impl Into<String>,
1514 scopes: Vec<String>,
1515 lifetime: Option<Duration>,
1516 ) -> Result<String> {
1517 let user_id = user_id.into();
1518 let lifetime = lifetime.unwrap_or(self.default_lifetime);
1519 let now = Utc::now();
1520 let exp = now + chrono::Duration::from_std(lifetime).unwrap_or(chrono::Duration::hours(1));
1521
1522 let claims = JwtClaims {
1523 sub: user_id,
1524 iss: self.issuer.clone(),
1525 aud: self.audience.clone(),
1526 exp: exp.timestamp(),
1527 iat: now.timestamp(),
1528 nbf: now.timestamp(),
1529 jti: Uuid::new_v4().to_string(),
1530 scope: scopes.join(" "),
1531 permissions: None,
1532 roles: None,
1533 client_id: None,
1534 custom: HashMap::new(),
1535 };
1536
1537 let header = Header::new(self.algorithm);
1538
1539 encode(&header, &claims, &self.encoding_key)
1540 .map_err(|e| TokenError::creation_failed(format!("JWT encoding failed: {e}")).into())
1541 }
1542
1543 /// Validate and decode a JWT token.
1544 pub fn validate_jwt_token(&self, token: &str) -> Result<JwtClaims> {
1545 let mut validation = Validation::new(self.algorithm);
1546 validation.set_issuer(&[&self.issuer]);
1547 validation.set_audience(&[&self.audience]);
1548
1549 match decode::<JwtClaims>(token, &self.decoding_key, &validation) {
1550 Ok(token_data) => Ok(token_data.claims),
1551 Err(e) => {
1552 match e.kind() {
1553 jsonwebtoken::errors::ErrorKind::ExpiredSignature => {
1554 Err(AuthError::Token(TokenError::Expired))
1555 }
1556 jsonwebtoken::errors::ErrorKind::InvalidSignature => {
1557 // If signature is invalid, try the previous key if configured
1558 if let Some(prev_key) = &self.previous_decoding_key
1559 && let Ok(prev_token_data) =
1560 decode::<JwtClaims>(token, prev_key, &validation)
1561 {
1562 return Ok(prev_token_data.claims);
1563 }
1564
1565 Err(AuthError::Token(TokenError::Invalid {
1566 message: "Invalid token signature".to_string(),
1567 }))
1568 }
1569 _ => Err(AuthError::Token(TokenError::Invalid {
1570 message: "Invalid token format".to_string(),
1571 })),
1572 }
1573 }
1574 }
1575 }
1576
1577 /// Create a complete authentication token with JWT.
1578 pub fn create_auth_token(
1579 &self,
1580 user_id: impl Into<String>,
1581 scopes: impl Into<crate::types::Scopes>,
1582 auth_method: impl Into<String>,
1583 lifetime: Option<std::time::Duration>,
1584 ) -> Result<AuthToken> {
1585 let user_id_str = user_id.into();
1586 let scopes: crate::types::Scopes = scopes.into();
1587 let lifetime = lifetime.unwrap_or(self.default_lifetime);
1588
1589 let jwt_token = self.create_jwt_token(&user_id_str, scopes.to_vec(), Some(lifetime))?;
1590
1591 let token =
1592 AuthToken::new(user_id_str, jwt_token, lifetime, auth_method).with_scopes(scopes);
1593
1594 Ok(token)
1595 }
1596
1597 /// Validate an authentication token.
1598 pub fn validate_auth_token(&self, token: &AuthToken) -> Result<()> {
1599 // Check if token is expired
1600 if token.is_expired() {
1601 return Err(TokenError::Expired.into());
1602 }
1603
1604 // Check if token is revoked
1605 if token.is_revoked() {
1606 return Err(TokenError::Invalid {
1607 message: "Token has been revoked".to_string(),
1608 }
1609 .into());
1610 }
1611
1612 // Validate JWT if it's a JWT token
1613 if token.auth_method == "jwt" || token.access_token.contains('.') {
1614 self.validate_jwt_token(&token.access_token)?;
1615 }
1616
1617 Ok(())
1618 }
1619
1620 /// Refresh a token (create a new one with extended lifetime).
1621 pub fn refresh_token(&self, token: &AuthToken) -> Result<AuthToken> {
1622 if token.is_expired() {
1623 return Err(TokenError::Expired.into());
1624 }
1625
1626 if token.is_revoked() {
1627 return Err(TokenError::Invalid {
1628 message: "Cannot refresh revoked token".to_string(),
1629 }
1630 .into());
1631 }
1632
1633 // Create a new token with the same properties but new expiry
1634 self.create_auth_token(
1635 &token.user_id,
1636 token.scopes.clone(),
1637 &token.auth_method,
1638 Some(self.default_lifetime),
1639 )
1640 }
1641
1642 /// Extract token information from a JWT.
1643 pub fn extract_token_info(&self, token: &str) -> Result<TokenInfo> {
1644 let claims = self.validate_jwt_token(token)?;
1645
1646 Ok(TokenInfo {
1647 user_id: claims.sub,
1648 username: claims
1649 .custom
1650 .get("username")
1651 .and_then(|v| v.as_str())
1652 .map(|s| s.to_string()),
1653 email: claims
1654 .custom
1655 .get("email")
1656 .and_then(|v| v.as_str())
1657 .map(|s| s.to_string()),
1658 name: claims
1659 .custom
1660 .get("name")
1661 .and_then(|v| v.as_str())
1662 .map(|s| s.to_string()),
1663 roles: claims
1664 .custom
1665 .get("roles")
1666 .and_then(|v| v.as_array())
1667 .map(|arr| {
1668 arr.iter()
1669 .filter_map(|v| v.as_str())
1670 .map(|s| s.to_string())
1671 .collect()
1672 })
1673 .unwrap_or_default(),
1674 permissions: claims
1675 .scope
1676 .split_whitespace()
1677 .map(|s| s.to_string())
1678 .collect(),
1679 attributes: claims.custom,
1680 })
1681 }
1682}
1683
1684/// Trait for converting tokens to user profiles
1685#[async_trait::async_trait]
1686pub trait TokenToProfile {
1687 /// Convert this token to a user profile using the specified provider
1688 async fn to_profile(&self, provider: &OAuthProvider) -> Result<ProviderProfile>;
1689
1690 /// Convert this token to a user profile with a custom extractor
1691 async fn to_profile_with_extractor(
1692 &self,
1693 provider: &OAuthProvider,
1694 extractor: &ProfileExtractor,
1695 ) -> Result<ProviderProfile>;
1696}
1697
1698#[async_trait::async_trait]
1699impl TokenToProfile for AuthToken {
1700 async fn to_profile(&self, provider: &OAuthProvider) -> Result<ProviderProfile> {
1701 let extractor = ProfileExtractor::new();
1702 extractor.extract_profile(self, provider).await
1703 }
1704
1705 async fn to_profile_with_extractor(
1706 &self,
1707 provider: &OAuthProvider,
1708 extractor: &ProfileExtractor,
1709 ) -> Result<ProviderProfile> {
1710 extractor.extract_profile(self, provider).await
1711 }
1712}
1713
1714#[cfg(test)]
1715mod tests {
1716 use super::*;
1717
1718 #[test]
1719 fn test_auth_token_creation() {
1720 let token = AuthToken::new(
1721 "user123",
1722 "token123",
1723 Duration::from_secs(3600), // 1 hour
1724 "password",
1725 );
1726
1727 assert_eq!(token.user_id(), "user123");
1728 assert_eq!(token.access_token(), "token123");
1729 assert!(!token.is_expired());
1730 assert!(!token.is_revoked());
1731 assert!(token.is_valid());
1732 }
1733
1734 #[test]
1735 fn test_token_expiry() {
1736 let token = AuthToken::new("user123", "token123", Duration::from_millis(1), "password");
1737
1738 // Wait a bit to ensure expiry
1739 std::thread::sleep(std::time::Duration::from_millis(10));
1740
1741 assert!(token.is_expired());
1742 assert!(!token.is_valid());
1743 }
1744
1745 #[test]
1746 fn test_token_revocation() {
1747 let mut token = AuthToken::new(
1748 "user123",
1749 "token123",
1750 Duration::from_secs(3600), // 1 hour
1751 "password",
1752 );
1753
1754 assert!(!token.is_revoked());
1755
1756 token.revoke(Some("User logout".to_string()));
1757
1758 assert!(token.is_revoked());
1759 assert!(!token.is_valid());
1760 assert!(token.metadata.revoked);
1761 }
1762}