//! ### Only Claims //! If you don't care about that header as long as the header is verified, signing //! and verification can be done with just a few traits. //! #### Signing //! Claims can be any `serde::Serialize` type, usually derived with //! `serde_derive`. //! ```rust //! use hmac::{Hmac, Mac}; //! use jwt::SignWithKey; //! use sha2::Sha256; //! use std::collections::BTreeMap; //! //! # use jwt::Error; //! # fn try_main() -> Result<(), Error> { //! let key: Hmac = Hmac::new_from_slice(b"some-secret")?; //! let mut claims = BTreeMap::new(); //! claims.insert("sub", "someone"); //! let token_str = claims.sign_with_key(&key)?; //! assert_eq!(token_str, "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lb25lIn0.5wwE1sBrs-vftww_BGIuTVDeHtc1Jsjo-fiHhDwR8m0"); //! # Ok(()) //! # } //! # try_main().unwrap() //! ``` //! #### Verification //! Claims can be any `serde::Deserialize` type, usually derived with //! `serde_derive`. //! ```rust //! use hmac::{Hmac, Mac}; //! use jwt::VerifyWithKey; //! use sha2::Sha256; //! use std::collections::BTreeMap; //! //! # use jwt::Error; //! # fn try_main() -> Result<(), Error> { //! let key: Hmac = Hmac::new_from_slice(b"some-secret")?; //! let token_str = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lb25lIn0.5wwE1sBrs-vftww_BGIuTVDeHtc1Jsjo-fiHhDwR8m0"; //! let claims: BTreeMap = token_str.verify_with_key(&key)?; //! assert_eq!(claims["sub"], "someone"); //! # Ok(()) //! # } //! # try_main().unwrap() //! ``` //! ### Header and Claims //! If you need to customize the header, you can use the `Token` struct. For //! convenience, a `Header` struct is provided for all of the commonly defined //! fields, but any type that implements `JoseHeader` can be used. //! #### Signing //! Both header and claims have to implement `serde::Serialize`. //! ```rust //! use hmac::{Hmac, Mac}; //! use jwt::{AlgorithmType, Header, SignWithKey, Token}; //! use sha2::Sha384; //! use std::collections::BTreeMap; //! //! # use jwt::Error; //! # fn try_main() -> Result<(), Error> { //! let key: Hmac = Hmac::new_from_slice(b"some-secret")?; //! let header = Header { //! algorithm: AlgorithmType::Hs384, //! ..Default::default() //! }; //! let mut claims = BTreeMap::new(); //! claims.insert("sub", "someone"); //! let token = Token::new(header, claims).sign_with_key(&key)?; //! assert_eq!(token.as_str(), "eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJzb21lb25lIn0.WM_WnPUkHK6zm6Wz7zk1kmIxz990Te7nlDjQ3vzcye29szZ-Sj47rLNSTJNzpQd_"); //! # Ok(()) //! # } //! # try_main().unwrap() //! ``` //! #### Verification //! Both header and claims have to implement `serde::Deserialize`. //! ```rust //! use hmac::{Hmac, Mac}; //! use jwt::{AlgorithmType, Header, Token, VerifyWithKey}; //! use sha2::Sha384; //! use std::collections::BTreeMap; //! //! # use jwt::Error; //! # fn try_main() -> Result<(), Error> { //! let key: Hmac = Hmac::new_from_slice(b"some-secret")?; //! let token_str = "eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJzb21lb25lIn0.WM_WnPUkHK6zm6Wz7zk1kmIxz990Te7nlDjQ3vzcye29szZ-Sj47rLNSTJNzpQd_"; //! let token: Token, _> = token_str.verify_with_key(&key)?; //! let header = token.header(); //! let claims = token.claims(); //! assert_eq!(header.algorithm, AlgorithmType::Hs384); //! assert_eq!(claims["sub"], "someone"); //! # Ok(()) //! # } //! # try_main().unwrap() //! ``` #[cfg(doctest)] doctest!("../README.md"); use std::borrow::Cow; #[cfg(doctest)] use doc_comment::doctest; use serde::{Deserialize, Serialize}; #[cfg(feature = "openssl")] pub use crate::algorithm::openssl::PKeyWithDigest; pub use crate::algorithm::store::Store; pub use crate::algorithm::{AlgorithmType, SigningAlgorithm, VerifyingAlgorithm}; pub use crate::claims::Claims; pub use crate::claims::RegisteredClaims; pub use crate::error::Error; pub use crate::header::{Header, JoseHeader}; pub use crate::token::signed::{SignWithKey, SignWithStore}; pub use crate::token::verified::{VerifyWithKey, VerifyWithStore}; pub use crate::token::{Unsigned, Unverified, Verified}; pub mod algorithm; pub mod claims; pub mod error; pub mod header; pub mod token; const SEPARATOR: &str = "."; /// Representation of a structured JWT. Methods vary based on the signature /// type `S`. pub struct Token { header: H, claims: C, signature: S, } impl Token { pub fn header(&self) -> &H { &self.header } pub fn claims(&self) -> &C { &self.claims } pub fn remove_signature(self) -> Token { Token { header: self.header, claims: self.claims, signature: Unsigned, } } } impl From> for (H, C) { fn from(token: Token) -> Self { (token.header, token.claims) } } /// A trait used to convert objects in base64 encoding. The return type can /// be either owned if the header is dynamic, or it can be borrowed if the /// header is a static, pre-computed value. It is implemented automatically /// for every type that implements /// [Serialize](../../serde/trait.Serialize.html). as a base64 encoding of /// the object's JSON representation. pub trait ToBase64 { fn to_base64(&self) -> Result, Error>; } impl ToBase64 for T { fn to_base64(&self) -> Result, Error> { let json_bytes = serde_json::to_vec(&self)?; let encoded_json_bytes = base64::encode_config(&json_bytes, base64::URL_SAFE_NO_PAD); Ok(Cow::Owned(encoded_json_bytes)) } } /// A trait used to parse objects from base64 encoding. The return type can /// be either owned if the header is dynamic, or it can be borrowed if the /// header is a static, pre-computed value. It is implemented automatically /// for every type that implements /// [DeserializeOwned](../../serde/trait.Deserialize.html) for /// the base64 encoded JSON representation. pub trait FromBase64: Sized { fn from_base64>(raw: &Input) -> Result; } impl Deserialize<'de> + Sized> FromBase64 for T { fn from_base64>(raw: &Input) -> Result { let json_bytes = base64::decode_config(raw, base64::URL_SAFE_NO_PAD)?; Ok(serde_json::from_slice(&json_bytes)?) } } #[cfg(test)] mod tests { use crate::algorithm::AlgorithmType::Hs256; use crate::error::Error; use crate::header::Header; use crate::token::signed::SignWithKey; use crate::token::verified::VerifyWithKey; use crate::Claims; use crate::Token; use hmac::Hmac; use hmac::Mac; use sha2::Sha256; #[test] pub fn raw_data() -> Result<(), Error> { let raw = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"; let token: Token = Token::parse_unverified(raw)?; assert_eq!(token.header.algorithm, Hs256); let verifier: Hmac = Hmac::new_from_slice(b"secret")?; assert!(token.verify_with_key(&verifier).is_ok()); Ok(()) } #[test] pub fn roundtrip() -> Result<(), Error> { let token: Token = Default::default(); let key: Hmac = Hmac::new_from_slice(b"secret")?; let signed_token = token.sign_with_key(&key)?; let signed_token_str = signed_token.as_str(); let recreated_token: Token = Token::parse_unverified(signed_token_str)?; assert_eq!(signed_token.header(), recreated_token.header()); assert_eq!(signed_token.claims(), recreated_token.claims()); recreated_token.verify_with_key(&key)?; Ok(()) } }