Validate a JWT token string and extract the authenticated identity. Performs: 1. Base64 decode header + payload + signature 2. HMAC-SHA256 signature verification (if configured) 3. Expiration check (`exp` claim) 4. Issuer/audience validation (if configured) 5. Map claims → `AuthenticatedIdentity`
(&self, token: &str)
| 120 | /// 4. Issuer/audience validation (if configured) |
| 121 | /// 5. Map claims → `AuthenticatedIdentity` |
| 122 | pub fn validate(&self, token: &str) -> Result<AuthenticatedIdentity, JwtError> { |
| 123 | let parts: Vec<&str> = token.split('.').collect(); |
| 124 | if parts.len() != 3 { |
| 125 | return Err(JwtError::MalformedToken); |
| 126 | } |
| 127 | |
| 128 | // Decode header to determine algorithm. |
| 129 | let header_bytes = base64_url_decode(parts[0]).ok_or(JwtError::DecodingError)?; |
| 130 | let header: JwtHeader = |
| 131 | sonic_rs::from_slice(&header_bytes).map_err(|_| JwtError::InvalidClaims)?; |
| 132 | |
| 133 | // Decode payload (middle part). We verify signature separately. |
| 134 | let payload_bytes = base64_url_decode(parts[1]).ok_or(JwtError::DecodingError)?; |
| 135 | let claims: JwtClaims = |
| 136 | sonic_rs::from_slice(&payload_bytes).map_err(|_| JwtError::InvalidClaims)?; |
| 137 | |
| 138 | // Verify signature based on algorithm declared in header. |
| 139 | let signing_input = format!("{}.{}", parts[0], parts[1]); |
| 140 | let signature_bytes = base64_url_decode(parts[2]).ok_or(JwtError::DecodingError)?; |
| 141 | |
| 142 | match header.alg.as_str() { |
| 143 | "HS256" => { |
| 144 | if self.config.hmac_secret.is_empty() { |
| 145 | return Err(JwtError::UnsupportedAlgorithm); |
| 146 | } |
| 147 | if !verify_hmac_sha256( |
| 148 | &self.config.hmac_secret, |
| 149 | signing_input.as_bytes(), |
| 150 | &signature_bytes, |
| 151 | ) { |
| 152 | return Err(JwtError::InvalidSignature); |
| 153 | } |
| 154 | } |
| 155 | "RS256" => { |
| 156 | if self.config.rsa_public_key_der.is_empty() { |
| 157 | return Err(JwtError::UnsupportedAlgorithm); |
| 158 | } |
| 159 | if !verify_rsa_sha256( |
| 160 | &self.config.rsa_public_key_der, |
| 161 | signing_input.as_bytes(), |
| 162 | &signature_bytes, |
| 163 | ) { |
| 164 | return Err(JwtError::InvalidSignature); |
| 165 | } |
| 166 | } |
| 167 | _ => return Err(JwtError::UnsupportedAlgorithm), |
| 168 | } |
| 169 | |
| 170 | // Check expiration. |
| 171 | let now = SystemTime::now() |
| 172 | .duration_since(UNIX_EPOCH) |
| 173 | .unwrap_or_default() |
| 174 | .as_secs(); |
| 175 | |
| 176 | if claims.exp > 0 && now > claims.exp + self.config.clock_skew_seconds { |
| 177 | return Err(JwtError::Expired); |
| 178 | } |
| 179 | if claims.nbf > 0 && now + self.config.clock_skew_seconds < claims.nbf { |