jwt/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
97#[cfg(doctest)]
98use doc_comment::doctest;
99use serde::{Deserialize, Serialize};
100
101#[cfg(feature = "openssl")]
102pub use crate::algorithm::openssl::PKeyWithDigest;
103pub use crate::algorithm::store::Store;
104pub use crate::algorithm::{AlgorithmType, SigningAlgorithm, VerifyingAlgorithm};
105pub use crate::claims::Claims;
106pub use crate::claims::RegisteredClaims;
107pub use crate::error::Error;
108pub use crate::header::{Header, JoseHeader};
109pub use crate::token::signed::{SignWithKey, SignWithStore};
110pub use crate::token::verified::{VerifyWithKey, VerifyWithStore};
111pub use crate::token::{Unsigned, Unverified, Verified};
112
113pub mod algorithm;
114pub mod claims;
115pub mod error;
116pub mod header;
117pub mod token;
118
119const SEPARATOR: &str = ".";
120
121/// Representation of a structured JWT. Methods vary based on the signature
122/// type `S`.
123pub struct Token<H, C, S> {
124 header: H,
125 claims: C,
126 signature: S,
127}
128
129impl<H, C, S> Token<H, C, S> {
130 pub fn header(&self) -> &H {
131 &self.header
132 }
133
134 pub fn claims(&self) -> &C {
135 &self.claims
136 }
137
138 pub fn remove_signature(self) -> Token<H, C, Unsigned> {
139 Token {
140 header: self.header,
141 claims: self.claims,
142 signature: Unsigned,
143 }
144 }
145}
146
147impl<H, C, S> From<Token<H, C, S>> for (H, C) {
148 fn from(token: Token<H, C, S>) -> Self {
149 (token.header, token.claims)
150 }
151}
152
153/// A trait used to convert objects in base64 encoding. The return type can
154/// be either owned if the header is dynamic, or it can be borrowed if the
155/// header is a static, pre-computed value. It is implemented automatically
156/// for every type that implements
157/// [Serialize](../../serde/trait.Serialize.html). as a base64 encoding of
158/// the object's JSON representation.
159pub trait ToBase64 {
160 fn to_base64(&self) -> Result<Cow<str>, Error>;
161}
162
163impl<T: Serialize> ToBase64 for T {
164 fn to_base64(&self) -> Result<Cow<str>, Error> {
165 let json_bytes = serde_json::to_vec(&self)?;
166 let encoded_json_bytes = base64::encode_config(&json_bytes, base64::URL_SAFE_NO_PAD);
167 Ok(Cow::Owned(encoded_json_bytes))
168 }
169}
170
171/// A trait used to parse objects from base64 encoding. The return type can
172/// be either owned if the header is dynamic, or it can be borrowed if the
173/// header is a static, pre-computed value. It is implemented automatically
174/// for every type that implements
175/// [DeserializeOwned](../../serde/trait.Deserialize.html) for
176/// the base64 encoded JSON representation.
177pub trait FromBase64: Sized {
178 fn from_base64<Input: ?Sized + AsRef<[u8]>>(raw: &Input) -> Result<Self, Error>;
179}
180
181impl<T: for<'de> Deserialize<'de> + Sized> FromBase64 for T {
182 fn from_base64<Input: ?Sized + AsRef<[u8]>>(raw: &Input) -> Result<Self, Error> {
183 let json_bytes = base64::decode_config(raw, base64::URL_SAFE_NO_PAD)?;
184 Ok(serde_json::from_slice(&json_bytes)?)
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use crate::algorithm::AlgorithmType::Hs256;
191 use crate::error::Error;
192 use crate::header::Header;
193 use crate::token::signed::SignWithKey;
194 use crate::token::verified::VerifyWithKey;
195 use crate::Claims;
196 use crate::Token;
197 use hmac::Hmac;
198 use hmac::Mac;
199 use sha2::Sha256;
200
201 #[test]
202 pub fn raw_data() -> Result<(), Error> {
203 let raw = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
204 let token: Token<Header, Claims, _> = Token::parse_unverified(raw)?;
205
206 assert_eq!(token.header.algorithm, Hs256);
207
208 let verifier: Hmac<Sha256> = Hmac::new_from_slice(b"secret")?;
209 assert!(token.verify_with_key(&verifier).is_ok());
210
211 Ok(())
212 }
213
214 #[test]
215 pub fn roundtrip() -> Result<(), Error> {
216 let token: Token<Header, Claims, _> = Default::default();
217 let key: Hmac<Sha256> = Hmac::new_from_slice(b"secret")?;
218 let signed_token = token.sign_with_key(&key)?;
219 let signed_token_str = signed_token.as_str();
220
221 let recreated_token: Token<Header, Claims, _> = Token::parse_unverified(signed_token_str)?;
222
223 assert_eq!(signed_token.header(), recreated_token.header());
224 assert_eq!(signed_token.claims(), recreated_token.claims());
225 recreated_token.verify_with_key(&key)?;
226 Ok(())
227 }
228}