ssi_jwt/lib.rs
1//! JSON Web Token (JWT) implementation following [RFC7519].
2//!
3//! [RFC7519]: <https://datatracker.ietf.org/doc/html/rfc7519>
4//!
5//! # Usage
6//!
7//! ## Decoding & Verification
8//!
9//! ```
10//! # async_std::task::block_on(async {
11//! use serde_json::json;
12//! use ssi_jwk::JWK;
13//! use ssi_jws::Jws;
14//! use ssi_jwt::ToDecodedJwt;
15//!
16//! let jws = Jws::new(b"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBTbWl0aCIsImlhdCI6MTcxNTM0Mjc5MCwiaXNzIjoiaHR0cDovL2V4YW1wbGUub3JnLyNpc3N1ZXIifQ.S51Gmlkwy4UxOhhc4nVl4_sHHVPSrNmjZDwJCDXDbKp2MT8-UyhZLw03gVKe-JRUzcsteWoeRCUoA5rwnuTSoA").unwrap();
17//!
18//! let jwk: JWK = json!({
19//! "kty": "EC",
20//! "use": "sig",
21//! "crv": "P-256",
22//! "x": "dxdB360AJqJFYhdctoKZD_a_P6vLGAxtEVaCLnyraXQ",
23//! "y": "iH6o0l5AECsfRuEw2Eghbrp-6Fob3j98-1Cbe1YOmwM",
24//! "alg": "ES256"
25//! }).try_into().unwrap();
26//!
27//! assert!(jws.verify_jwt(&jwk).await.unwrap().is_ok());
28//! # })
29//! ```
30//!
31//! Internally [`ToDecodedJwt::verify_jwt`] uses
32//! [`ToDecodedJwt::to_decoded_jwt`] to decode the JWT,
33//! then [`DecodedJws::verify`] to validate the signature and
34//! registered claims.
35//!
36//! [`DecodedJws::verify`]: ssi_jws::DecodedJws::verify
37//!
38//! ## Signature
39//!
40//! Use the [`JwsPayload::sign`] method to sign a payload into a JWT.
41//!
42//! [`JwsPayload::sign`]: ssi_jws::JwsPayload::sign
43//!
44//! ```
45//! # async_std::task::block_on(async {
46//! use serde_json::json;
47//! use ssi_jwk::JWK;
48//! use ssi_jws::JwsPayload;
49//! use ssi_jwt::{JWTClaims, Issuer, IssuedAt, ExpirationTime};
50//!
51//! let mut claims: JWTClaims = Default::default();
52//! claims.registered.set(Issuer("http://example.org/#issuer".parse().unwrap()));
53//! claims.registered.set(IssuedAt("1715342790".parse().unwrap()));
54//! claims.registered.set(ExpirationTime("1746881356".parse().unwrap()));
55//! claims.private.set("name".to_owned(), "John Smith".into());
56//!
57//! let jwk: JWK = json!({
58//! "kty": "EC",
59//! "d": "3KSLs0_obYeQXfEI9I3BBH5y7aOm028bEx3rW6i5UN4",
60//! "use": "sig",
61//! "crv": "P-256",
62//! "x": "dxdB360AJqJFYhdctoKZD_a_P6vLGAxtEVaCLnyraXQ",
63//! "y": "iH6o0l5AECsfRuEw2Eghbrp-6Fob3j98-1Cbe1YOmwM",
64//! "alg": "ES256"
65//! }).try_into().unwrap();
66//!
67//! let jwt = claims.sign(&jwk).await.unwrap();
68//! assert_eq!(jwt, "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwOi8vZXhhbXBsZS5vcmcvI2lzc3VlciIsImV4cCI6MTc0Njg4MTM1NiwiaWF0IjoxNzE1MzQyNzkwLCJuYW1lIjoiSm9obiBTbWl0aCJ9.zBfMZzfQuuSfzcZmnz0MjXwT1sP26qwVq2GZX3qL0DR3wRMVG-wbCu9jPJ48l-F_q7W253_VqMWpoLluHo-gpg")
69//! # })
70//! ```
71use serde::de::DeserializeOwned;
72use serde::Serialize;
73
74use ssi_jwk::{Algorithm, JWK};
75use ssi_jws::{Error, Header};
76
77mod claims;
78mod datatype;
79mod decoding;
80
81pub use claims::*;
82pub use datatype::*;
83pub use decoding::*;
84
85pub fn encode_sign<Claims: Serialize>(
86 algorithm: Algorithm,
87 claims: &Claims,
88 key: &JWK,
89) -> Result<String, Error> {
90 let payload = serde_json::to_string(claims)?;
91 let header = Header {
92 algorithm,
93 key_id: key.key_id.clone(),
94 type_: Some("JWT".to_string()),
95 ..Default::default()
96 };
97 ssi_jws::encode_sign_custom_header(&payload, key, &header)
98}
99
100pub fn encode_unsigned<Claims: Serialize>(claims: &Claims) -> Result<String, Error> {
101 let payload = serde_json::to_string(claims)?;
102 ssi_jws::encode_unsigned(&payload)
103}
104
105pub fn decode_verify<Claims: DeserializeOwned>(jwt: &str, key: &JWK) -> Result<Claims, Error> {
106 let (_header, payload) = ssi_jws::decode_verify(jwt, key)?;
107 let claims = serde_json::from_slice(&payload)?;
108 Ok(claims)
109}
110
111// for vc-test-suite
112pub fn decode_unverified<Claims: DeserializeOwned>(jwt: &str) -> Result<Claims, Error> {
113 let (_header, payload) = ssi_jws::decode_unverified(jwt)?;
114 let claims = serde_json::from_slice(&payload)?;
115 Ok(claims)
116}