Skip to main content

alibi_plugins/jwt/
callbacks.rs

1use super::JwtSignOptions;
2use alibi_core::AuthRequest;
3use alibi_core::AuthResult;
4use alibi_core::CreateJwk;
5use alibi_core::Jwk;
6use alibi_core::wire::SessionView;
7use alibi_core::wire::UserView;
8use async_trait::async_trait;
9use serde::Serialize;
10use serde_json::Map;
11use serde_json::Value;
12#[derive(Clone, Debug, Serialize)]
13pub struct JwtSession {
14    pub user: UserView,
15    pub session: SessionView,
16    /// The nested deferred-session response field. Direct completed-response
17    /// hooks observe the original stored snapshot and omit this field.
18    #[serde(rename = "needsRefresh", skip_serializing_if = "Option::is_none")]
19    pub needs_refresh: Option<bool>,
20    /// The verified cache clock seen by a direct get-session response hook.
21    #[serde(rename = "updatedAt", skip_serializing_if = "Option::is_none")]
22    pub updated_at: Option<f64>,
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub version: Option<String>,
25}
26
27#[async_trait]
28pub trait DefineJwtPayload: Send + Sync {
29    async fn define_payload(&self, session: &JwtSession) -> AuthResult<Map<String, Value>>;
30}
31
32#[async_trait]
33pub trait DefineJwtSubject: Send + Sync {
34    async fn subject(&self, session: &JwtSession) -> AuthResult<Option<String>>;
35}
36
37#[async_trait]
38pub trait JwtKeyring: Send + Sync {
39    async fn keys(&self, context: &JwtKeyringContext<'_>) -> AuthResult<Vec<Jwk>>;
40    async fn create_key(&self, key: CreateJwk, context: &JwtKeyringContext<'_>) -> AuthResult<Jwk>;
41}
42
43/// The real endpoint context supplied to application key storage. Server-only
44/// operations have a virtual endpoint path and may have no HTTP request.
45#[derive(Clone, Copy, Debug)]
46pub struct JwtKeyringContext<'a> {
47    pub path: &'a str,
48    pub request: Option<&'a AuthRequest>,
49    pub endpoint: Option<&'a alibi_core::endpoint::EndpointCall>,
50}
51
52/// One property in an application-owned remote signing payload.
53#[derive(Clone, Copy, Debug, PartialEq)]
54pub enum RemoteJwtClaim<'a> {
55    Absent,
56    Undefined,
57    Value(&'a alibi_core::utils::json::JsValue),
58}
59
60/// Claims passed to a configured remote signer, before managed JOSE validation.
61///
62/// `raw_claims` preserves IEEE754 numbers and all supplied JSON values. The
63/// added `iat` and `nbf` properties can be own properties with an undefined
64/// value; use `claim` to distinguish these from absent or null properties.
65/// Undefined properties are omitted from `raw_claims`, matching JSON.stringify.
66#[derive(Clone, Debug)]
67pub struct RemoteJwtPayload {
68    pub(in crate::jwt) raw_claims: alibi_core::utils::json::JsValue,
69    pub(in crate::jwt) own_keys: Vec<String>,
70    pub(in crate::jwt) undefined_claims: Vec<String>,
71}
72
73impl RemoteJwtPayload {
74    #[must_use]
75    pub const fn raw_claims(&self) -> &alibi_core::utils::json::JsValue {
76        &self.raw_claims
77    }
78
79    /// Property names in the order observed by the application signer.
80    #[must_use]
81    pub fn own_keys(&self) -> &[String] {
82        &self.own_keys
83    }
84
85    #[must_use]
86    pub fn claim(&self, name: &str) -> RemoteJwtClaim<'_> {
87        if self.undefined_claims.iter().any(|key| key == name) {
88            RemoteJwtClaim::Undefined
89        } else if let Some(value) = self.raw_claims.get(name) {
90            RemoteJwtClaim::Value(value)
91        } else {
92            RemoteJwtClaim::Absent
93        }
94    }
95}
96
97/// An application-owned signer controls serialization, claim validation and
98/// its returned token. Managed local signing rules are not applied beforehand.
99#[async_trait]
100pub trait SignRemoteJwt: Send + Sync {
101    async fn sign(
102        &self,
103        payload: &RemoteJwtPayload,
104        options: &JwtSignOptions,
105    ) -> AuthResult<String>;
106}