Skip to main content

modkit_auth/
standard_claims.rs

1//! Standard JWT claim names as defined in RFC 7519.
2//!
3//! This module provides type-safe constants for standard JWT claim names,
4//! reducing the risk of typos and providing a central place for claim name definitions.
5//!
6//! # References
7//! - [RFC 7519 - JSON Web Token (JWT)](https://datatracker.ietf.org/doc/html/rfc7519)
8//! - [IANA JWT Claims Registry](https://www.iana.org/assignments/jwt/jwt.xhtml)
9
10/// Standard JWT claim names as defined in RFC 7519 and OIDC specifications.
11///
12/// This struct provides constants for standard claim names used in JWT tokens.
13/// Using these constants instead of string literals helps prevent typos and
14/// provides a single source of truth for claim names.
15///
16/// # Example
17/// ```
18/// use modkit_auth::StandardClaim;
19/// use serde_json::json;
20///
21/// let claims = json!({
22///     "sub": "user-123",
23///     "iss": "https://auth.example.com"
24/// });
25///
26/// let subject = claims.get(StandardClaim::SUB);
27/// let issuer = claims.get(StandardClaim::ISS);
28/// ```
29pub struct StandardClaim;
30
31impl StandardClaim {
32    // =========================================================================
33    // RFC 7519 Registered Claims (Section 4.1)
34    // =========================================================================
35
36    /// Issuer claim - identifies the principal that issued the JWT.
37    ///
38    /// The "iss" (issuer) claim identifies the principal that issued the JWT.
39    /// The processing of this claim is generally application specific.
40    ///
41    /// See: <https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1>
42    pub const ISS: &'static str = "iss";
43
44    /// Subject claim - identifies the principal that is the subject of the JWT.
45    ///
46    /// The "sub" (subject) claim identifies the principal that is the subject
47    /// of the JWT. The claims in a JWT are normally statements about the subject.
48    ///
49    /// See: <https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2>
50    pub const SUB: &'static str = "sub";
51
52    /// Audience claim - identifies the recipients that the JWT is intended for.
53    ///
54    /// The "aud" (audience) claim identifies the recipients that the JWT is
55    /// intended for. Each principal intended to process the JWT MUST identify
56    /// itself with a value in the audience claim.
57    ///
58    /// See: <https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3>
59    pub const AUD: &'static str = "aud";
60
61    /// Expiration Time claim - identifies the expiration time of the JWT.
62    ///
63    /// The "exp" (expiration time) claim identifies the expiration time on
64    /// or after which the JWT MUST NOT be accepted for processing.
65    ///
66    /// See: <https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4>
67    pub const EXP: &'static str = "exp";
68
69    /// Not Before claim - identifies the time before which the JWT must not be accepted.
70    ///
71    /// The "nbf" (not before) claim identifies the time before which the JWT
72    /// MUST NOT be accepted for processing.
73    ///
74    /// See: <https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5>
75    pub const NBF: &'static str = "nbf";
76
77    /// Issued At claim - identifies the time at which the JWT was issued.
78    ///
79    /// The "iat" (issued at) claim identifies the time at which the JWT was issued.
80    /// This claim can be used to determine the age of the JWT.
81    ///
82    /// See: <https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6>
83    pub const IAT: &'static str = "iat";
84
85    /// JWT ID claim - provides a unique identifier for the JWT.
86    ///
87    /// The "jti" (JWT ID) claim provides a unique identifier for the JWT.
88    /// The identifier value MUST be assigned in a manner that ensures that
89    /// there is a negligible probability that the same value will be
90    /// accidentally assigned to a different data object.
91    ///
92    /// See: <https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.7>
93    pub const JTI: &'static str = "jti";
94
95    // =========================================================================
96    // OpenID Connect Claims
97    // =========================================================================
98
99    /// Authorized party claim - the party to which the ID Token was issued.
100    ///
101    /// The "azp" (authorized party) claim identifies the party to which the
102    /// ID Token was issued. If present, it MUST contain the OAuth 2.0 Client ID
103    /// of this party.
104    ///
105    /// See: <https://openid.net/specs/openid-connect-core-1_0.html#IDToken>
106    pub const AZP: &'static str = "azp";
107
108    /// Returns a slice containing all standard JWT claim names (RFC 7519).
109    ///
110    /// This is useful for filtering out standard claims when collecting
111    /// extra/custom claims from a token.
112    #[must_use]
113    pub const fn all_registered() -> &'static [&'static str] {
114        &[
115            Self::ISS,
116            Self::SUB,
117            Self::AUD,
118            Self::EXP,
119            Self::NBF,
120            Self::IAT,
121            Self::JTI,
122            Self::AZP,
123        ]
124    }
125
126    /// Checks if the given claim name is a standard registered claim.
127    #[must_use]
128    pub fn is_registered(name: &str) -> bool {
129        Self::all_registered().contains(&name)
130    }
131}
132
133#[cfg(test)]
134#[cfg_attr(coverage_nightly, coverage(off))]
135mod tests {
136    use super::*;
137
138    #[test]
139    fn test_claim_constants() {
140        assert_eq!(StandardClaim::ISS, "iss");
141        assert_eq!(StandardClaim::SUB, "sub");
142        assert_eq!(StandardClaim::AUD, "aud");
143        assert_eq!(StandardClaim::EXP, "exp");
144        assert_eq!(StandardClaim::NBF, "nbf");
145        assert_eq!(StandardClaim::IAT, "iat");
146        assert_eq!(StandardClaim::JTI, "jti");
147        assert_eq!(StandardClaim::AZP, "azp");
148    }
149
150    #[test]
151    fn test_all_registered() {
152        let all = StandardClaim::all_registered();
153        assert_eq!(all.len(), 8);
154        assert!(all.contains(&"iss"));
155        assert!(all.contains(&"sub"));
156        assert!(all.contains(&"aud"));
157        assert!(all.contains(&"exp"));
158        assert!(all.contains(&"nbf"));
159        assert!(all.contains(&"iat"));
160        assert!(all.contains(&"jti"));
161        assert!(all.contains(&"azp"));
162    }
163
164    #[test]
165    fn test_is_registered() {
166        assert!(StandardClaim::is_registered("iss"));
167        assert!(StandardClaim::is_registered("sub"));
168        assert!(StandardClaim::is_registered("azp"));
169        assert!(!StandardClaim::is_registered("custom_claim"));
170        assert!(!StandardClaim::is_registered("tenant_id"));
171        assert!(!StandardClaim::is_registered("roles"));
172    }
173}