e870254
use crate::config::jwt::JwtConfig;
use crate::jwt::{Jwks, JwtError, SigningKey, sign, signing_key};
use actix_web::web;
use serde::Serialize;
use crate::server::errors::{AuthError, AuthResult};
pub struct SigningState {
key: SigningKey,
kid: String,
jwks: Jwks,
issuer: String,
}
impl SigningState {
pub fn from_config(config: &JwtConfig) -> AuthResult<Self> {
let material = config.private_key.as_deref().ok_or_else(|| {
AuthError::Unavailable("jwt.private_key is required to issue tokens".to_owned())
})?;
let key = signing_key(material).map_err(map)?;
let kid = config.key_id.trim().to_owned();
if kid.is_empty() {
return Err(AuthError::Unavailable("jwt.key_id is required".to_owned()));
}
let mut entries = vec![(kid.clone(), key.verifying_key())];
for retired in &config.retired {
// 重复 kid 会让验签按配置顺序落到不确定的公钥上,轮换期表现为随机 BadSignature。
let retired_kid = retired.key_id.trim();
if retired_kid.is_empty() || entries.iter().any(|(existing, _)| existing == retired_kid)
{
return Err(AuthError::Unavailable(
"jwt.retired keys require distinct non-empty key_id values".to_owned(),
));
}
let signing = signing_key(&retired.private_key).map_err(map)?;
entries.push((retired_kid.to_owned(), signing.verifying_key()));
}
let jwks = Jwks::multi(entries);
Ok(Self {
key,
kid,
jwks,
issuer: config.issuer.clone(),
})
}
pub fn issuer(&self) -> &str {
&self.issuer
}
pub fn kid(&self) -> &str {
&self.kid
}
pub fn jwks(&self) -> &Jwks {
&self.jwks
}
pub fn sign<T: Serialize>(&self, claims: &T) -> AuthResult<String> {
sign(&self.key, &self.kid, claims).map_err(map)
}
/// 签 id_token:标题 `typ` 为 `JWT`,与访问令牌的 `at+jwt` 区分。
pub fn sign_id_token<T: Serialize>(&self, claims: &T) -> AuthResult<String> {
crate::jwt::sign_typed(&self.key, &self.kid, crate::jwt::ID_TOKEN_TYP, claims).map_err(map)
}
}
pub async fn jwks(svc: web::Data<crate::server::AuthSvc>) -> web::Json<Jwks> {
web::Json(svc.signing().jwks().clone())
}
fn map(error: JwtError) -> AuthError {
AuthError::Unavailable(error.to_string())
}