use std::collections::HashSet;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::ops::Deref;
use std::rc::Rc;
use chrono::{DateTime, Utc};
use oauth2::helpers::variant_name;
use oauth2::{ClientId, ClientSecret};
use ring::constant_time::verify_slices_are_equal;
use serde::de::DeserializeOwned;
use serde::Serialize;
use crate::jwt::{JsonWebToken, JsonWebTokenJsonPayloadSerde};
use crate::user_info::UserInfoClaimsImpl;
use crate::{
AdditionalClaims, Audience, AuthenticationContextClass, GenderClaim, IdTokenClaims, IssuerUrl,
JsonWebKey, JsonWebKeySet, JsonWebKeyType, JsonWebKeyUse, JsonWebTokenAccess,
JsonWebTokenAlgorithm, JsonWebTokenHeader, JweContentEncryptionAlgorithm, JwsSigningAlgorithm,
Nonce, SubjectIdentifier,
};
pub(crate) trait AudiencesClaim {
fn audiences(&self) -> Option<&Vec<Audience>>;
}
pub(crate) trait IssuerClaim {
fn issuer(&self) -> Option<&IssuerUrl>;
}
#[derive(Clone, Debug, Fail, PartialEq)]
pub enum ClaimsVerificationError {
#[fail(display = "Expired: {}", _0)]
Expired(String),
#[fail(display = "Invalid audiences: {}", _0)]
InvalidAudience(String),
#[fail(display = "Invalid authorization context class reference: {}", _0)]
InvalidAuthContext(String),
#[fail(display = "Invalid authentication time: {}", _0)]
InvalidAuthTime(String),
#[fail(display = "Invalid issuer: {}", _0)]
InvalidIssuer(String),
#[fail(display = "Invalid nonce: {}", _0)]
InvalidNonce(String),
#[fail(display = "Invalid subject: {}", _0)]
InvalidSubject(String),
#[fail(display = "Claims must be signed")]
NoSignature,
#[fail(display = "{}", _0)]
Other(String),
#[fail(display = "Signature verification failed")]
SignatureVerification(#[cause] SignatureVerificationError),
#[fail(display = "Unsupported: {}", _0)]
Unsupported(String),
}
#[derive(Clone, Debug, Fail, PartialEq)]
pub enum SignatureVerificationError {
#[fail(display = "Ambiguous key identification: {}", _0)]
AmbiguousKeyId(String),
#[fail(display = "Crypto error: {}", _0)]
CryptoError(String),
#[fail(display = "Disallowed signature algorithm: {}", _0)]
DisallowedAlg(String),
#[fail(display = "Invalid cryptographic key: {}", _0)]
InvalidKey(String),
#[fail(display = "No matching key found")]
NoMatchingKey,
#[fail(display = "Unsupported signature algorithm: {}", _0)]
UnsupportedAlg(String),
#[fail(display = "Other error: {}", _0)]
Other(String),
}
#[derive(Clone)]
struct JwtClaimsVerifier<'a, JS, JT, JU, K>
where
JS: JwsSigningAlgorithm<JT>,
JT: JsonWebKeyType,
JU: JsonWebKeyUse,
K: JsonWebKey<JS, JT, JU>,
{
allowed_algs: Option<HashSet<JS>>,
aud_match_required: bool,
client_id: ClientId,
client_secret: Option<ClientSecret>,
iss_required: bool,
issuer: IssuerUrl,
is_signature_check_enabled: bool,
other_aud_verifier_fn: Rc<dyn Fn(&Audience) -> bool + 'a>,
signature_keys: JsonWebKeySet<JS, JT, JU, K>,
}
impl<'a, JS, JT, JU, K> JwtClaimsVerifier<'a, JS, JT, JU, K>
where
JS: JwsSigningAlgorithm<JT>,
JT: JsonWebKeyType,
JU: JsonWebKeyUse,
K: JsonWebKey<JS, JT, JU>,
{
pub fn new(
client_id: ClientId,
issuer: IssuerUrl,
signature_keys: JsonWebKeySet<JS, JT, JU, K>,
) -> Self {
JwtClaimsVerifier {
allowed_algs: Some([JS::rsa_sha_256()].iter().cloned().collect()),
aud_match_required: true,
client_id,
client_secret: None,
iss_required: true,
issuer,
is_signature_check_enabled: true,
other_aud_verifier_fn: Rc::new(|_| false),
signature_keys,
}
}
pub fn require_audience_match(mut self, aud_required: bool) -> Self {
self.aud_match_required = aud_required;
self
}
pub fn require_issuer_match(mut self, iss_required: bool) -> Self {
self.iss_required = iss_required;
self
}
pub fn require_signature_check(mut self, sig_required: bool) -> Self {
self.is_signature_check_enabled = sig_required;
self
}
pub fn set_allowed_algs<I>(mut self, algs: I) -> Self
where
I: IntoIterator<Item = JS>,
{
self.allowed_algs = Some(algs.into_iter().collect());
self
}
pub fn allow_any_alg(mut self) -> Self {
self.allowed_algs = None;
self
}
pub fn set_client_secret(mut self, client_secret: ClientSecret) -> Self {
self.client_secret = Some(client_secret);
self
}
pub fn set_other_audience_verifier_fn<T>(mut self, other_aud_verifier_fn: T) -> Self
where
T: Fn(&Audience) -> bool + 'a,
{
self.other_aud_verifier_fn = Rc::new(other_aud_verifier_fn);
self
}
fn validate_jose_header<JE>(
jose_header: &JsonWebTokenHeader<JE, JS, JT>,
) -> Result<(), ClaimsVerificationError>
where
JE: JweContentEncryptionAlgorithm<JT>,
{
if let Some(ref jwt_type) = jose_header.typ {
if jwt_type.to_uppercase() != "JWT" {
return Err(ClaimsVerificationError::Unsupported(format!(
"unexpected or unsupported JWT type `{}`",
**jwt_type
)));
}
}
if let Some(ref content_type) = jose_header.cty {
if content_type.to_uppercase() == "JWT" {
return Err(ClaimsVerificationError::Unsupported(
"nested JWT's are not currently supported".to_string(),
));
} else {
return Err(ClaimsVerificationError::Unsupported(format!(
"unexpected or unsupported JWT content type `{}`",
**content_type
)));
}
}
if jose_header.crit.is_some() {
return Err(ClaimsVerificationError::Unsupported(
"critical JWT header fields are unsupported".to_string(),
));
}
Ok(())
}
pub fn verified_claims<A, C, JE, T>(&self, jwt: A) -> Result<T, ClaimsVerificationError>
where
A: JsonWebTokenAccess<JE, JS, JT, C, ReturnType = T>,
C: AudiencesClaim + Debug + DeserializeOwned + IssuerClaim + Serialize,
JE: JweContentEncryptionAlgorithm<JT>,
T: AudiencesClaim + IssuerClaim,
{
{
let jose_header = jwt.unverified_header();
Self::validate_jose_header(jose_header)?;
if let JsonWebTokenAlgorithm::Encryption(ref encryption_alg) = jose_header.alg {
return Err(ClaimsVerificationError::Unsupported(format!(
"JWE encryption is not currently supported (found algorithm `{}`)",
variant_name(encryption_alg),
)));
}
}
{
let unverified_claims = jwt.unverified_payload_ref();
if self.iss_required {
if let Some(issuer) = unverified_claims.issuer() {
if *issuer != self.issuer {
return Err(ClaimsVerificationError::InvalidIssuer(format!(
"expected `{}` (found `{}`)",
*self.issuer, **issuer
)));
}
} else {
return Err(ClaimsVerificationError::InvalidIssuer(
"missing issuer claim".to_string(),
));
}
}
if self.aud_match_required {
if let Some(audiences) = unverified_claims.audiences() {
if !audiences
.iter()
.any(|aud| (**aud).deref() == self.client_id.deref())
{
return Err(ClaimsVerificationError::InvalidAudience(format!(
"must contain `{}` (found audiences: {})",
*self.client_id,
audiences
.iter()
.map(|aud| format!("`{}`", Deref::deref(aud)))
.collect::<Vec<_>>()
.join(", ")
)));
} else if audiences.len() > 1 {
audiences
.iter()
.filter(|aud| (**aud).deref() != self.client_id.deref())
.find(|aud| !(self.other_aud_verifier_fn)(aud))
.map(|aud| {
Err(ClaimsVerificationError::InvalidAudience(format!(
"`{}` is not a trusted audience",
**aud,
)))
})
.unwrap_or(Ok(()))?;
}
} else {
return Err(ClaimsVerificationError::InvalidAudience(
"missing audiences claim".to_string(),
));
}
}
}
if !self.is_signature_check_enabled {
return Ok(jwt.unverified_payload());
}
let signature_alg = match jwt.unverified_header().alg {
JsonWebTokenAlgorithm::Encryption(_) => unreachable!(),
JsonWebTokenAlgorithm::Signature(ref signature_alg, _) => signature_alg,
JsonWebTokenAlgorithm::None => return Err(ClaimsVerificationError::NoSignature),
}
.clone();
if let Some(ref allowed_algs) = self.allowed_algs {
if !allowed_algs.contains(&signature_alg) {
return Err(ClaimsVerificationError::SignatureVerification(
SignatureVerificationError::DisallowedAlg(format!(
"algorithm `{}` is not one of: {}",
variant_name(&signature_alg),
allowed_algs
.iter()
.map(variant_name)
.collect::<Vec<_>>()
.join(", "),
)),
));
}
}
if signature_alg.uses_shared_secret() {
if let Some(ref client_secret) = self.client_secret {
let key = K::new_symmetric(client_secret.secret().clone().into_bytes());
return jwt
.payload(&signature_alg.clone(), &key)
.map_err(ClaimsVerificationError::SignatureVerification);
} else {
return Err(ClaimsVerificationError::SignatureVerification(
SignatureVerificationError::DisallowedAlg(
"symmetric signatures are disallowed for public clients".to_string(),
),
));
}
}
let public_keys = {
let jose_header = jwt.unverified_header();
self.signature_keys
.keys()
.iter()
.filter(|key|
Some(key.key_type()) == signature_alg.key_type().as_ref() &&
(key.key_use().is_none() ||
key.key_use().iter().any(
|key_use| key_use.allows_signature()
)) &&
(jose_header.kid.is_none() ||
jose_header.kid.as_ref() == key.key_id()))
.collect::<Vec<&K>>()
};
if public_keys.is_empty() {
return Err(ClaimsVerificationError::SignatureVerification(
SignatureVerificationError::NoMatchingKey,
));
} else if public_keys.len() != 1 {
return Err(ClaimsVerificationError::SignatureVerification(
SignatureVerificationError::AmbiguousKeyId(format!(
"JWK set must only contain one eligible public key \
({} eligible keys: {})",
public_keys.len(),
public_keys
.iter()
.map(|key| format!(
"{} ({})",
key.key_id()
.map(|kid| format!("`{}`", **kid))
.unwrap_or_else(|| "null ID".to_string()),
variant_name(key.key_type())
))
.collect::<Vec<_>>()
.join(", ")
)),
));
}
jwt.payload(
&signature_alg.clone(),
*public_keys.first().expect("unreachable"),
)
.map_err(ClaimsVerificationError::SignatureVerification)
}
}
pub trait NonceVerifier {
fn verify(self, nonce: Option<&Nonce>) -> Result<(), String>;
}
impl NonceVerifier for &Nonce {
fn verify(self, nonce: Option<&Nonce>) -> Result<(), String> {
if let Some(claims_nonce) = nonce {
if verify_slices_are_equal(claims_nonce.secret().as_bytes(), self.secret().as_bytes())
.is_err()
{
return Err("nonce mismatch".to_string());
}
} else {
return Err("missing nonce claim".to_string());
}
Ok(())
}
}
impl<F> NonceVerifier for F
where
F: FnOnce(Option<&Nonce>) -> Result<(), String>,
{
fn verify(self, nonce: Option<&Nonce>) -> Result<(), String> {
self(nonce)
}
}
#[derive(Clone)]
pub struct IdTokenVerifier<'a, JS, JT, JU, K>
where
JS: JwsSigningAlgorithm<JT>,
JT: JsonWebKeyType,
JU: JsonWebKeyUse,
K: JsonWebKey<JS, JT, JU>,
{
acr_verifier_fn: Rc<dyn Fn(Option<&AuthenticationContextClass>) -> Result<(), String> + 'a>,
#[allow(clippy::type_complexity)]
auth_time_verifier_fn: Rc<dyn Fn(Option<DateTime<Utc>>) -> Result<(), String> + 'a>,
iat_verifier_fn: Rc<dyn Fn(DateTime<Utc>) -> Result<(), String> + 'a>,
jwt_verifier: JwtClaimsVerifier<'a, JS, JT, JU, K>,
time_fn: Rc<dyn Fn() -> DateTime<Utc> + 'a>,
}
impl<'a, JS, JT, JU, K> IdTokenVerifier<'a, JS, JT, JU, K>
where
JS: JwsSigningAlgorithm<JT>,
JT: JsonWebKeyType,
JU: JsonWebKeyUse,
K: JsonWebKey<JS, JT, JU>,
{
fn new(jwt_verifier: JwtClaimsVerifier<'a, JS, JT, JU, K>) -> Self {
IdTokenVerifier {
acr_verifier_fn: Rc::new(|_| Ok(())),
auth_time_verifier_fn: Rc::new(|_| Ok(())),
iat_verifier_fn: Rc::new(|_| Ok(())),
jwt_verifier,
time_fn: Rc::new(Utc::now),
}
}
pub fn new_public_client(
client_id: ClientId,
issuer: IssuerUrl,
signature_keys: JsonWebKeySet<JS, JT, JU, K>,
) -> Self {
Self::new(JwtClaimsVerifier::new(client_id, issuer, signature_keys))
}
pub fn new_confidential_client(
client_id: ClientId,
client_secret: ClientSecret,
issuer: IssuerUrl,
signature_keys: JsonWebKeySet<JS, JT, JU, K>,
) -> Self {
Self::new(
JwtClaimsVerifier::new(client_id, issuer, signature_keys)
.set_client_secret(client_secret),
)
}
pub fn set_allowed_algs<I>(mut self, algs: I) -> Self
where
I: IntoIterator<Item = JS>,
{
self.jwt_verifier = self.jwt_verifier.set_allowed_algs(algs);
self
}
pub fn allow_any_alg(mut self) -> Self {
self.jwt_verifier = self.jwt_verifier.allow_any_alg();
self
}
pub fn set_auth_context_verifier_fn<T>(mut self, acr_verifier_fn: T) -> Self
where
T: Fn(Option<&AuthenticationContextClass>) -> Result<(), String> + 'a,
{
self.acr_verifier_fn = Rc::new(acr_verifier_fn);
self
}
pub fn set_auth_time_verifier_fn<T>(mut self, auth_time_verifier_fn: T) -> Self
where
T: Fn(Option<DateTime<Utc>>) -> Result<(), String> + 'a,
{
self.auth_time_verifier_fn = Rc::new(auth_time_verifier_fn);
self
}
pub fn enable_signature_check(mut self) -> Self {
self.jwt_verifier = self.jwt_verifier.require_signature_check(true);
self
}
pub fn insecure_disable_signature_check(mut self) -> Self {
self.jwt_verifier = self.jwt_verifier.require_signature_check(false);
self
}
pub fn require_issuer_match(mut self, iss_required: bool) -> Self {
self.jwt_verifier = self.jwt_verifier.require_issuer_match(iss_required);
self
}
pub fn require_audience_match(mut self, aud_required: bool) -> Self {
self.jwt_verifier = self.jwt_verifier.require_audience_match(aud_required);
self
}
pub fn set_time_fn<T>(mut self, time_fn: T) -> Self
where
T: Fn() -> DateTime<Utc> + 'a,
{
self.time_fn = Rc::new(time_fn);
self
}
pub fn set_issue_time_verifier_fn<T>(mut self, iat_verifier_fn: T) -> Self
where
T: Fn(DateTime<Utc>) -> Result<(), String> + 'a,
{
self.iat_verifier_fn = Rc::new(iat_verifier_fn);
self
}
pub fn set_other_audience_verifier_fn<T>(mut self, other_aud_verifier_fn: T) -> Self
where
T: Fn(&Audience) -> bool + 'a,
{
self.jwt_verifier = self
.jwt_verifier
.set_other_audience_verifier_fn(other_aud_verifier_fn);
self
}
pub(super) fn verified_claims<'b, AC, GC, JE, N>(
&self,
jwt: &'b JsonWebToken<JE, JS, JT, IdTokenClaims<AC, GC>, JsonWebTokenJsonPayloadSerde>,
nonce_verifier: N,
) -> Result<&'b IdTokenClaims<AC, GC>, ClaimsVerificationError>
where
AC: AdditionalClaims,
GC: GenderClaim,
JE: JweContentEncryptionAlgorithm<JT>,
N: NonceVerifier,
{
let partially_verified_claims = self.jwt_verifier.verified_claims(jwt)?;
self.verify_claims(partially_verified_claims, nonce_verifier)?;
Ok(partially_verified_claims)
}
pub(super) fn verified_claims_owned<AC, GC, JE, N>(
&self,
jwt: JsonWebToken<JE, JS, JT, IdTokenClaims<AC, GC>, JsonWebTokenJsonPayloadSerde>,
nonce_verifier: N,
) -> Result<IdTokenClaims<AC, GC>, ClaimsVerificationError>
where
AC: AdditionalClaims,
GC: GenderClaim,
JE: JweContentEncryptionAlgorithm<JT>,
N: NonceVerifier,
{
let partially_verified_claims = self.jwt_verifier.verified_claims(jwt)?;
self.verify_claims(&partially_verified_claims, nonce_verifier)?;
Ok(partially_verified_claims)
}
fn verify_claims<'b, AC, GC, N>(
&self,
partially_verified_claims: &'b IdTokenClaims<AC, GC>,
nonce_verifier: N,
) -> Result<(), ClaimsVerificationError>
where
AC: AdditionalClaims,
GC: GenderClaim,
N: NonceVerifier,
{
let cur_time = (*self.time_fn)();
if cur_time >= partially_verified_claims.expiration() {
return Err(ClaimsVerificationError::Expired(format!(
"ID token expired at {} (current time is {})",
partially_verified_claims.expiration(),
cur_time
)));
}
(*self.iat_verifier_fn)(partially_verified_claims.issue_time())
.map_err(ClaimsVerificationError::Expired)?;
nonce_verifier
.verify(partially_verified_claims.nonce())
.map_err(ClaimsVerificationError::InvalidNonce)?;
(*self.acr_verifier_fn)(partially_verified_claims.auth_context_ref())
.map_err(ClaimsVerificationError::InvalidAuthContext)?;
(*self.auth_time_verifier_fn)(partially_verified_claims.auth_time())
.map_err(ClaimsVerificationError::InvalidAuthTime)?;
Ok(())
}
}
#[derive(Clone)]
pub struct UserInfoVerifier<'a, JE, JS, JT, JU, K>
where
JE: JweContentEncryptionAlgorithm<JT>,
JS: JwsSigningAlgorithm<JT>,
JT: JsonWebKeyType,
JU: JsonWebKeyUse,
K: JsonWebKey<JS, JT, JU>,
{
jwt_verifier: JwtClaimsVerifier<'a, JS, JT, JU, K>,
expected_subject: Option<SubjectIdentifier>,
_phantom: PhantomData<JE>,
}
impl<'a, JE, JS, JT, JU, K> UserInfoVerifier<'a, JE, JS, JT, JU, K>
where
JE: JweContentEncryptionAlgorithm<JT>,
JS: JwsSigningAlgorithm<JT>,
JT: JsonWebKeyType,
JU: JsonWebKeyUse,
K: JsonWebKey<JS, JT, JU>,
{
pub fn new(
client_id: ClientId,
issuer: IssuerUrl,
signature_keys: JsonWebKeySet<JS, JT, JU, K>,
expected_subject: Option<SubjectIdentifier>,
) -> Self {
UserInfoVerifier {
jwt_verifier: JwtClaimsVerifier::new(client_id, issuer, signature_keys),
expected_subject,
_phantom: PhantomData,
}
}
pub(crate) fn expected_subject(&self) -> Option<&SubjectIdentifier> {
self.expected_subject.as_ref()
}
pub fn require_issuer_match(mut self, iss_required: bool) -> Self {
self.jwt_verifier = self.jwt_verifier.require_issuer_match(iss_required);
self
}
pub fn require_audience_match(mut self, aud_required: bool) -> Self {
self.jwt_verifier = self.jwt_verifier.require_audience_match(aud_required);
self
}
pub(crate) fn verified_claims<AC, GC>(
&self,
user_info_jwt: JsonWebToken<
JE,
JS,
JT,
UserInfoClaimsImpl<AC, GC>,
JsonWebTokenJsonPayloadSerde,
>,
) -> Result<UserInfoClaimsImpl<AC, GC>, ClaimsVerificationError>
where
AC: AdditionalClaims,
GC: GenderClaim,
{
let user_info = self.jwt_verifier.verified_claims(user_info_jwt)?;
if self
.expected_subject
.iter()
.all(|expected_subject| user_info.standard_claims.sub == *expected_subject)
{
Ok(user_info)
} else {
Err(ClaimsVerificationError::InvalidSubject(format!(
"expected `{}` (found `{}`)",
self.expected_subject.as_ref().unwrap().as_str(),
user_info.standard_claims.sub.as_str()
)))
}
}
}
#[cfg(test)]
mod tests {
use std::cell::Cell;
use chrono::{TimeZone, Utc};
use oauth2::{ClientId, ClientSecret};
use super::{
AudiencesClaim, ClaimsVerificationError, IssuerClaim, JsonWebTokenHeader,
JwtClaimsVerifier, SignatureVerificationError, SubjectIdentifier,
};
use crate::core::{
CoreIdToken, CoreIdTokenClaims, CoreIdTokenVerifier, CoreJsonWebKey, CoreJsonWebKeySet,
CoreJsonWebKeyType, CoreJsonWebKeyUse, CoreJweContentEncryptionAlgorithm,
CoreJwsSigningAlgorithm, CoreRsaPrivateSigningKey, CoreUserInfoClaims,
CoreUserInfoJsonWebToken, CoreUserInfoVerifier,
};
use crate::jwt::tests::{TEST_RSA_PRIV_KEY, TEST_RSA_PUB_KEY};
use crate::jwt::{JsonWebToken, JsonWebTokenJsonPayloadSerde};
use crate::types::helpers::seconds_to_utc;
use crate::types::Base64UrlEncodedBytes;
use crate::types::Seconds;
use crate::{
AccessToken, Audience, AuthenticationContextClass, AuthorizationCode, EndUserName,
IssuerUrl, JsonWebKeyId, Nonce, StandardClaims, UserInfoError,
};
type CoreJsonWebTokenHeader = JsonWebTokenHeader<
CoreJweContentEncryptionAlgorithm,
CoreJwsSigningAlgorithm,
CoreJsonWebKeyType,
>;
type CoreJwtClaimsVerifier<'a> = JwtClaimsVerifier<
'a,
CoreJwsSigningAlgorithm,
CoreJsonWebKeyType,
CoreJsonWebKeyUse,
CoreJsonWebKey,
>;
fn assert_unsupported<T>(result: Result<T, ClaimsVerificationError>, expected_substr: &str) {
match result {
Err(ClaimsVerificationError::Unsupported(msg)) => {
assert!(msg.contains(expected_substr))
}
Err(err) => panic!("unexpected error: {:?}", err),
Ok(_) => panic!("validation should fail"),
}
}
#[test]
fn test_jose_header() {
assert_unsupported(
CoreJwtClaimsVerifier::validate_jose_header(
&serde_json::from_str::<CoreJsonWebTokenHeader>(
"{\"alg\":\"RS256\",\"typ\":\"NOT_A_JWT\"}",
)
.expect("failed to deserialize"),
),
"unsupported JWT type",
);
assert_unsupported(
CoreJwtClaimsVerifier::validate_jose_header(
&serde_json::from_str::<CoreJsonWebTokenHeader>(
"{\"alg\":\"RS256\",\"cty\":\"JWT\"}",
)
.expect("failed to deserialize"),
),
"nested JWT",
);
assert_unsupported(
CoreJwtClaimsVerifier::validate_jose_header(
&serde_json::from_str::<CoreJsonWebTokenHeader>(
"{\"alg\":\"RS256\",\"cty\":\"NOT_A_JWT\"}",
)
.expect("failed to deserialize"),
),
"unsupported JWT content type",
);
assert_unsupported(
CoreJwtClaimsVerifier::validate_jose_header(
&serde_json::from_str::<CoreJsonWebTokenHeader>(
"{\
\"alg\":\"RS256\",\
\"crit\":[\"http://example.invalid/UNDEFINED\"],\
\"http://example.invalid/UNDEFINED\":true\
}",
)
.expect("failed to deserialize"),
),
"critical JWT header fields are unsupported",
);
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
struct TestClaims {
aud: Option<Vec<Audience>>,
iss: Option<IssuerUrl>,
payload: String,
}
impl AudiencesClaim for TestClaims {
fn audiences(&self) -> Option<&Vec<Audience>> {
self.aud.as_ref()
}
}
impl IssuerClaim for TestClaims {
fn issuer(&self) -> Option<&IssuerUrl> {
self.iss.as_ref()
}
}
type TestClaimsJsonWebToken = JsonWebToken<
CoreJweContentEncryptionAlgorithm,
CoreJwsSigningAlgorithm,
CoreJsonWebKeyType,
TestClaims,
JsonWebTokenJsonPayloadSerde,
>;
#[test]
fn test_jwt_verified_claims() {
let rsa_key = serde_json::from_str::<CoreJsonWebKey>(TEST_RSA_PUB_KEY)
.expect("deserialization failed");
let client_id = ClientId::new("my_client".to_string());
let issuer = IssuerUrl::new("https://example.com".to_string()).unwrap();
let verifier = CoreJwtClaimsVerifier::new(
client_id.clone(),
issuer.clone(),
CoreJsonWebKeySet::new(vec![rsa_key.clone()]),
);
assert_unsupported(
verifier.verified_claims(
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJBMjU2R0NNIiwiY3R5IjoiSldUIn0.eyJhdWQiOlsibXlfY2xpZW50Il0sImlzcyI6Im\
h0dHBzOi8vZXhhbXBsZS5jb20iLCJwYXlsb2FkIjoiaGVsbG8gd29ybGQifQ.YmFkX2hhc2g"
.to_string(),
)).expect("failed to deserialize"),
),
"nested JWT",
);
assert_unsupported(
verifier.verified_claims(
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJBMjU2R0NNIn0.eyJhdWQiOlsibXlfY2xpZW50Il0sImlzcyI6Imh0dHBzOi8vZXhhbX\
BsZS5jb20iLCJwYXlsb2FkIjoiaGVsbG8gd29ybGQifQ.YmFkX2hhc2g"
.to_string(),
)).expect("failed to deserialize"),
),
"JWE encryption",
);
match verifier.verified_claims(
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsibXlfY2xpZW50Il0sImlzcyI6Imh0dHBzOi8vYXR0YWNrZXIuY\
29tIiwicGF5bG9hZCI6ImhlbGxvIHdvcmxkIn0.YmFkX2hhc2g"
.to_string(),
)).expect("failed to deserialize"),
) {
Err(ClaimsVerificationError::InvalidIssuer(_)) => {},
other => panic!("unexpected result: {:?}", other),
}
match verifier.verified_claims(
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsibXlfY2xpZW50Il0sInBheWxvYWQiOiJoZWxsbyB3b3JsZCJ9.\
YmFkX2hhc2g"
.to_string(),
)).expect("failed to deserialize"),
) {
Err(ClaimsVerificationError::InvalidIssuer(_)) => {},
other => panic!("unexpected result: {:?}", other),
}
verifier
.clone()
.require_issuer_match(false)
.verified_claims(
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsibXlfY2xpZW50Il0sInBheWxvYWQiOiJoZWxsbyB3b3JsZCJ9.\
nv09al63NNDfb8cF3IozegXKbPaUC08zknRPKmQ5qKgXv80hjVxknkpRz7BxocB3JYTBjhYd0gyN9wAuJj\
byZ1QaUC14HOB83awAGbehy5yFLkLadTfPT7-siBCvE2V7AF73a_21YvwdkKmJ-RaKWHzFnG8CDmioma3X\
cWyrsdRLgvUkrWllajLRo8DCIXQ8OuZo1_o4n17PSlPxSkhKIrgaWCvG6tan40Y_1DZOFv47bx4hQUGd-J\
h2aEjiwn65WV3M_Xb2vQMP7VgYNVaNlfxzpL4yDASItbPMWaXBt3ZUa_IOGoSx2GMnPkrQ4xp56qUth6U7\
esWPqRSqqolnHg"
.to_string(),
)).expect("failed to deserialize"),
).expect("verification should succeed");
match verifier.verified_claims(
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsib3RoZXJfY2xpZW50Il0sImlzcyI6Imh0dHBzOi8vZXhhbXBsZ\
S5jb20iLCJwYXlsb2FkIjoiaGVsbG8gd29ybGQifQ.YmFkX2hhc2g"
.to_string(),
)).expect("failed to deserialize"),
) {
Err(ClaimsVerificationError::InvalidAudience(_)) => {},
other => panic!("unexpected result: {:?}", other),
}
match verifier.verified_claims(
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIiwicGF5bG9hZCI6ImhlbGxvI\
HdvcmxkIn0.YmFkX2hhc2g"
.to_string(),
)).expect("failed to deserialize"),
) {
Err(ClaimsVerificationError::InvalidAudience(_)) => {},
other => panic!("unexpected result: {:?}", other),
}
verifier
.clone()
.require_audience_match(false)
.verified_claims(
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIiwicGF5bG9hZCI6Imhlb\
GxvIHdvcmxkIn0.lP-Z_zGPNoKIbLQsnrZc2LAc5qJrKyb7t07ZtJUKVhcwHiCUou4bBhq5RHlElCh\
0ElRRP6I25lp6UszkRvIC46UV3GVze0x73kVkHSvCVI7MO75LbL9BRqrm5b4CN2zCiFBY8-EwTXnJd\
Ri0d_U8K29TV24L2I-Z5ZILebwUue1N59AGDjx2yYLFx5NOw3TUsPyscG62aZAT321pL_jcYwTWTWw\
2FYm07zguwx-PUTZwGXlJiOgXQqRIbY_1bS3I_D8UWsmEB3DmV0f9z-iklgIPFawa4wHaE-hpzBAEx\
pSieyOavA5pl0Se3XRYA-CkdDVgzG0Pt4IdnxFanfUXTw"
.to_string(),
)).expect("failed to deserialize"),
).expect("verification should succeed");
match verifier.verified_claims(
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsiYXVkMSIsIm15X2NsaWVudCIsImF1ZDIiXSwiaXNzIjoia\
HR0cHM6Ly9leGFtcGxlLmNvbSIsInBheWxvYWQiOiJoZWxsbyB3b3JsZCJ9.N9ibisEe0kKLe1GDWM\
ON3PmYqbL73dag-loM8pjKJNinF9SB7n4JuSu4FrNkeW4F1Cz8MIbLuWfKvDa_4v_3FstMA3GODZWH\
BVIiuNFay2ovCfGFyykwe47dF_47g_OM5AkJc_teE5MN8lPh9V5zYCy3ON3zZ3acFPJMOPTdbU56xD\
eFe7lil6DmV4JU9A52t5ZkJILFaIuxxXJUIDmqpPTvHkggh_QOj9C2US9bgg5b543JwT4j-HbDp51L\
dDB4k3azOssT1ddtoAuuDOctnraMKUtqffJXexxfwA1uM6EIofSrK5v11xwgTciL9xDXAvav_G2buP\
ol1bjGLa2t0Q"
.to_string(),
))
.expect("failed to deserialize"),
) {
Err(ClaimsVerificationError::InvalidAudience(_)) => {}
other => panic!("unexpected result: {:?}", other),
}
verifier
.clone()
.set_other_audience_verifier_fn(|aud| **aud == "aud1" || **aud == "aud2")
.verified_claims(
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsiYXVkMSIsIm15X2NsaWVudCIsImF1ZDIiXSwiaXNzIjoia\
HR0cHM6Ly9leGFtcGxlLmNvbSIsInBheWxvYWQiOiJoZWxsbyB3b3JsZCJ9.N9ibisEe0kKLe1GDWM\
ON3PmYqbL73dag-loM8pjKJNinF9SB7n4JuSu4FrNkeW4F1Cz8MIbLuWfKvDa_4v_3FstMA3GODZWH\
BVIiuNFay2ovCfGFyykwe47dF_47g_OM5AkJc_teE5MN8lPh9V5zYCy3ON3zZ3acFPJMOPTdbU56xD\
eFe7lil6DmV4JU9A52t5ZkJILFaIuxxXJUIDmqpPTvHkggh_QOj9C2US9bgg5b543JwT4j-HbDp51L\
dDB4k3azOssT1ddtoAuuDOctnraMKUtqffJXexxfwA1uM6EIofSrK5v11xwgTciL9xDXAvav_G2buP\
ol1bjGLa2t0Q"
.to_string(),
)).expect("failed to deserialize"),
).expect("verification should succeed");
match verifier.verified_claims(
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsiYXVkMSIsImF1ZDIiXSwiaXNzIjoiaHR0cHM6Ly9leGFtcGxlL\
mNvbSIsInBheWxvYWQiOiJoZWxsbyB3b3JsZCJ9.YmFkX2hhc2g"
.to_string(),
)).expect("failed to deserialize"),
) {
Err(ClaimsVerificationError::InvalidAudience(_)) => {},
other => panic!("unexpected result: {:?}", other),
}
verifier
.clone()
.require_signature_check(false)
.verified_claims(
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsibXlfY2xpZW50Il0sImlzcyI6Imh0dHBzOi8vZXhhbXBsZ\
S5jb20iLCJwYXlsb2FkIjoiaGVsbG8gd29ybGQifQ.YmFkX2hhc2g"
.to_string(),
)).expect("failed to deserialize"),
).expect("verification should succeed");
match verifier.verified_claims(
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJub25lIn0.eyJhdWQiOlsibXlfY2xpZW50Il0sImlzcyI6Imh0dHBzOi8vZXhhbXBsZ\
S5jb20iLCJwYXlsb2FkIjoiaGVsbG8gd29ybGQifQ."
.to_string(),
))
.expect("failed to deserialize"),
) {
Err(ClaimsVerificationError::NoSignature) => {}
other => panic!("unexpected result: {:?}", other),
}
let valid_rs256_jwt =
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsibXlfY2xpZW50Il0sImlzcyI6Imh0dHBzOi8vZXhhbXBsZ\
S5jb20iLCJwYXlsb2FkIjoiaGVsbG8gd29ybGQifQ.UZ7vmAsDmOBzeB6e2_0POUfyhMRZKM6WSKz3\
jB2QdmO-eZ9605EzhkJufJQ8515ryWnHv-gUHtZHQi3zilrzhBwvE2cVP83Gv2XIL1EKaMMmfISeEB\
ShWez_FvqxN_bamh5yTROhWmoZTmof-MweBCHgINcsEd7K4e_BHHgq3aaRBpvSFlL_z4l_1NwNcTBo\
kqjNScKZITk42AbsSuGR39L94BWLhz6WXQZ_Sn6R1Ro6roOm1b7E82jJiQEtlseQiCCvPR2JJ6LgW6\
XTMzQ0vCqSh1A7U_IBDsjY_yag8_X3xxFh2URCtHJ47ZSjqfv6hq7OAq8tmVecOVgfIvABOg"
.to_string(),
))
.expect("failed to deserialize");
verifier
.verified_claims(valid_rs256_jwt.clone())
.expect("verification should succeed");
let verifier_with_client_secret = CoreJwtClaimsVerifier::new(
client_id.clone(),
issuer.clone(),
CoreJsonWebKeySet::new(vec![]),
)
.set_client_secret(ClientSecret::new("my_secret".to_string()));
let valid_hs256_jwt =
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsibXlfY2xpZW50Il0sImlzcyI6Imh0dHBzOi8vZXhhbXBsZ\
S5jb20iLCJwYXlsb2FkIjoiaGVsbG8gd29ybGQifQ.dTXvSWen74_rC4oiWw0ziLZNe4KZk8Jw2VZe\
N6vLCDo"
.to_string(),
))
.expect("failed to deserialize");
match verifier_with_client_secret.verified_claims(valid_hs256_jwt.clone()) {
Err(ClaimsVerificationError::SignatureVerification(
SignatureVerificationError::DisallowedAlg(_),
)) => {}
other => panic!("unexpected result: {:?}", other),
}
verifier
.clone()
.allow_any_alg()
.verified_claims(valid_rs256_jwt.clone())
.expect("verification should succeed");
verifier_with_client_secret
.clone()
.allow_any_alg()
.verified_claims(valid_hs256_jwt.clone())
.expect("verification should succeed");
match verifier.clone().allow_any_alg().verified_claims(
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJub25lIn0.eyJhdWQiOlsibXlfY2xpZW50Il0sImlzcyI6Imh0dHBzOi8vZXhhbXBsZ\
S5jb20iLCJwYXlsb2FkIjoiaGVsbG8gd29ybGQifQ."
.to_string(),
))
.expect("failed to deserialize"),
) {
Err(ClaimsVerificationError::NoSignature) => {}
other => panic!("unexpected result: {:?}", other),
}
match verifier
.clone()
.allow_any_alg()
.verified_claims(valid_hs256_jwt.clone())
{
Err(ClaimsVerificationError::SignatureVerification(
SignatureVerificationError::DisallowedAlg(_),
)) => {}
other => panic!("unexpected result: {:?}", other),
}
verifier_with_client_secret
.clone()
.set_allowed_algs(vec![CoreJwsSigningAlgorithm::HmacSha256])
.verified_claims(valid_hs256_jwt.clone())
.expect("verification should succeed");
match verifier_with_client_secret
.clone()
.set_allowed_algs(vec![CoreJwsSigningAlgorithm::HmacSha256])
.verified_claims(
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsibXlfY2xpZW50Il0sImlzcyI6Imh0dHBzOi8vZXhhbXBsZ\
S5jb20iLCJwYXlsb2FkIjoiaGVsbG8gd29ybGQifQ.dTXvSWen74_rC4oiWw0ziLZNe4KZk8Jw2VZe\
N6vLCEo"
.to_string(),
)).expect("failed to deserialize")
)
{
Err(ClaimsVerificationError::SignatureVerification(
SignatureVerificationError::CryptoError(_),
)) => {}
other => panic!("unexpected result: {:?}", other),
}
match CoreJwtClaimsVerifier::new(
client_id.clone(),
issuer.clone(),
CoreJsonWebKeySet::new(vec![]),
)
.verified_claims(valid_rs256_jwt.clone())
{
Err(ClaimsVerificationError::SignatureVerification(
SignatureVerificationError::NoMatchingKey,
)) => {}
other => panic!("unexpected result: {:?}", other),
}
let kid = JsonWebKeyId::new("bilbo.baggins@hobbiton.example".to_string());
let n = Base64UrlEncodedBytes::new(vec![
159, 129, 15, 180, 3, 130, 115, 208, 37, 145, 228, 7, 63, 49, 210, 182, 0, 27, 130,
206, 219, 77, 146, 240, 80, 22, 93, 71, 207, 202, 184, 163, 196, 28, 183, 120, 172,
117, 83, 121, 63, 142, 249, 117, 118, 141, 26, 35, 116, 216, 113, 37, 100, 195, 188,
215, 123, 158, 164, 52, 84, 72, 153, 64, 124, 255, 0, 153, 146, 10, 147, 26, 36, 196,
65, 72, 82, 171, 41, 189, 176, 169, 92, 6, 83, 243, 108, 96, 230, 11, 249, 11, 98, 88,
221, 165, 111, 55, 4, 123, 165, 194, 209, 208, 41, 175, 156, 157, 64, 186, 199, 170,
65, 199, 138, 13, 209, 6, 138, 221, 105, 158, 128, 143, 234, 1, 30, 161, 68, 29, 138,
79, 123, 180, 233, 123, 227, 159, 85, 241, 221, 212, 78, 156, 75, 163, 53, 21, 151, 3,
212, 211, 75, 96, 62, 101, 20, 122, 79, 35, 214, 211, 192, 153, 108, 117, 237, 238,
132, 106, 130, 209, 144, 174, 16, 120, 60, 150, 28, 240, 56, 122, 237, 33, 6, 210, 208,
85, 91, 111, 217, 55, 250, 213, 83, 83, 135, 224, 255, 114, 255, 190, 120, 148, 20, 2,
176, 184, 34, 234, 42, 116, 182, 5, 140, 29, 171, 249, 179, 74, 118, 203, 99, 184, 127,
170, 44, 104, 71, 184, 226, 131, 127, 255, 145, 24, 110, 107, 28, 20, 145, 28, 249,
137, 168, 144, 146, 168, 28, 230, 1, 221, 172, 211, 249, 207,
]);
let e = Base64UrlEncodedBytes::new(vec![1, 0, 1]);
match CoreJwtClaimsVerifier::new(
client_id.clone(),
issuer.clone(),
CoreJsonWebKeySet::new(vec![CoreJsonWebKey {
kty: CoreJsonWebKeyType::Symmetric,
use_: Some(CoreJsonWebKeyUse::Signature),
kid: Some(kid.clone()),
n: None,
e: None,
k: Some(Base64UrlEncodedBytes::new(vec![1, 2, 3, 4])),
}]),
)
.verified_claims(valid_rs256_jwt.clone())
{
Err(ClaimsVerificationError::SignatureVerification(
SignatureVerificationError::NoMatchingKey,
)) => {}
other => panic!("unexpected result: {:?}", other),
}
match CoreJwtClaimsVerifier::new(
client_id.clone(),
issuer.clone(),
CoreJsonWebKeySet::new(vec![CoreJsonWebKey {
kty: CoreJsonWebKeyType::RSA,
use_: Some(CoreJsonWebKeyUse::Encryption),
kid: Some(kid.clone()),
n: Some(n.clone()),
e: Some(e.clone()),
k: None,
}]),
)
.verified_claims(valid_rs256_jwt.clone())
{
Err(ClaimsVerificationError::SignatureVerification(
SignatureVerificationError::NoMatchingKey,
)) => {}
other => panic!("unexpected result: {:?}", other),
}
match verifier.verified_claims(
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiIsImtpZCI6Indyb25nX2tleSJ9.eyJhdWQiOlsibXlfY2xpZW50Il0sImlzcyI6I\
mh0dHBzOi8vZXhhbXBsZS5jb20iLCJwYXlsb2FkIjoiaGVsbG8gd29ybGQifQ.lVLomyIyO8WmyS1VZWPu\
cGhRTUyK9RCw90fJC5CfDWUCgt1CBn-aP_ieWWBGfjb4ccR4dl57OYxdLl0Day8QN5pTCBud9QKpQ0rKQX\
K8eBlOW8uSosx8q5pwU_bRyy-XuKJiPlDCOwTEHOp_hOgZFGjoN27MH3Xm8kc0iT3PgyqQ46-wsqHY9S02\
hdJORX7vqYwQLZF8_k_L8K0IG_dC-1Co0g5oAf37oVSdl8hE-ScQ9K-AiSpS-cGYyldbMhyKNDL3ry2cuI\
EUgYSIznkVFuM7RrEdNK222z5PF11ijYx-TM7BIDggbcIyJm-UqpmvVaJImmj5FNkMzuHYznLtdg"
.to_string(),
)).expect("failed to deserialize")
) {
Err(ClaimsVerificationError::SignatureVerification(
SignatureVerificationError::NoMatchingKey,
)) => {}
other => panic!("unexpected result: {:?}", other),
}
verifier
.clone()
.set_client_secret(ClientSecret::new("my_secret".to_string()))
.verified_claims(valid_rs256_jwt.clone())
.expect("verification should succeed");
match CoreJwtClaimsVerifier::new(
client_id.clone(),
issuer.clone(),
CoreJsonWebKeySet::new(vec![rsa_key.clone(), rsa_key.clone()]),
)
.verified_claims(valid_rs256_jwt.clone())
{
Err(ClaimsVerificationError::SignatureVerification(
SignatureVerificationError::AmbiguousKeyId(_),
)) => {}
other => panic!("unexpected result: {:?}", other),
}
match CoreJwtClaimsVerifier::new(
client_id.clone(),
issuer.clone(),
CoreJsonWebKeySet::new(vec![rsa_key.clone(), rsa_key.clone()]),
).verified_claims(
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9.eyJhdWQiO\
lsibXlfY2xpZW50Il0sImlzcyI6Imh0dHBzOi8vZXhhbXBsZS5jb20iLCJwYXlsb2FkIjoiaGVsbG8gd29\
ybGQifQ.jH0v2fQGvH2MD0jn5pQP6W6AF5rJlizyofdyRUIt7E3GraGA1LYDiLAVIfhST3uwJopP-TgtBk\
zc-zyJSvgTR63S8iI1YlHypItpx7r4I9ydzo8GSN5RrZudcU2esY4uEnLbVl17ZVNu4IyTExeKJ0sPM0Hj\
qkOA4XaP2cJwsK-bookNHSA8NRE6adRMrHAKJbor5jrGjpkZAKHbnQFK-wu-nEV_OjS9jpN_FboRZVcDTZ\
GFzeFbqFqHdRn6UWPFnVpVnUhih16UjNH1om6gwc0uFoPWTDxJlXQCFbHMhZtgCbUkXQBH7twPMc4YUziw\
S8GIRKCcXjdrP5oyxmcitQ"
.to_string(),
)).expect("failed to deserialize")
) {
Err(ClaimsVerificationError::SignatureVerification(
SignatureVerificationError::AmbiguousKeyId(_),
)) => {}
other => panic!("unexpected result: {:?}", other),
}
verifier
.verified_claims(valid_rs256_jwt.clone())
.expect("verification should succeed");
match verifier.verified_claims(
serde_json::from_value::<TestClaimsJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsibXlfY2xpZW50Il0sImlzcyI6Imh0dHBzOi8vZXhhbXBsZS5jb\
20iLCJwYXlsb2FkIjoiaGVsbG8gd29ybGQifQ.YmFkX2hhc2g"
.to_string(),
)).expect("failed to deserialize"),
) {
Err(ClaimsVerificationError::SignatureVerification(
SignatureVerificationError::CryptoError(_),
)) => {}
other => panic!("unexpected result: {:?}", other),
}
}
type CoreIdTokenJwt = JsonWebToken<
CoreJweContentEncryptionAlgorithm,
CoreJwsSigningAlgorithm,
CoreJsonWebKeyType,
CoreIdTokenClaims,
JsonWebTokenJsonPayloadSerde,
>;
#[test]
fn test_id_token_verified_claims() {
let rsa_key = serde_json::from_str::<CoreJsonWebKey>(TEST_RSA_PUB_KEY)
.expect("deserialization failed");
let client_id = ClientId::new("my_client".to_string());
let issuer = IssuerUrl::new("https://example.com".to_string()).unwrap();
let mock_current_time = Cell::new(1544932149);
let mock_is_valid_issue_time = Cell::new(true);
{
let public_client_verifier = CoreIdTokenVerifier::new_public_client(
client_id.clone(),
issuer.clone(),
CoreJsonWebKeySet::new(vec![rsa_key.clone()]),
)
.set_time_fn(|| seconds_to_utc(&Seconds::new(mock_current_time.get().into())).unwrap())
.set_issue_time_verifier_fn(|_| {
if mock_is_valid_issue_time.get() {
Ok(())
} else {
Err("Invalid iat claim".to_string())
}
});
let test_jwt_without_nonce =
serde_json::from_value::<CoreIdTokenJwt>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsibXlfY2xpZW50Il0sImlzcyI6Imh0dHBzOi8vZXhhbXBsZ\
S5jb20iLCJzdWIiOiJzdWJqZWN0IiwiZXhwIjoxNTQ0OTMyMTQ5LCJpYXQiOjE1NDQ5Mjg1NDl9.nN\
aTxNwclnTHd1Q9POkddm5wB1w3wJ-gwQWHomhimttk3SWQTLhxI0SSjWrHahGxlfkjufJlSyt-t_VO\
SdcROvIYZTDznDfFZz3oSOev-p9XiZ-EZTS-U6N11Y923sDQjbTMeukz1F3ZFEfn5Mv2xjdEoJccCe\
7SaGuDmVqMqTLXMtsw9NCE_KDd0oKSwDzbJIBBPEfG3JjbKg0Dln7ENHg9wzoNFQzPXrkKzjneBgD3\
vuwFCV5y-e8xUBdLaLZF1kdkDZJIA48uRROLlWjsM8pEptosA5QK07luQCZNqcaZWEczoGXeQs8PyA\
zkNV7JEmti3bJnWSN-ud4cFU0LiQ"
.to_string(),
))
.expect("failed to deserialize");
match public_client_verifier.verified_claims(
&serde_json::from_value::<CoreIdTokenJwt>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsibXlfY2xpZW50Il0sImlzcyI6Imh0dHBzOi8vYXR0YWNrZ\
XIuY29tIiwic3ViIjoic3ViamVjdCIsImV4cCI6MTU0NDkzMjE0OSwiaWF0IjoxNTQ0OTI4NTQ5LCJ\
ub25jZSI6InRoZV9ub25jZSIsImFjciI6InRoZV9hY3IifQ.Pkicxk0dTU5BkSxgqTON6lE7A7ir3l\
aADRyoeRoCNDX3AOx7BXCbfzbda6HJiPskN2nu56w0q-0OdkDSIHls-2xTUlLEJv2Bv0BLYwV5ZVJ8\
hoc-rTd0_oLUb5NzyD80RyVByjVMK8bh6cwysTnr8QDxsEiFZbFo3mVJob2yjPZnNOdcNJWPcVVueP\
8vqMJnx5kHih1gKZpWj_dMN9b2AW6zVLOInW3Ox__gx6fsFFz7rjxItG-PTY_OQMzthqeHUyq4o9y7\
Jv8mB_jFkTZGVKHTPpObHV-qptJ_rnlwvF_mP5GARBLng-4Yd7nmSr31onYL48QDjGOrwPqQ-IyaCQ"
.to_string(),
))
.expect("failed to deserialize"), |_: Option<&Nonce>| Ok(())) {
Err(ClaimsVerificationError::InvalidIssuer(_)) => {}
other => panic!("unexpected result: {:?}", other),
}
mock_current_time.set(1544928549 + 3600);
match public_client_verifier
.verified_claims(&test_jwt_without_nonce, |_: Option<&Nonce>| Ok(()))
{
Err(ClaimsVerificationError::Expired(_)) => {}
other => panic!("unexpected result: {:?}", other),
}
mock_current_time.set(1544928549 + 1);
mock_is_valid_issue_time.set(false);
match public_client_verifier
.verified_claims(&test_jwt_without_nonce, |_: Option<&Nonce>| Ok(()))
{
Err(ClaimsVerificationError::Expired(_)) => {}
other => panic!("unexpected result: {:?}", other),
}
mock_is_valid_issue_time.set(true);
let valid_nonce = Nonce::new("the_nonce".to_string());
public_client_verifier
.verified_claims(&test_jwt_without_nonce, |_: Option<&Nonce>| Ok(()))
.expect("verification should succeed");
match public_client_verifier.verified_claims(&test_jwt_without_nonce, &valid_nonce) {
Err(ClaimsVerificationError::InvalidNonce(_)) => {}
other => panic!("unexpected result: {:?}", other),
}
match public_client_verifier.verified_claims(
&test_jwt_without_nonce,
|nonce: Option<&Nonce>| {
if nonce.iter().any(|n| n.secret() == valid_nonce.secret()) {
Ok(())
} else {
Err("invalid nonce".to_string())
}
},
) {
Err(ClaimsVerificationError::InvalidNonce(_)) => {}
other => panic!("unexpected result: {:?}", other),
}
let test_jwt_with_nonce =
serde_json::from_value::<CoreIdTokenJwt>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsibXlfY2xpZW50Il0sImlzcyI6Imh0dHBzOi8vZXhhbXBsZ\
S5jb20iLCJzdWIiOiJzdWJqZWN0IiwiZXhwIjoxNTQ0OTMyMTQ5LCJpYXQiOjE1NDQ5Mjg1NDksIm5\
vbmNlIjoidGhlX25vbmNlIiwiYWNyIjoidGhlX2FjciIsImF1dGhfdGltZSI6MTU0NDkyODU0OH0.W\
XA7SS9aMh_6rvBEgQce5D2J84OqphmmnCLGgEKRTN5G-UuQTNOBp8VS5_4f3xgzMEEMvGJJauJoALk\
muUeHB-N_ESrkmB3tgDzBSYBa7kuYPHUPYpdjZM2UVolqI9RYyHaWwKjL_Io5YyAazB5lH5ibPaiBl\
UNKGs3cmVsEB22UGMFKM6cek7GinrHQe_aJQsMU839-c2zzlEyFSeI8QBphQtG6AN82IPkNRv8QWmw\
ZjUiB5a-W73Z3gURYMNs7f32BjAUNoJzW0Qj34vzD2djoSHhltE0wHKBzPqGhUM1Y3A-a3q-LS2g1h\
6qgXb_KQ_Mmok8v8ld0cW_aYRLfNg"
.to_string(),
))
.expect("failed to deserialize");
match public_client_verifier.verified_claims(
&test_jwt_with_nonce,
&Nonce::new("different_nonce".to_string()),
) {
Err(ClaimsVerificationError::InvalidNonce(_)) => {}
other => panic!("unexpected result: {:?}", other),
}
match public_client_verifier
.clone()
.set_auth_context_verifier_fn(|acr| {
assert_eq!(**acr.unwrap(), "the_acr");
Err("Invalid acr claim".to_string())
})
.verified_claims(&test_jwt_with_nonce, &valid_nonce)
{
Err(ClaimsVerificationError::InvalidAuthContext(_)) => {}
other => panic!("unexpected result: {:?}", other),
}
let test_jwt_without_auth_time =
serde_json::from_value::<CoreIdTokenJwt>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsibXlfY2xpZW50Il0sImlzcyI6Imh0dHBzOi8vZXhhbXBsZ\
S5jb20iLCJzdWIiOiJzdWJqZWN0IiwiZXhwIjoxNTQ0OTMyMTQ5LCJpYXQiOjE1NDQ5Mjg1NDksIm5\
vbmNlIjoidGhlX25vbmNlIiwiYWNyIjoidGhlX2FjciJ9.c_lU1VRasTg0mB4lwdOzbzvFS_XShMLN\
lAPUpHBaMtCSPtI71L2x3hIByfkqIrAED-Qc_am2gNJ20bifidlkTOO6nyaBrJuaSjwT8aqajEbXon\
5JFswwPvqCIWjd0eV5dXC1MZunpd7ANXSC7Qw16v3m_crc9wcI_fLFCzuAKrWYokGvNy0gr1CxcgVg\
aE9qR0eqaatetzCuaOJhYOq4njrRlGZWtbj5Q56q3zhxJ_yS8K8gv1QcB4sHjUyXIj21jzjUD87zVG\
dJsn8E-nFJSltBdQhEaLksTBH6ZZhkeGicQ8cEPnNeS4L1vfVyAd_cjl64JHLmzw8RUp8XuoF9nA"
.to_string(),
))
.expect("failed to deserialize");
public_client_verifier
.verified_claims(&test_jwt_without_auth_time, |_: Option<&Nonce>| Ok(()))
.expect("verification should succeed");
match public_client_verifier
.clone()
.set_auth_time_verifier_fn(|auth_time| {
assert!(auth_time.is_none());
Err("Invalid auth_time claim".to_string())
})
.verified_claims(&test_jwt_without_auth_time, |_: Option<&Nonce>| Ok(()))
{
Err(ClaimsVerificationError::InvalidAuthTime(_)) => {}
other => panic!("unexpected result: {:?}", other),
}
match public_client_verifier
.clone()
.set_auth_time_verifier_fn(|auth_time| {
assert_eq!(
auth_time.unwrap(),
seconds_to_utc(&Seconds::new(1544928548.into())).unwrap(),
);
Err("Invalid auth_time claim".to_string())
})
.verified_claims(&test_jwt_with_nonce, &valid_nonce)
{
Err(ClaimsVerificationError::InvalidAuthTime(_)) => {}
other => panic!("unexpected result: {:?}", other),
}
public_client_verifier
.verified_claims(&test_jwt_with_nonce, |_: Option<&Nonce>| Ok(()))
.expect("verification should succeed");
public_client_verifier
.verified_claims(&test_jwt_with_nonce, &valid_nonce)
.expect("verification should succeed");
public_client_verifier
.verified_claims(&test_jwt_with_nonce, |nonce: Option<&Nonce>| {
if nonce.iter().any(|n| n.secret() == valid_nonce.secret()) {
Ok(())
} else {
Err("invalid nonce".to_string())
}
})
.expect("verification should succeed");
let test_jwt_hs256 =
serde_json::from_value::<CoreIdTokenJwt>(serde_json::Value::String(
"eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsibXlfY2xpZW50Il0sImlzcyI6Imh0dHBzOi8vZXhhbXBsZ\
S5jb20iLCJzdWIiOiJzdWJqZWN0IiwiZXhwIjoxNTQ0OTMyMTQ5LCJpYXQiOjE1NDQ5Mjg1NDksIm5\
vbmNlIjoidGhlX25vbmNlIn0.xUnSwSbcHsHWyJxwKGg69BIo_CktcyN5BVulGDb_QzE"
.to_string(),
))
.expect("failed to deserialize");
let private_client_verifier = CoreIdTokenVerifier::new_confidential_client(
client_id.clone(),
ClientSecret::new("my_secret".to_string()),
issuer.clone(),
CoreJsonWebKeySet::new(vec![rsa_key.clone()]),
)
.set_time_fn(|| seconds_to_utc(&Seconds::new(mock_current_time.get().into())).unwrap());
match private_client_verifier.verified_claims(&test_jwt_hs256, &valid_nonce) {
Err(ClaimsVerificationError::SignatureVerification(_)) => {}
other => panic!("unexpected result: {:?}", other),
}
private_client_verifier
.clone()
.set_allowed_algs(vec![CoreJwsSigningAlgorithm::HmacSha256])
.verified_claims(&test_jwt_hs256, &valid_nonce)
.expect("verification should succeed");
private_client_verifier
.clone()
.allow_any_alg()
.verified_claims(&test_jwt_hs256, &valid_nonce)
.expect("verification should succeed");
let private_client_verifier_with_other_secret =
CoreIdTokenVerifier::new_confidential_client(
client_id.clone(),
ClientSecret::new("other_secret".to_string()),
issuer.clone(),
CoreJsonWebKeySet::new(vec![rsa_key.clone()]),
)
.allow_any_alg()
.set_time_fn(|| {
seconds_to_utc(&Seconds::new(mock_current_time.get().into())).unwrap()
});
match private_client_verifier_with_other_secret
.verified_claims(&test_jwt_hs256, &valid_nonce)
{
Err(ClaimsVerificationError::SignatureVerification(_)) => {}
other => panic!("unexpected result: {:?}", other),
}
private_client_verifier_with_other_secret
.clone()
.insecure_disable_signature_check()
.verified_claims(&test_jwt_hs256, &valid_nonce)
.expect("verification should succeed");
};
}
#[test]
fn test_new_id_token() {
let client_id = ClientId::new("my_client".to_string());
let issuer = IssuerUrl::new("https://example.com".to_string()).unwrap();
let nonce = Nonce::new("the_nonce".to_string());
let rsa_priv_key = CoreRsaPrivateSigningKey::from_pem(TEST_RSA_PRIV_KEY, None).unwrap();
let id_token = CoreIdToken::new(
CoreIdTokenClaims::new(
issuer.clone(),
vec![Audience::new((*client_id).clone())],
Utc.timestamp(1544932149, 0),
Utc.timestamp(1544928549, 0),
StandardClaims::new(SubjectIdentifier::new("subject".to_string())),
Default::default(),
)
.set_nonce(Some(nonce.clone()))
.set_auth_context_ref(Some(AuthenticationContextClass::new("the_acr".to_string())))
.set_auth_time(Some(Utc.timestamp(1544928548, 0))),
&rsa_priv_key,
CoreJwsSigningAlgorithm::RsaSsaPkcs1V15Sha256,
Some(&AccessToken::new("the_access_token".to_string())),
Some(&AuthorizationCode::new(
"the_authorization_code".to_string(),
)),
)
.unwrap();
let serialized_jwt: serde_json::Value = serde_json::to_value(&id_token).unwrap();
let expected_serialized_jwt =
"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIiwiYXVkIjpbIm15X2NsaWVudCJdL\
CJleHAiOjE1NDQ5MzIxNDksImlhdCI6MTU0NDkyODU0OSwiYXV0aF90aW1lIjoxNTQ0OTI4NTQ4LCJub25jZSI\
6InRoZV9ub25jZSIsImFjciI6InRoZV9hY3IiLCJhdF9oYXNoIjoiWjNJQUNVR00tbXhIV3lZUXZpSzhFUSIsI\
mNfaGFzaCI6Imo2OW1CZmFIbmRMM1Y1RmNoak9LVXciLCJzdWIiOiJzdWJqZWN0In0.CHCWFcIqbCZhZwZH4oY\
_mlcRy5aUQQtlNI0VHNYxiILn9ppRHLL4Bn_LMn9VP8tGXkfZWxCgP25ZTyBXXKfk0fQvnukVdyM0bCOpQbiBg\
5gB9c46l_f-ZznDoHWonpnKky2Gmzk3ocb3TCUQ9GSeRXAzRdRNWTT0ElWNBsLWU4j2IIdnghM78gkXwOC76Rk\
pshgB73ubtuHGdIf5L9Ec3hifHlVjzKuvedAM4SIOjdBOelgtBlF3463ufX_Ut91CjP5TzLMsuK3Lh_vyo8ttn\
S41rBDuetR2ENvR0yj5RjkX_SPY3V0yCW8_NPPu1CHu_1oL0Nma0ohCbF3vnUJcwg";
assert_eq!(expected_serialized_jwt, serialized_jwt.as_str().unwrap());
let rsa_pub_key = serde_json::from_str::<CoreJsonWebKey>(TEST_RSA_PUB_KEY)
.expect("deserialization failed");
let mock_current_time = Cell::new(1544932148);
let verifier = CoreIdTokenVerifier::new_public_client(
client_id,
issuer,
CoreJsonWebKeySet::new(vec![rsa_pub_key.clone()]),
)
.set_time_fn(|| seconds_to_utc(&Seconds::new(mock_current_time.get().into())).unwrap());
id_token.claims(&verifier, &nonce).unwrap();
}
#[test]
fn test_user_info_verified_claims() {
let rsa_key = serde_json::from_str::<CoreJsonWebKey>(TEST_RSA_PUB_KEY)
.expect("deserialization failed");
let client_id = ClientId::new("my_client".to_string());
let issuer = IssuerUrl::new("https://example.com".to_string()).unwrap();
let sub = SubjectIdentifier::new("the_subject".to_string());
let verifier = CoreUserInfoVerifier::new(
client_id.clone(),
issuer.clone(),
CoreJsonWebKeySet::new(vec![rsa_key.clone()]),
Some(sub.clone()),
);
let json_claims = "{\
\"sub\": \"the_subject\",\
\"name\": \"Jane Doe\"\
}";
assert_eq!(
CoreUserInfoClaims::from_json::<crate::reqwest::HttpClientError>(
json_claims.as_bytes(),
Some(&sub)
)
.expect("verification should succeed")
.name()
.unwrap()
.iter()
.collect::<Vec<_>>(),
vec![(None, &EndUserName::new("Jane Doe".to_string()))],
);
match CoreUserInfoClaims::from_json::<crate::reqwest::HttpClientError>(
json_claims.as_bytes(),
Some(&SubjectIdentifier::new("wrong_subject".to_string())),
) {
Err(UserInfoError::ClaimsVerification(ClaimsVerificationError::InvalidSubject(_))) => {}
other => panic!("unexpected result: {:?}", other),
}
let jwt_claims =
serde_json::from_value::<CoreUserInfoJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsibXlfY2xpZW50Il0sImlzcyI6Imh0dHBzOi8vZXhhb\
XBsZS5jb20iLCJzdWIiOiJ0aGVfc3ViamVjdCIsIm5hbWUiOiJKYW5lIERvZSJ9.aX7VpexLAd\
43HtC1cFTot3jmqsr105rB50mzTcS1TXzWcxLbqYf1K7Kf-S1oP-ZCL_dnL9-nu3iDK_vRa6xT\
nGGt3I1JwhoIv6znSS3JOPT1wtekyD-sLcUwqsJHWBBiTSBwlmGG_kVRuGkBtXgVZ9aGlqg9u1\
FlxvyGUJ5q1o9gdb8mKql5ojgsThTNo9qdW3lPIVsiDO-n4mMp4HuOp1re4ZDDkHxiExjtLQAV\
kR4q3SlhJC2mkr4mw3_0a2AW52ocWDiwY_lPcdmohmwFaB8aHlivYLFnmKGQIatEW-KDaW5fFo\
JYreNkplo4FvzXYyxgxAsqHjHMI8MZVEa1IA"
.to_string(),
))
.expect("failed to deserialize");
jwt_claims
.clone()
.claims(&verifier)
.expect("verification should succeed");
match serde_json::from_value::<CoreUserInfoJsonWebToken>(serde_json::Value::String(
"eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsibXlfY2xpZW50Il0sImlzcyI6Imh0dHBzOi8vZXhhb\
XBsZS5jb20iLCJzdWIiOiJ0aGVfc3ViamVjdCIsIm5hbWUiOiJKYW5lIERvZSJ9.bX7VpexLAd\
43HtC1cFTot3jmqsr105rB50mzTcS1TXzWcxLbqYf1K7Kf-S1oP-ZCL_dnL9-nu3iDK_vRa6xT\
nGGt3I1JwhoIv6znSS3JOPT1wtekyD-sLcUwqsJHWBBiTSBwlmGG_kVRuGkBtXgVZ9aGlqg9u1\
FlxvyGUJ5q1o9gdb8mKql5ojgsThTNo9qdW3lPIVsiDO-n4mMp4HuOp1re4ZDDkHxiExjtLQAV\
kR4q3SlhJC2mkr4mw3_0a2AW52ocWDiwY_lPcdmohmwFaB8aHlivYLFnmKGQIatEW-KDaW5fFo\
JYreNkplo4FvzXYyxgxAsqHjHMI8MZVEa1IA"
.to_string(),
))
.expect("failed to deserialize")
.claims(&verifier)
{
Err(ClaimsVerificationError::SignatureVerification(
SignatureVerificationError::CryptoError(_),
)) => {}
other => panic!("unexpected result: {:?}", other),
}
match jwt_claims.clone().claims(&CoreUserInfoVerifier::new(
client_id.clone(),
IssuerUrl::new("https://attacker.com".to_string()).unwrap(),
CoreJsonWebKeySet::new(vec![rsa_key.clone()]),
Some(sub.clone()),
)) {
Err(ClaimsVerificationError::InvalidIssuer(_)) => {}
other => panic!("unexpected result: {:?}", other),
}
jwt_claims
.clone()
.claims(
&CoreUserInfoVerifier::new(
client_id.clone(),
IssuerUrl::new("https://attacker.com".to_string()).unwrap(),
CoreJsonWebKeySet::new(vec![rsa_key.clone()]),
Some(sub.clone()),
)
.require_issuer_match(false),
)
.expect("verification should succeed");
match jwt_claims.clone().claims(&CoreUserInfoVerifier::new(
ClientId::new("wrong_client".to_string()),
issuer.clone(),
CoreJsonWebKeySet::new(vec![rsa_key.clone()]),
Some(sub.clone()),
)) {
Err(ClaimsVerificationError::InvalidAudience(_)) => {}
other => panic!("unexpected result: {:?}", other),
}
jwt_claims
.clone()
.claims(
&CoreUserInfoVerifier::new(
ClientId::new("wrong_client".to_string()),
issuer.clone(),
CoreJsonWebKeySet::new(vec![rsa_key.clone()]),
Some(sub.clone()),
)
.require_audience_match(false),
)
.expect("verification should succeed");
}
#[test]
fn test_new_user_info_claims() {
let claims = CoreUserInfoClaims::new(
StandardClaims {
sub: SubjectIdentifier::new("the_subject".to_string()),
name: Some(EndUserName::new("John Doe".to_string()).into()),
given_name: None,
family_name: None,
middle_name: None,
nickname: None,
preferred_username: None,
profile: None,
picture: None,
website: None,
email: None,
email_verified: None,
gender: None,
birthday: None,
zoneinfo: None,
locale: None,
phone_number: None,
phone_number_verified: None,
address: None,
updated_at: Some(Utc.timestamp(1544928548, 0)),
},
Default::default(),
);
assert_eq!(
"{\"sub\":\"the_subject\",\"name\":\"John Doe\",\"updated_at\":1544928548}",
serde_json::to_string(&claims).unwrap()
);
let rsa_priv_key = CoreRsaPrivateSigningKey::from_pem(TEST_RSA_PRIV_KEY, None).unwrap();
let claims_jwt = CoreUserInfoJsonWebToken::new(
claims,
&rsa_priv_key,
CoreJwsSigningAlgorithm::RsaSsaPkcs1V15Sha256,
)
.unwrap();
assert_eq!(
"eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ0aGVfc3ViamVjdCIsIm5hbWUiOiJKb2huIERvZSIsInVwZGF0ZWRfY\
XQiOjE1NDQ5Mjg1NDh9.nJ7Buckt_p_ACXkyVRCQLqyaW8KhDsk5H9Nu7PdNf4daEcEWm-lGjoSTAfAbDPgHAZ\
78knomgLgDxiGWrj1qdFTIEFep32I3q18VBP_DcMdyuQafipK6T98RgZFWP8YnxlxLPHeJQlRsdMpemHK4vxas\
ZD4A4aIn0K7z5J9RvrR3L7DWnc3fJQ0VU2v5QLePyqNWnFxks5eyl8Ios8JrZhwr4Q8GES8Q4Iw8Sz6W9vYpHK\
2r1YdaACMM4g_TTtV91lpjn-Li2-HxW9NERdLvYvF6HwGIwbss26trp2yjNTARlxBUT6LR7y82oPIJKXIKL1GD\
YeSLeErhb6oTQ0a5gQ",
serde_json::to_value(&claims_jwt).unwrap().as_str().unwrap()
);
}
}