Skip to main content

oxide_framework_core/auth/
claims.rs

1//! JWT payload claims inserted into request extensions after successful auth.
2
3use serde::{Deserialize, Serialize};
4
5/// Standard claims decoded from a JWT (Bearer or session cookie).
6///
7/// Include `roles` as a JSON array of strings in the token payload, for example:
8/// `{"sub":"user-1","roles":["admin","user"],"exp":...}`.
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct AuthClaims {
11    /// Subject (typically user id).
12    pub sub: String,
13    /// Role names for [`crate::auth::RequireRole`] and [`AuthClaims::has_role`].
14    #[serde(default)]
15    pub roles: Vec<String>,
16    /// Expiration time (Unix seconds). Required for validation.
17    pub exp: u64,
18    /// Issuer — include when validating with [`crate::auth::AuthConfig::with_issuer`].
19    #[serde(default)]
20    pub iss: Option<String>,
21    /// Audience — include when validating with [`crate::auth::AuthConfig::with_audience`].
22    #[serde(default)]
23    pub aud: Option<String>,
24}
25
26impl AuthClaims {
27    /// Build claims with `exp` set to now + `ttl_secs`.
28    pub fn new(sub: impl Into<String>, roles: Vec<String>, ttl_secs: u64) -> Self {
29        let exp = jsonwebtoken::get_current_timestamp().saturating_add(ttl_secs);
30        Self {
31            sub: sub.into(),
32            roles,
33            exp,
34            iss: None,
35            aud: None,
36        }
37    }
38
39    /// Returns true if `role` is present in [`Self::roles`].
40    pub fn has_role(&self, role: &str) -> bool {
41        self.roles.iter().any(|r| r == role)
42    }
43
44    /// Returns true if the principal has **any** of the given roles.
45    pub fn has_any_role(&self, roles: &[&str]) -> bool {
46        roles.iter().any(|r| self.has_role(r))
47    }
48
49    /// Returns true if the principal has **all** of the given roles.
50    pub fn has_all_roles(&self, roles: &[&str]) -> bool {
51        roles.iter().all(|r| self.has_role(r))
52    }
53}
54