Skip to main content

better_auth_api/plugins/jwt/
options.rs

1use super::*;
2use std::{future::Future, pin::Pin, sync::Arc};
3
4/// Expected token recipients, preserving the string or array claim representation.
5#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
6#[serde(untagged)]
7pub enum JwtAudience {
8    /// One recipient encoded as a string.
9    Single(String),
10    /// Recipients encoded as an array, including an explicit empty array.
11    Multiple(Vec<String>),
12}
13
14impl From<String> for JwtAudience {
15    fn from(value: String) -> Self {
16        Self::Single(value)
17    }
18}
19
20impl From<&str> for JwtAudience {
21    fn from(value: &str) -> Self {
22        Self::Single(value.into())
23    }
24}
25
26impl From<Vec<String>> for JwtAudience {
27    fn from(value: Vec<String>) -> Self {
28        Self::Multiple(value)
29    }
30}
31
32impl JwtAudience {
33    pub(super) fn recipients(&self) -> Vec<&str> {
34        match self {
35            Self::Single(value) => vec![value],
36            Self::Multiple(values) => values.iter().map(String::as_str).collect(),
37        }
38    }
39}
40
41/// Default expiration when a payload does not supply its own `exp` claim.
42#[derive(Clone, Debug)]
43pub enum JwtExpiration {
44    /// Lifetime relative to the payload's issue time, or the current time when omitted.
45    After(Duration),
46    /// Absolute NumericDate in seconds. Fractional values remain observable.
47    At(serde_json::Number),
48}
49
50impl From<Duration> for JwtExpiration {
51    fn from(value: Duration) -> Self {
52        Self::After(value)
53    }
54}
55
56impl From<chrono::DateTime<Utc>> for JwtExpiration {
57    fn from(value: chrono::DateTime<Utc>) -> Self {
58        Self::At(value.timestamp().into())
59    }
60}
61
62impl From<i64> for JwtExpiration {
63    fn from(value: i64) -> Self {
64        Self::At(value.into())
65    }
66}
67
68impl JwtExpiration {
69    pub(super) fn claim(&self, issued: f64) -> Value {
70        match self {
71            Self::At(value) => Value::Number(value.clone()),
72            Self::After(duration) => {
73                let seconds =
74                    duration.num_seconds() as f64 + f64::from(duration.subsec_nanos()) / 1e9;
75                let expires = issued + (seconds + 0.5).floor();
76                // Preserve integer NumericDates for typed JWT consumers, as JSON.stringify does.
77                if expires.fract() == 0.0
78                    && (i64::MIN as f64..-(i64::MIN as f64)).contains(&expires)
79                {
80                    Value::from(expires as i64)
81                } else {
82                    Value::from(expires)
83                }
84            }
85        }
86    }
87}
88
89/// An asynchronous application callback result.
90pub type JwtCallbackFuture<T> = Pin<Box<dyn Future<Output = AuthResult<T>> + Send>>;
91/// Define claims from the complete authenticated session and user object.
92pub type JwtDefinePayload =
93    Arc<dyn Fn(Value) -> JwtCallbackFuture<Map<String, Value>> + Send + Sync>;
94/// Select a subject from the complete authenticated session and user object.
95pub type JwtGetSubject = Arc<dyn Fn(Value) -> JwtCallbackFuture<String> + Send + Sync>;
96/// Sign with application-managed keys. The callback must set `alg` and `kid`.
97pub type JwtCustomSign =
98    Arc<dyn Fn(Map<String, Value>, JwtSigningOptions) -> JwtCallbackFuture<String> + Send + Sync>;
99
100/// Per-call protected headers and key selection, matching upstream signing overrides.
101#[derive(Clone, Debug, Default)]
102pub struct JwtSigningOptions {
103    /// Pin a previously provisioned key. Missing or expired keys fail.
104    pub key_id: Option<String>,
105    /// Pin an algorithm. Only configured algorithms can be provisioned lazily.
106    pub algorithm: Option<JwtAlgorithm>,
107    /// Additional protected headers. Local signing always supplies `alg` and `kid`.
108    pub header: Map<String, Value>,
109}
110
111/// Parameters for primary or lazily provisioned additional signing keys.
112#[derive(Clone, Copy, Debug, serde::Deserialize)]
113#[serde(rename_all = "camelCase")]
114pub struct JwtKeyPairConfig {
115    /// Signing algorithm.
116    #[serde(rename = "alg")]
117    pub algorithm: JwtAlgorithm,
118    /// RSA modulus length for RS256 and PS256. Defaults to 2048 bits.
119    #[serde(default = "default_modulus_length")]
120    pub modulus_length: u32,
121}
122
123impl JwtKeyPairConfig {
124    /// Use the upstream default key parameters for an algorithm.
125    pub fn new(algorithm: JwtAlgorithm) -> Self {
126        Self {
127            algorithm,
128            modulus_length: 2048,
129        }
130    }
131}
132
133fn default_modulus_length() -> u32 {
134    2048
135}