Fix JWT 'aud' array (#4488)

Co-authored-by: Drew Ridley <drewr@Drews-MacBook-Pro.local>
Co-authored-by: Gerard Guillemas Martos <gguillemas@users.noreply.github.com>
This commit is contained in:
Drew Ridley 2024-08-12 04:50:27 -04:00 committed by GitHub
parent c0be139e59
commit d2c59f0b71
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 22 additions and 12 deletions

View file

@ -8,6 +8,13 @@ use std::collections::HashMap;
pub static HEADER: Lazy<Header> = Lazy::new(|| Header::new(Algorithm::HS512)); pub static HEADER: Lazy<Header> = Lazy::new(|| Header::new(Algorithm::HS512));
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(untagged)]
pub enum Audience {
Single(String),
Multiple(Vec<String>),
}
#[derive(Debug, Default, Serialize, Deserialize, Clone)] #[derive(Debug, Default, Serialize, Deserialize, Clone)]
#[non_exhaustive] #[non_exhaustive]
pub struct Claims { pub struct Claims {
@ -22,7 +29,7 @@ pub struct Claims {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub sub: Option<String>, pub sub: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub aud: Option<String>, pub aud: Option<Audience>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub jti: Option<String>, pub jti: Option<String>,
#[serde(alias = "ns")] #[serde(alias = "ns")]
@ -80,7 +87,10 @@ impl From<Claims> for Value {
} }
// Add aud field if set // Add aud field if set
if let Some(aud) = v.aud { if let Some(aud) = v.aud {
out.insert("aud".to_string(), aud.into()); match aud {
Audience::Single(v) => out.insert("aud".to_string(), v.into()),
Audience::Multiple(v) => out.insert("aud".to_string(), v.into()),
};
} }
// Add iat field if set // Add iat field if set
if let Some(iat) = v.iat { if let Some(iat) = v.iat {

View file

@ -691,7 +691,7 @@ async fn authenticate_jwt(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::iam::token::HEADER; use crate::iam::token::{Audience, HEADER};
use argon2::password_hash::{PasswordHasher, SaltString}; use argon2::password_hash::{PasswordHasher, SaltString};
use chrono::Duration; use chrono::Duration;
use jsonwebtoken::{encode, EncodingKey}; use jsonwebtoken::{encode, EncodingKey};
@ -2246,7 +2246,7 @@ mod tests {
let key = EncodingKey::from_secret(secret.as_ref()); let key = EncodingKey::from_secret(secret.as_ref());
let claims = Claims { let claims = Claims {
iss: Some("surrealdb-test".to_string()), iss: Some("surrealdb-test".to_string()),
aud: Some("surrealdb-test".to_string()), aud: Some(Audience::Single("surrealdb-test".to_string())),
iat: Some(Utc::now().timestamp()), iat: Some(Utc::now().timestamp()),
nbf: Some(Utc::now().timestamp()), nbf: Some(Utc::now().timestamp()),
exp: Some((Utc::now() + Duration::hours(1)).timestamp()), exp: Some((Utc::now() + Duration::hours(1)).timestamp()),
@ -2315,7 +2315,7 @@ mod tests {
let key = EncodingKey::from_secret(secret.as_ref()); let key = EncodingKey::from_secret(secret.as_ref());
let claims = Claims { let claims = Claims {
iss: Some("surrealdb-test".to_string()), iss: Some("surrealdb-test".to_string()),
aud: Some("invalid".to_string()), aud: Some(Audience::Single("invalid".to_string())),
iat: Some(Utc::now().timestamp()), iat: Some(Utc::now().timestamp()),
nbf: Some(Utc::now().timestamp()), nbf: Some(Utc::now().timestamp()),
exp: Some((Utc::now() + Duration::hours(1)).timestamp()), exp: Some((Utc::now() + Duration::hours(1)).timestamp()),
@ -2372,7 +2372,7 @@ mod tests {
let key = EncodingKey::from_secret(secret.as_ref()); let key = EncodingKey::from_secret(secret.as_ref());
let claims = Claims { let claims = Claims {
iss: Some("surrealdb-test".to_string()), iss: Some("surrealdb-test".to_string()),
aud: Some("invalid".to_string()), aud: Some(Audience::Single("invalid".to_string())),
iat: Some(Utc::now().timestamp()), iat: Some(Utc::now().timestamp()),
nbf: Some(Utc::now().timestamp()), nbf: Some(Utc::now().timestamp()),
exp: Some((Utc::now() + Duration::hours(1)).timestamp()), exp: Some((Utc::now() + Duration::hours(1)).timestamp()),
@ -2431,7 +2431,7 @@ mod tests {
let key = EncodingKey::from_secret(secret.as_ref()); let key = EncodingKey::from_secret(secret.as_ref());
let claims = Claims { let claims = Claims {
iss: Some("surrealdb-test".to_string()), iss: Some("surrealdb-test".to_string()),
aud: Some("surrealdb-test".to_string()), aud: Some(Audience::Single("surrealdb-test".to_string())),
iat: Some(Utc::now().timestamp()), iat: Some(Utc::now().timestamp()),
nbf: Some(Utc::now().timestamp()), nbf: Some(Utc::now().timestamp()),
exp: Some((Utc::now() + Duration::hours(1)).timestamp()), exp: Some((Utc::now() + Duration::hours(1)).timestamp()),
@ -2498,7 +2498,7 @@ mod tests {
let key = EncodingKey::from_secret(secret.as_ref()); let key = EncodingKey::from_secret(secret.as_ref());
let claims = Claims { let claims = Claims {
iss: Some("surrealdb-test".to_string()), iss: Some("surrealdb-test".to_string()),
aud: Some("invalid".to_string()), aud: Some(Audience::Single("invalid".to_string())),
iat: Some(Utc::now().timestamp()), iat: Some(Utc::now().timestamp()),
nbf: Some(Utc::now().timestamp()), nbf: Some(Utc::now().timestamp()),
exp: Some((Utc::now() + Duration::hours(1)).timestamp()), exp: Some((Utc::now() + Duration::hours(1)).timestamp()),
@ -2554,7 +2554,7 @@ mod tests {
let key = EncodingKey::from_secret(secret.as_ref()); let key = EncodingKey::from_secret(secret.as_ref());
let claims = Claims { let claims = Claims {
iss: Some("surrealdb-test".to_string()), iss: Some("surrealdb-test".to_string()),
aud: Some("invalid".to_string()), aud: Some(Audience::Single("invalid".to_string())),
iat: Some(Utc::now().timestamp()), iat: Some(Utc::now().timestamp()),
nbf: Some(Utc::now().timestamp()), nbf: Some(Utc::now().timestamp()),
exp: Some((Utc::now() + Duration::hours(1)).timestamp()), exp: Some((Utc::now() + Duration::hours(1)).timestamp()),
@ -2612,7 +2612,7 @@ mod tests {
let key = EncodingKey::from_secret(secret.as_ref()); let key = EncodingKey::from_secret(secret.as_ref());
let claims = Claims { let claims = Claims {
iss: Some("surrealdb-test".to_string()), iss: Some("surrealdb-test".to_string()),
aud: Some("surrealdb-test".to_string()), aud: Some(Audience::Single("surrealdb-test".to_string())),
iat: Some(Utc::now().timestamp()), iat: Some(Utc::now().timestamp()),
nbf: Some(Utc::now().timestamp()), nbf: Some(Utc::now().timestamp()),
exp: Some((Utc::now() + Duration::hours(1)).timestamp()), exp: Some((Utc::now() + Duration::hours(1)).timestamp()),
@ -2677,7 +2677,7 @@ mod tests {
let key = EncodingKey::from_secret(secret.as_ref()); let key = EncodingKey::from_secret(secret.as_ref());
let claims = Claims { let claims = Claims {
iss: Some("surrealdb-test".to_string()), iss: Some("surrealdb-test".to_string()),
aud: Some("invalid".to_string()), aud: Some(Audience::Single("invalid".to_string())),
iat: Some(Utc::now().timestamp()), iat: Some(Utc::now().timestamp()),
nbf: Some(Utc::now().timestamp()), nbf: Some(Utc::now().timestamp()),
exp: Some((Utc::now() + Duration::hours(1)).timestamp()), exp: Some((Utc::now() + Duration::hours(1)).timestamp()),
@ -2732,7 +2732,7 @@ mod tests {
let key = EncodingKey::from_secret(secret.as_ref()); let key = EncodingKey::from_secret(secret.as_ref());
let claims = Claims { let claims = Claims {
iss: Some("surrealdb-test".to_string()), iss: Some("surrealdb-test".to_string()),
aud: Some("invalid".to_string()), aud: Some(Audience::Single("invalid".to_string())),
iat: Some(Utc::now().timestamp()), iat: Some(Utc::now().timestamp()),
nbf: Some(Utc::now().timestamp()), nbf: Some(Utc::now().timestamp()),
exp: Some((Utc::now() + Duration::hours(1)).timestamp()), exp: Some((Utc::now() + Duration::hours(1)).timestamp()),