use std::marker::PhantomData;
use super::scopes::ScopeSet;
use super::{AuthError, AddressClaim, Claims, VerifyConfig};
use crate::engine::shared_error::SharedAuthError;
use crate::engine::{
check_acr, check_algorithm, check_at_hash, check_auth_time, check_azp, check_c_hash,
check_header, check_id_token_cat, check_id_token_pii, check_nonce, raw,
};
use crate::KeySet;
pub async fn verify<S: ScopeSet>(
token: &str,
cfg: &VerifyConfig,
key_set: &KeySet,
) -> Result<Claims<S>, AuthError> {
if token.len() > cfg.shared.max_token_size {
return Err(AuthError::Jose(SharedAuthError::OversizedToken));
}
if token.starts_with('{') {
return Err(AuthError::Jose(SharedAuthError::JwsJsonRejected));
}
if token.split('.').count() == 5 {
return Err(AuthError::Jose(SharedAuthError::JwePayload));
}
check_algorithm::run(token, &cfg.shared)?;
check_header::run(token, &cfg.shared, key_set)?;
let payload = raw::parse_payload_json(token)?;
check_id_token_cat::run(&payload)?;
check_nonce::run(&payload, &cfg.expected_nonce)?;
check_at_hash::run(&payload, cfg)?;
check_c_hash::run(&payload, cfg)?;
check_azp::run(&payload, cfg)?;
check_auth_time::run(&payload, cfg)?;
check_acr::run(&payload, cfg)?;
check_id_token_pii::run(&payload, S::names())?;
Ok(deserialize_claims::<S>(&payload))
}
fn deserialize_claims<S: ScopeSet>(payload: &serde_json::Value) -> Claims<S> {
let s = |key: &str| payload.get(key).and_then(|v| v.as_str()).map(str::to_owned);
let i = |key: &str| payload.get(key).and_then(|v| v.as_i64());
let b = |key: &str| payload.get(key).and_then(|v| v.as_bool());
let aud = match payload.get("aud") {
Some(serde_json::Value::String(s)) => vec![s.clone()],
Some(serde_json::Value::Array(a)) => a
.iter()
.filter_map(|v| v.as_str().map(str::to_owned))
.collect(),
_ => Vec::new(),
};
let amr = payload.get("amr").and_then(|v| v.as_array()).map(|a| {
a.iter()
.filter_map(|v| v.as_str().map(str::to_owned))
.collect()
});
let address = payload
.get("address")
.and_then(|v| serde_json::from_value::<AddressClaim>(v.clone()).ok());
Claims {
iss: s("iss").unwrap_or_default(),
sub: s("sub").unwrap_or_default(),
aud,
exp: i("exp").unwrap_or(0),
iat: i("iat").unwrap_or(0),
nonce: s("nonce").unwrap_or_default(),
azp: s("azp"),
auth_time: i("auth_time"),
acr: s("acr"),
amr,
email: s("email"),
email_verified: b("email_verified"),
name: s("name"),
given_name: s("given_name"),
family_name: s("family_name"),
middle_name: s("middle_name"),
nickname: s("nickname"),
preferred_username: s("preferred_username"),
profile: s("profile"),
picture: s("picture"),
website: s("website"),
gender: s("gender"),
birthdate: s("birthdate"),
zoneinfo: s("zoneinfo"),
locale: s("locale"),
updated_at: i("updated_at"),
phone_number: s("phone_number"),
phone_number_verified: b("phone_number_verified"),
address,
_scope: PhantomData,
}
}