jwt_next/lib.rs
1//! ### Only Claims
2//! If you don't care about that header as long as the header is verified, signing
3//! and verification can be done with just a few traits.
4//! #### Signing
5//! Claims can be any `serde::Serialize` type, usually derived with
6//! `serde_derive`.
7//! ```rust
8//! use hmac::{Hmac, Mac};
9//! use jwt::SignWithKey;
10//! use sha2::Sha256;
11//! use std::collections::BTreeMap;
12//!
13//! # use jwt::Error;
14//! # fn try_main() -> Result<(), Error> {
15//! let key: Hmac<Sha256> = Hmac::new_from_slice(b"some-secret")?;
16//! let mut claims = BTreeMap::new();
17//! claims.insert("sub", "someone");
18//! let token_str = claims.sign_with_key(&key)?;
19//! assert_eq!(token_str, "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lb25lIn0.5wwE1sBrs-vftww_BGIuTVDeHtc1Jsjo-fiHhDwR8m0");
20//! # Ok(())
21//! # }
22//! # try_main().unwrap()
23//! ```
24//! #### Verification
25//! Claims can be any `serde::Deserialize` type, usually derived with
26//! `serde_derive`.
27//! ```rust
28//! use hmac::{Hmac, Mac};
29//! use jwt::VerifyWithKey;
30//! use sha2::Sha256;
31//! use std::collections::BTreeMap;
32//!
33//! # use jwt::Error;
34//! # fn try_main() -> Result<(), Error> {
35//! let key: Hmac<Sha256> = Hmac::new_from_slice(b"some-secret")?;
36//! let token_str = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lb25lIn0.5wwE1sBrs-vftww_BGIuTVDeHtc1Jsjo-fiHhDwR8m0";
37//! let claims: BTreeMap<String, String> = token_str.verify_with_key(&key)?;
38//! assert_eq!(claims["sub"], "someone");
39//! # Ok(())
40//! # }
41//! # try_main().unwrap()
42//! ```
43//! ### Header and Claims
44//! If you need to customize the header, you can use the `Token` struct. For
45//! convenience, a `Header` struct is provided for all of the commonly defined
46//! fields, but any type that implements `JoseHeader` can be used.
47//! #### Signing
48//! Both header and claims have to implement `serde::Serialize`.
49//! ```rust
50//! use hmac::{Hmac, Mac};
51//! use jwt::{AlgorithmType, Header, SignWithKey, Token};
52//! use sha2::Sha384;
53//! use std::collections::BTreeMap;
54//!
55//! # use jwt::Error;
56//! # fn try_main() -> Result<(), Error> {
57//! let key: Hmac<Sha384> = Hmac::new_from_slice(b"some-secret")?;
58//! let header = Header {
59//! algorithm: AlgorithmType::Hs384,
60//! ..Default::default()
61//! };
62//! let mut claims = BTreeMap::new();
63//! claims.insert("sub", "someone");
64//! let token = Token::new(header, claims).sign_with_key(&key)?;
65//! assert_eq!(token.as_str(), "eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJzb21lb25lIn0.WM_WnPUkHK6zm6Wz7zk1kmIxz990Te7nlDjQ3vzcye29szZ-Sj47rLNSTJNzpQd_");
66//! # Ok(())
67//! # }
68//! # try_main().unwrap()
69//! ```
70//! #### Verification
71//! Both header and claims have to implement `serde::Deserialize`.
72//! ```rust
73//! use hmac::{Hmac, Mac};
74//! use jwt::{AlgorithmType, Header, Token, VerifyWithKey};
75//! use sha2::Sha384;
76//! use std::collections::BTreeMap;
77//!
78//! # use jwt::Error;
79//! # fn try_main() -> Result<(), Error> {
80//! let key: Hmac<Sha384> = Hmac::new_from_slice(b"some-secret")?;
81//! let token_str = "eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJzb21lb25lIn0.WM_WnPUkHK6zm6Wz7zk1kmIxz990Te7nlDjQ3vzcye29szZ-Sj47rLNSTJNzpQd_";
82//! let token: Token<Header, BTreeMap<String, String>, _> = token_str.verify_with_key(&key)?;
83//! let header = token.header();
84//! let claims = token.claims();
85//! assert_eq!(header.algorithm, AlgorithmType::Hs384);
86//! assert_eq!(claims["sub"], "someone");
87//! # Ok(())
88//! # }
89//! # try_main().unwrap()
90//! ```
91
92#[cfg(doctest)]
93doctest!("../README.md");
94
95use std::borrow::Cow;
96
97use base64::Engine;
98#[cfg(doctest)]
99use doc_comment::doctest;
100use serde::{Deserialize, Serialize};
101
102#[cfg(feature = "openssl")]
103pub use crate::algorithm::openssl::PKeyWithDigest;
104pub use crate::algorithm::rust_crypto::asymmetric;
105pub use crate::algorithm::store::Store;
106pub use crate::algorithm::{AlgorithmType, SigningAlgorithm, VerifyingAlgorithm};
107pub use crate::claims::Claims;
108pub use crate::claims::RegisteredClaims;
109pub use crate::error::Error;
110pub use crate::header::{Header, JoseHeader};
111pub use crate::token::signed::{SignWithKey, SignWithStore};
112pub use crate::token::verified::{VerifyWithKey, VerifyWithStore};
113pub use crate::token::{Unsigned, Unverified, Verified};
114
115pub mod algorithm;
116pub mod claims;
117pub mod error;
118pub mod header;
119pub mod token;
120
121const SEPARATOR: &str = ".";
122
123/// Representation of a structured JWT. Methods vary based on the signature
124/// type `S`.
125pub struct Token<H, C, S> {
126 header: H,
127 claims: C,
128 signature: S,
129}
130
131impl<H, C, S> Token<H, C, S> {
132 pub fn header(&self) -> &H {
133 &self.header
134 }
135
136 pub fn claims(&self) -> &C {
137 &self.claims
138 }
139
140 pub fn remove_signature(self) -> Token<H, C, Unsigned> {
141 Token {
142 header: self.header,
143 claims: self.claims,
144 signature: Unsigned,
145 }
146 }
147}
148
149impl<H, C, S> From<Token<H, C, S>> for (H, C) {
150 fn from(token: Token<H, C, S>) -> Self {
151 (token.header, token.claims)
152 }
153}
154
155/// A trait used to convert objects in base64 encoding. The return type can
156/// be either owned if the header is dynamic, or it can be borrowed if the
157/// header is a static, pre-computed value. It is implemented automatically
158/// for every type that implements
159/// [Serialize](../../serde/trait.Serialize.html). as a base64 encoding of
160/// the object's JSON representation.
161pub trait ToBase64 {
162 fn to_base64(&self) -> Result<Cow<str>, Error>;
163}
164
165impl<T: Serialize> ToBase64 for T {
166 fn to_base64(&self) -> Result<Cow<str>, Error> {
167 let json_bytes = serde_json::to_vec(&self)?;
168 let encoded_json_bytes =
169 base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(json_bytes);
170 Ok(Cow::Owned(encoded_json_bytes))
171 }
172}
173
174/// A trait used to parse objects from base64 encoding. The return type can
175/// be either owned if the header is dynamic, or it can be borrowed if the
176/// header is a static, pre-computed value. It is implemented automatically
177/// for every type that implements
178/// [DeserializeOwned](../../serde/trait.Deserialize.html) for
179/// the base64 encoded JSON representation.
180pub trait FromBase64: Sized {
181 fn from_base64<Input: ?Sized + AsRef<[u8]>>(raw: &Input) -> Result<Self, Error>;
182}
183
184impl<T: for<'de> Deserialize<'de> + Sized> FromBase64 for T {
185 fn from_base64<Input: ?Sized + AsRef<[u8]>>(raw: &Input) -> Result<Self, Error> {
186 let json_bytes = base64::engine::general_purpose::URL_SAFE_NO_PAD.decode(raw)?;
187 Ok(serde_json::from_slice(&json_bytes)?)
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use crate::algorithm::AlgorithmType::Hs256;
194 use crate::error::Error;
195 use crate::header::Header;
196 use crate::token::signed::SignWithKey;
197 use crate::token::verified::VerifyWithKey;
198 use crate::Claims;
199 use crate::Token;
200 use hmac::Hmac;
201 use hmac::Mac;
202 use sha2::Sha256;
203
204 #[test]
205 pub fn raw_data() -> Result<(), Error> {
206 let raw = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
207 let token: Token<Header, Claims, _> = Token::parse_unverified(raw)?;
208
209 assert_eq!(token.header.algorithm, Hs256);
210
211 let verifier: Hmac<Sha256> = Hmac::new_from_slice(b"secret")?;
212 assert!(token.verify_with_key(&verifier).is_ok());
213
214 Ok(())
215 }
216
217 #[test]
218 pub fn roundtrip() -> Result<(), Error> {
219 let token: Token<Header, Claims, _> = Default::default();
220 let key: Hmac<Sha256> = Hmac::new_from_slice(b"secret")?;
221 let signed_token = token.sign_with_key(&key)?;
222 let signed_token_str = signed_token.as_str();
223
224 let recreated_token: Token<Header, Claims, _> = Token::parse_unverified(signed_token_str)?;
225
226 assert_eq!(signed_token.header(), recreated_token.header());
227 assert_eq!(signed_token.claims(), recreated_token.claims());
228 recreated_token.verify_with_key(&key)?;
229 Ok(())
230 }
231}