Skip to main content

better_auth_api/plugins/jwt/
overrides.rs

1use super::*;
2
3/// Token defaults that replace the entire upstream `jwt` option group for one native signing call.
4#[derive(Clone, Default, serde::Deserialize)]
5#[serde(rename_all = "camelCase")]
6pub struct JwtTokenOptions {
7    pub issuer: Option<String>,
8    pub audience: Option<JwtAudience>,
9    #[serde(default, deserialize_with = "input::expiration")]
10    pub expiration_time: Option<JwtExpiration>,
11    #[serde(skip)]
12    pub custom_sign: Option<JwtCustomSign>,
13}
14
15/// Key defaults that replace the entire upstream `jwks` option group for one native signing call.
16#[derive(Clone, Default, serde::Deserialize)]
17#[serde(rename_all = "camelCase")]
18pub struct JwtKeyOptions {
19    #[serde(rename = "keyPairConfig")]
20    pub key_pair: Option<JwtKeyPairConfig>,
21    pub key_pair_configs: Option<Vec<JwtKeyPairConfig>>,
22    #[serde(default, deserialize_with = "input::duration")]
23    pub rotation_interval: Option<Duration>,
24    pub disable_private_key_encryption: Option<bool>,
25}
26
27/// Independent group replacements for upstream `signJWT.overrideOptions`.
28/// An omitted group retains registration defaults. An empty adapter restores default persistence.
29pub struct JwtCallOverrides<S: AuthSchema> {
30    pub jwt: Option<JwtTokenOptions>,
31    pub jwks: Option<JwtKeyOptions>,
32    pub adapter: Option<JwtCallbacks<S>>,
33}
34
35impl<S: AuthSchema> Default for JwtCallOverrides<S> {
36    fn default() -> Self {
37        Self {
38            jwt: None,
39            jwks: None,
40            adapter: None,
41        }
42    }
43}
44
45impl<S: AuthSchema> JwtCallOverrides<S> {
46    pub(super) fn apply(self, config: &mut JwtPluginConfig, context: &mut AuthContext<S>) -> Value {
47        let mut body = Map::new();
48        if let Some(jwt) = self.jwt {
49            let mut values = Map::new();
50            if let Some(issuer) = &jwt.issuer {
51                let _ = values.insert("issuer".into(), issuer.clone().into());
52            }
53            if let Some(audience) = &jwt.audience {
54                let _ = values.insert("audience".into(), json!(audience));
55            }
56            if let Some(expiration) = &jwt.expiration_time {
57                let value = match expiration {
58                    JwtExpiration::At(value) => Value::Number(value.clone()),
59                    JwtExpiration::After(duration) => {
60                        format!("{}s", duration_seconds(*duration)).into()
61                    }
62                };
63                let _ = values.insert("expirationTime".into(), value);
64            }
65            let _ = body.insert("jwt".into(), values.into());
66            config.issuer = jwt.issuer;
67            config.audience = jwt.audience;
68            config.expiration_time = jwt
69                .expiration_time
70                .unwrap_or(JwtExpiration::After(Duration::minutes(15)));
71            config.custom_sign = jwt.custom_sign;
72            config.define_payload = None;
73            config.get_subject = None;
74        }
75        if let Some(jwks) = self.jwks {
76            let mut values = Map::new();
77            if let Some(parameters) = jwks.key_pair {
78                let _ = values.insert("keyPairConfig".into(), key_pair(parameters));
79            }
80            if let Some(parameters) = &jwks.key_pair_configs {
81                let _ = values.insert(
82                    "keyPairConfigs".into(),
83                    parameters.iter().copied().map(key_pair).collect(),
84                );
85            }
86            if let Some(rotation) = jwks.rotation_interval {
87                let _ = values.insert("rotationInterval".into(), duration_seconds(rotation).into());
88            }
89            if let Some(disabled) = jwks.disable_private_key_encryption {
90                let _ = values.insert("disablePrivateKeyEncryption".into(), disabled.into());
91            }
92            let _ = body.insert("jwks".into(), values.into());
93            let primary = jwks
94                .key_pair
95                .unwrap_or(JwtKeyPairConfig::new(JwtAlgorithm::EdDsa));
96            config.algorithm = primary.algorithm;
97            config.modulus_length = primary.modulus_length;
98            config.key_pair_configs = jwks.key_pair_configs.unwrap_or_default();
99            config.rotation_interval = jwks.rotation_interval;
100            config.disable_private_key_encryption =
101                jwks.disable_private_key_encryption.unwrap_or(false);
102        }
103        if let Some(adapter) = self.adapter {
104            let _ = body.insert("adapter".into(), json!({}));
105            context.extensions.insert(std::sync::Arc::new(adapter));
106        }
107        body.into()
108    }
109}
110
111fn key_pair(parameters: JwtKeyPairConfig) -> Value {
112    json!({"alg": parameters.algorithm.name(), "modulusLength": parameters.modulus_length})
113}
114
115fn duration_seconds(duration: Duration) -> f64 {
116    duration.num_seconds() as f64 + f64::from(duration.subsec_nanos()) / 1e9
117}
118
119#[path = "override_input.rs"]
120mod input;
121
122pub(super) fn from_input<S: AuthSchema>(
123    value: &Value,
124    original: &JwtPluginConfig,
125    original_input: Option<&Value>,
126) -> AuthResult<JwtCallOverrides<S>> {
127    let object = |value: &Value| value.as_object().cloned().unwrap_or_default().into();
128    let mut jwt = value
129        .get("jwt")
130        .map(|value| serde_json::from_value::<JwtTokenOptions>(object(value)))
131        .transpose()?;
132    if let Some(jwt) = &mut jwt
133        && value.get("jwt").is_some_and(Value::is_object)
134        && original_input.and_then(|value| value.get("jwt")).is_some()
135    {
136        jwt.custom_sign = original.custom_sign.clone();
137    }
138    let jwks = value
139        .get("jwks")
140        .map(|value| serde_json::from_value::<JwtKeyOptions>(object(value)))
141        .transpose()?;
142    Ok(JwtCallOverrides {
143        jwt,
144        jwks,
145        adapter: None,
146    })
147}