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}