use super::JwtPlugin;
use super::JwtSession;
use super::JwtSignOptions;
use super::unauthorized;
use alibi_core::AuthContext;
use alibi_core::AuthError;
use alibi_core::AuthRequest;
use alibi_core::AuthResponse;
use alibi_core::AuthResult;
use alibi_core::AuthSchema;
use chrono::Utc;
use serde_json::Map;
use serde_json::Value;
use serde_json::json;
impl JwtPlugin {
pub(in crate::jwt) async fn session_token(
&self,
req: &AuthRequest,
ctx: &AuthContext<impl AuthSchema>,
) -> AuthResult<String> {
let read = alibi_core::session::cookie_cache::runtime::authenticated(ctx, req, false)
.await
.map_err(|_error| unauthorized())?
.ok_or_else(unauthorized)?;
let session = JwtSession {
user: match read.user {
alibi_core::AuthenticatedUser::Stored(user) => ctx.user_view(&user),
alibi_core::AuthenticatedUser::Cached(user) => *user,
},
session: req.virtual_session().cloned().unwrap_or(read.session),
needs_refresh: read.needs_refresh,
updated_at: None,
version: None,
};
self.sign_session_token(Some(req), ctx, &session).await
}
pub(in crate::jwt) async fn sign_session_token(
&self,
req: Option<&AuthRequest>,
ctx: &AuthContext<impl AuthSchema>,
session: &JwtSession,
) -> AuthResult<String> {
let application_payload = match &self.config.define_payload {
Some(define) => define.define_payload(session).await?,
None => serde_json::to_value(&session.user)?
.as_object()
.cloned()
.ok_or_else(|| AuthError::internal("User payload was not an object"))?,
};
let mut payload = Map::new();
_ = payload.insert("iat".to_owned(), json!(Utc::now().timestamp()));
payload.extend(application_payload);
let subject = match &self.config.define_subject {
Some(define) => define
.subject(session)
.await?
.unwrap_or_else(|| session.user.id.clone()),
None => session.user.id.clone(),
};
_ = payload.insert("sub".to_owned(), json!(subject));
self.sign_jwt(payload, &JwtSignOptions::default(), req, ctx)
.await
}
pub(in crate::jwt) async fn jwks(
&self,
req: &AuthRequest,
ctx: &AuthContext<impl AuthSchema>,
) -> AuthResult<AuthResponse> {
if self.config.remote_url.is_some() {
return Ok(AuthResponse::new(404).with_header("content-type", "application/json"));
}
Ok(AuthResponse::json(
200,
&self.jwks_value(Some(req), ctx).await?,
)?)
}
pub(in crate::jwt) async fn jwks_value(
&self,
request: Option<&AuthRequest>,
ctx: &AuthContext<impl AuthSchema>,
) -> AuthResult<Value> {
if self.config.remote_url.is_some() {
return Err(AuthError::Api {
status: 404,
code: None,
message: String::new(),
});
}
let mut keys = self.keys(request, ctx).await?;
if keys.is_empty() {
_ = self.create_jwk(None, request, ctx).await?;
keys = self.keys(request, ctx).await?;
}
if keys.is_empty() {
return Err(AuthError::config(
"No key sets found. Make sure you have a key in your database.",
));
}
let now = Utc::now();
let keys = keys
.into_iter()
.filter(|key| {
key.expires_at
.is_none_or(|expires| expires + self.config.grace_period > now)
})
.map(|key| {
let mut public = Map::new();
_ = public.insert(
"alg".to_owned(),
json!(
key.alg
.as_deref()
.unwrap_or(self.config.key_pair.algorithm.as_str())
),
);
if let Some(curve) = &key.crv {
_ = public.insert("crv".to_owned(), json!(curve));
}
let parsed: Map<String, Value> = serde_json::from_str(&key.public_key)?;
public.extend(parsed);
_ = public.insert("kid".to_owned(), json!(key.id));
Ok::<_, AuthError>(public)
})
.collect::<AuthResult<Vec<_>>>()?;
Ok(json!({ "keys": keys }))
}
}