rustauth_plugins/jwt/
sign.rs1use josekit::jws::alg::ecdsa::EcdsaJwsAlgorithm::{Es256, Es512};
2use josekit::jws::alg::eddsa::EddsaJwsAlgorithm::Eddsa;
3use josekit::jws::alg::rsassa::RsassaJwsAlgorithm::Rs256;
4use josekit::jws::alg::rsassa_pss::RsassaPssJwsAlgorithm::Ps256;
5use josekit::jws::JwsHeader;
6use josekit::jwt::{self, JwtPayload};
7use rustauth_core::context::AuthContext;
8use rustauth_core::error::RustAuthError;
9use serde_json::Value;
10
11use super::claims::{claims_with_defaults, JwtClaims};
12use super::keys::{generate_jwk, JwkAlgorithm};
13use super::{adapter, crypto, JwtOptions};
14
15pub async fn sign_jwt(
16 context: &AuthContext,
17 claims: JwtClaims,
18 override_options: Option<JwtOptions>,
19) -> Result<String, RustAuthError> {
20 let options = override_options.unwrap_or_default();
21 sign_jwt_with_options(context, claims, &options).await
22}
23
24pub(crate) async fn sign_jwt_with_options(
25 context: &AuthContext,
26 claims: JwtClaims,
27 options: &JwtOptions,
28) -> Result<String, RustAuthError> {
29 let claims = claims_with_defaults(claims, &context.base_url, options)?;
30 if let Some(sign) = &options.jwt.sign {
31 return sign(claims).await;
32 }
33
34 let mut key = adapter::get_latest_key(context, options).await?;
35 if key
36 .as_ref()
37 .and_then(|key| key.expires_at)
38 .is_some_and(|expires_at| expires_at <= time::OffsetDateTime::now_utc())
39 {
40 key = None;
41 }
42 let key = match key {
43 Some(key) => key,
44 None => {
45 let key = crypto::encrypt_private_key(
46 context,
47 generate_jwk(options)?,
48 options.jwks.disable_private_key_encryption,
49 )?;
50 adapter::create_jwk(context, options, key).await?
51 }
52 };
53 let private_key = crypto::decrypt_private_key(
54 context,
55 &key.private_key,
56 options.jwks.disable_private_key_encryption,
57 )?;
58 encode_with_key(
59 &private_key,
60 key.alg.unwrap_or_else(|| options.algorithm()),
61 &key.id,
62 claims,
63 )
64}
65
66fn encode_with_key(
67 private_key: &str,
68 algorithm: JwkAlgorithm,
69 key_id: &str,
70 claims: JwtClaims,
71) -> Result<String, RustAuthError> {
72 let jwk = josekit::jwk::Jwk::from_bytes(private_key)
73 .map_err(|error| RustAuthError::Crypto(error.to_string()))?;
74 let payload = jwt_payload(claims)?;
75 let mut header = JwsHeader::new();
76 header.set_algorithm(algorithm.as_str());
77 header.set_key_id(key_id);
78
79 match algorithm {
80 JwkAlgorithm::EdDsa => jwt::encode_with_signer(
81 &payload,
82 &header,
83 &Eddsa
84 .signer_from_jwk(&jwk)
85 .map_err(|error| RustAuthError::Crypto(error.to_string()))?,
86 ),
87 JwkAlgorithm::Es256 => jwt::encode_with_signer(
88 &payload,
89 &header,
90 &Es256
91 .signer_from_jwk(&jwk)
92 .map_err(|error| RustAuthError::Crypto(error.to_string()))?,
93 ),
94 JwkAlgorithm::Es512 => jwt::encode_with_signer(
95 &payload,
96 &header,
97 &Es512
98 .signer_from_jwk(&jwk)
99 .map_err(|error| RustAuthError::Crypto(error.to_string()))?,
100 ),
101 JwkAlgorithm::Rs256 => jwt::encode_with_signer(
102 &payload,
103 &header,
104 &Rs256
105 .signer_from_jwk(&jwk)
106 .map_err(|error| RustAuthError::Crypto(error.to_string()))?,
107 ),
108 JwkAlgorithm::Ps256 => jwt::encode_with_signer(
109 &payload,
110 &header,
111 &Ps256
112 .signer_from_jwk(&jwk)
113 .map_err(|error| RustAuthError::Crypto(error.to_string()))?,
114 ),
115 }
116 .map_err(|error| RustAuthError::Crypto(error.to_string()))
117}
118
119fn jwt_payload(claims: JwtClaims) -> Result<JwtPayload, RustAuthError> {
120 let mut payload = JwtPayload::new();
121 for (key, value) in claims {
122 payload
123 .set_claim(&key, Some(value_to_jose(value)))
124 .map_err(|error| RustAuthError::Crypto(error.to_string()))?;
125 }
126 Ok(payload)
127}
128
129fn value_to_jose(value: Value) -> josekit::Value {
130 serde_json::from_value(value).unwrap_or(josekit::Value::Null)
131}