supabase_jwt/claims.rs
1//! # JWT Claims
2//!
3//! This module provides the `Claims` struct, which represents the deserialized data
4//! from a Supabase Auth JWT. It is designed to be a simple data carrier with
5//! convenient methods for accessing standard JWT claims and Supabase-specific metadata.
6//!
7//! The actual JWT parsing and validation logic is handled by the `JwtParser`,
8//! and this module focuses on providing a strongly-typed structure for the claims
9//! once they have been successfully validated.
10
11use crate::{error::AuthError, jwks::JwksCache, parser::JwtParser};
12use serde::{Deserialize, Serialize};
13
14/// Represents the claims of a Supabase JWT.
15///
16/// This struct acts as a data carrier for all the claims contained within a JWT,
17/// making it easy to access user information and metadata. The validation logic
18/// is handled by the `JwtParser` before the claims are instantiated.
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct Claims {
21 /// (Subject) The user ID.
22 pub sub: String,
23 /// (Expiration Time) The timestamp when the token expires.
24 pub exp: usize,
25 /// (Issued At) The timestamp when the token was issued.
26 pub iat: Option<usize>,
27 /// (JWT ID) A unique identifier for the token.
28 pub jti: Option<String>,
29 /// The user's email address.
30 pub email: Option<String>,
31 /// The user's phone number.
32 pub phone: Option<String>,
33 /// The user's role.
34 pub role: Option<String>,
35 /// Application-specific metadata.
36 pub app_metadata: Option<serde_json::Value>,
37 /// User-specific metadata.
38 pub user_metadata: Option<serde_json::Value>,
39 /// (Audience) The recipient for which the JWT is intended.
40 pub aud: Option<String>,
41 /// (Issuer) The principal that issued the JWT.
42 pub iss: Option<String>,
43 /// (Authentication Assurance Level) The level of assurance.
44 pub aal: Option<String>,
45 /// (Authentication Methods References) A list of authentication methods.
46 pub amr: Option<Vec<serde_json::Value>>,
47 /// The session ID.
48 pub session_id: Option<String>,
49 /// Indicates if the user is anonymous.
50 pub is_anonymous: Option<bool>,
51 /// (Key ID) The ID of the key used to sign the token. Not serialized.
52 #[serde(skip)]
53 pub kid: Option<String>,
54}
55
56impl Claims {
57 /// Parses and validates claims from a raw JWT string.
58 ///
59 /// # Arguments
60 ///
61 /// * `token` - The raw JWT string.
62 /// * `jwks_cache` - A reference to the `JwksCache` for key retrieval.
63 ///
64 /// # Returns
65 ///
66 /// A `Result` containing the validated `Claims` or an `AuthError`.
67 pub async fn from_token(token: &str, jwks_cache: &JwksCache) -> Result<Self, AuthError> {
68 let jwt_header = JwtParser::decode_header(token)?;
69 let kid = jwt_header.kid.ok_or(AuthError::InvalidToken)?;
70
71 let jwk = jwks_cache.find_key(&kid).await?;
72 let decoding_key = JwtParser::create_decoding_key(&jwk)?;
73 let algorithm = JwtParser::parse_algorithm(&jwt_header.alg)?;
74
75 let mut claims = JwtParser::verify_and_decode(token, &decoding_key, algorithm)?;
76 claims.kid = Some(kid);
77
78 claims.validate_security()?;
79
80 Ok(claims)
81 }
82
83 /// Parses and validates claims from a "Bearer" token string.
84 ///
85 /// This method expects the token to be prefixed with "Bearer ".
86 ///
87 /// # Arguments
88 ///
89 /// * `bearer_token` - The Bearer token string (e.g., "Bearer eyJ...").
90 /// * `jwks_cache` - A reference to the `JwksCache` for key retrieval.
91 ///
92 /// # Returns
93 ///
94 /// A `Result` containing the validated `Claims` or an `AuthError`.
95 pub async fn from_bearer_token(
96 bearer_token: &str,
97 jwks_cache: &JwksCache,
98 ) -> Result<Self, AuthError> {
99 let token = bearer_token
100 .strip_prefix("Bearer ")
101 .ok_or(AuthError::MalformedToken)?;
102
103 Self::from_token(token, jwks_cache).await
104 }
105
106 /// Performs basic security validation on the claims.
107 ///
108 /// This validation is minimal, trusting that Supabase Auth has already performed
109 /// comprehensive checks. It primarily ensures that the subject (user ID) is not empty.
110 pub fn validate_security(&self) -> Result<(), AuthError> {
111 if self.sub.trim().is_empty() {
112 return Err(AuthError::InvalidClaims);
113 }
114 Ok(())
115 }
116}
117
118// Data access methods
119impl Claims {
120 /// Returns the user ID (subject).
121 pub fn user_id(&self) -> &str {
122 &self.sub
123 }
124
125 /// Returns the user's email, if available.
126 pub fn email(&self) -> Option<&str> {
127 self.email.as_deref()
128 }
129
130 /// Returns the user's role, defaulting to "authenticated".
131 pub fn role(&self) -> &str {
132 self.role.as_deref().unwrap_or("authenticated")
133 }
134
135 /// Returns the user's phone number, if available.
136 pub fn phone(&self) -> Option<&str> {
137 self.phone.as_deref()
138 }
139
140 /// Checks if the user is anonymous.
141 pub fn is_anonymous(&self) -> bool {
142 self.is_anonymous.unwrap_or(false)
143 }
144}
145
146// Metadata access methods
147impl Claims {
148 /// Retrieves a specific field from the user metadata.
149 ///
150 /// # Arguments
151 ///
152 /// * `key` - The key of the metadata field to retrieve.
153 ///
154 /// # Returns
155 ///
156 /// An `Option` containing the deserialized value if the key exists.
157 pub fn get_user_metadata<T>(&self, key: &str) -> Option<T>
158 where
159 T: serde::de::DeserializeOwned,
160 {
161 self.user_metadata
162 .as_ref()
163 .and_then(|metadata| metadata.get(key))
164 .and_then(|value| serde_json::from_value(value.clone()).ok())
165 }
166
167 /// Retrieves a specific field from the application metadata.
168 ///
169 /// # Arguments
170 ///
171 /// * `key` - The key of the metadata field to retrieve.
172 ///
173 /// # Returns
174 ///
175 /// An `Option` containing the deserialized value if the key exists.
176 pub fn get_app_metadata<T>(&self, key: &str) -> Option<T>
177 where
178 T: serde::de::DeserializeOwned,
179 {
180 self.app_metadata
181 .as_ref()
182 .and_then(|metadata| metadata.get(key))
183 .and_then(|value| serde_json::from_value(value.clone()).ok())
184 }
185}