rfc7515_a2/rfc7515a2.rs
1use jaws::Compact;
2
3// JAWS provides JWT format for printing JWTs in a style similar to the example above,
4// which is directly inspired by the way the ACME standard shows JWTs.
5use jaws::JWTFormat;
6
7// JAWS provides a single token type which is generic over the state of the token.
8// The states are defined in the `state` module, and are used to track the
9// signing and verification status.
10use jaws::Token;
11
12use jaws::key::DeserializeJWK;
13// The unverified token state, used like `Token<.., Unverified<..>, ..>`.
14// It is generic over the type of the custom header parameters.
15use jaws::token::Unverified;
16
17// JAWS provides type-safe support for JWT claims.
18use jaws::{Claims, RegisteredClaims};
19
20// We are going to use an RSA private key to sign our JWT, provided by
21// the `rsa` crate in the RustCrypto suite.
22use rsa::pkcs8::DecodePrivateKey;
23
24// The signing algorithm we will use (`RS256`) relies on the SHA-256 hash
25// function, so we get it here from the `sha2` crate in the RustCrypto suite.
26use sha2::Sha256;
27
28// Using serde_json allows us to quickly construct a serializable payload,
29// but applications may want to instead define a struct and use serde to
30// derive serialize and deserialize for added type safety.
31use serde_json::json;
32
33// Trait to convert a SigningKey into a VerifyingKey.
34use signature::Keypair;
35
36fn main() -> Result<(), Box<dyn std::error::Error>> {
37 // This key is from RFC 7515, Appendix A.2. Provide your own key instead!
38 // The key here is stored as a PKCS#8 PEM file, but you can leverage
39 // RustCrypto to load a variety of other formats.
40 let key = rsa::RsaPrivateKey::from_pkcs8_pem(include_str!(concat!(
41 env!("CARGO_MANIFEST_DIR"),
42 "/examples/rfc7515a2.pem"
43 )))
44 .unwrap();
45
46 // We will sign the JWT with the RS256 algorithm: RSA with SHA-256.
47 let alg = rsa::pkcs1v15::SigningKey::<Sha256>::new(key);
48
49 // Claims can combine registered and custom fields. The claims object
50 // can be any type which implements [serde::Serialize].
51 let claims: Claims<serde_json::Value, (), String, (), ()> = Claims {
52 registered: RegisteredClaims {
53 subject: "1234567890".to_string().into(),
54 ..Default::default()
55 },
56 claims: json!({
57 "name": "John Doe",
58 "admin": true,
59 }),
60 };
61
62 // Create a token with the default headers, and no custom headers.
63 // The unit type can be used here because it implements [serde::Serialize],
64 // but a custom type could be passed if we wanted to have custom header
65 // fields.
66 let mut token = Token::compact((), claims);
67
68 // We can modify the headers freely before signing the JWT. In this case,
69 // we provide the `typ` header, which is optional in the JWT spec.
70 *token.header_mut().r#type() = Some("JWT".to_string());
71
72 // We can also ask that some fields be derived from the signing key, for example,
73 // this will derive the JWK field in the header from the signing key.
74 token.header_mut().key().derived();
75
76 println!("=== Initial JWT ===");
77 // Initially the JWT has no defined signature:
78 println!("{}", token.formatted());
79
80 // Sign the token with the algorithm, and print the result.
81 let signed = token.sign::<_, rsa::pkcs1v15::Signature>(&alg).unwrap();
82
83 println!("=== Signed JWT ===");
84
85 println!("JWT:");
86 println!("{}", signed.formatted());
87 println!("Token: {}", signed.rendered().unwrap());
88
89 // We can't modify the token after signing it (that would change the signature)
90 // but we can access fields and read from them:
91 println!(
92 "Type: {:?}, Algorithm: {:?}",
93 signed.header().r#type(),
94 signed.header().algorithm(),
95 );
96
97 // We can also verify tokens.
98 let token: Token<Claims<serde_json::Value>, Unverified<()>, Compact> =
99 signed.rendered().unwrap().parse().unwrap();
100
101 println!("=== Parsed JWT ===");
102
103 // Unverified tokens can be printed for debugging, but there is deliberately
104 // no access to the payload, only to the header fields.
105 println!("JWT:");
106 println!("{}", token.formatted());
107
108 // We can use the JWK to verify that the token is signed with the correct key.
109 let hdr = token.header();
110 let jwk = hdr.key().unwrap();
111 let key = rsa::RsaPublicKey::from_jwk(jwk).unwrap();
112
113 assert_eq!(&key, alg.verifying_key().as_ref());
114 println!("=== Verification === ");
115 let alg: rsa::pkcs1v15::VerifyingKey<Sha256> = alg.verifying_key();
116
117 // We can't access the claims until we verify the token.
118 let verified = token
119 .verify::<_, jaws::algorithms::SignatureBytes>(&alg)
120 .unwrap();
121
122 println!("=== Verified JWT ===");
123 println!("JWT:");
124 println!("{}", verified.formatted());
125 println!(
126 "Payload: \n{}",
127 serde_json::to_string_pretty(&verified.payload()).unwrap()
128 );
129
130 Ok(())
131}