use super::AuthError;
use crate::algorithm::Algorithm;
use crate::engine::shared_config::SharedVerifyConfig;
use crate::engine::{check_algorithm, check_header, check_id_token_cat, raw};
use crate::{KeySet, SharedAuthError};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LogoutHint {
pub sub: String,
pub aud: Vec<String>,
}
#[derive(Debug, Clone, thiserror::Error, PartialEq, Eq)]
pub enum LogoutHintError {
#[error("id_token_hint: issuer mismatch")]
IssuerMismatch,
#[error("id_token_hint: subject claim absent")]
SubjectMissing,
#[error(transparent)]
Profile(#[from] AuthError),
#[error(transparent)]
Jose(#[from] SharedAuthError),
}
pub fn verify_logout_hint(
token: &str,
key_set: &KeySet,
expected_issuer: &str,
) -> Result<LogoutHint, LogoutHintError> {
let shared = SharedVerifyConfig::new(
expected_issuer.to_owned(),
String::new(),
"JWT",
8 * 1024,
Algorithm::ALL.to_vec(),
);
if token.len() > shared.max_token_size {
return Err(SharedAuthError::OversizedToken.into());
}
if token.starts_with('{') {
return Err(SharedAuthError::JwsJsonRejected.into());
}
if token.split('.').count() == 5 {
return Err(SharedAuthError::JwePayload.into());
}
check_algorithm::run(token, &shared)?;
check_header::run(token, &shared, key_set)?;
let payload = raw::parse_payload_json(token)?;
check_id_token_cat::run(&payload)?;
if payload.get("iss").and_then(|v| v.as_str()) != Some(expected_issuer) {
return Err(LogoutHintError::IssuerMismatch);
}
let sub = payload
.get("sub")
.and_then(|v| v.as_str())
.filter(|s| !s.is_empty())
.ok_or(LogoutHintError::SubjectMissing)?
.to_owned();
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(),
};
Ok(LogoutHint { sub, aud })
}