diff --git a/README.md b/README.md index edcf517..80645dd 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ let token = decode::(&token, "secret", Algorithm::HS256, &Validation::de - the token or its signature is invalid - error while decoding base64 or the result of decoding base64 is not valid UTF-8 -- validation failed +- validation of at least one reserved claim failed ### Validation This library validates automatically the `iat`, `exp` and `nbf` claims if found. You can also validate the `sub`, `iss` and `aud` but @@ -54,7 +54,7 @@ use jsonwebtoken::Validation; // Default valuation let validation = Validation::default(); -// Adding some leeway +// Adding some leeway (in ms) for iat, exp and nbf checks let mut validation = Validation {leeway: 1000 * 60, ..Default::default()}; // Checking issuer let mut validation = Validation {iss: Some("issuer".to_string()), ..Default::default()}; diff --git a/examples/custom_header.rs b/examples/custom_header.rs index 62aad32..ddca412 100644 --- a/examples/custom_header.rs +++ b/examples/custom_header.rs @@ -27,6 +27,7 @@ fn main() { Ok(t) => t, Err(_) => panic!() // in practice you would return the error }; + println!("{:?}", token); let token_data = match decode::(&token, key.as_ref(), Algorithm::HS512, Validation::default()) { Ok(c) => c, diff --git a/src/crypto.rs b/src/crypto.rs index b5415a8..c432d35 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -3,15 +3,9 @@ use std::sync::Arc; use base64; use ring::{rand, digest, hmac, signature}; use ring::constant_time::verify_slices_are_equal; -use serde::de::Deserialize; -use serde::ser::Serialize; use untrusted; - use errors::{Result, ErrorKind}; -use header::Header; -use serialization::{from_jwt_part, to_jwt_part, from_jwt_part_claims, TokenData}; -use validation::{Validation, validate}; /// The algorithms supported for signing/verifying @@ -33,8 +27,10 @@ pub enum Algorithm { } -/// Take the payload of a JWT and sign it using the algorithm given. -/// Returns the base64 url safe encoded of the result +/// Take the payload of a JWT, sign it using the algorithm given and return +/// the base64 url safe encoded of the result. +/// +/// Only use this function if you want to do something other than JWT. pub fn sign(signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result { match algorithm { Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => { @@ -78,18 +74,10 @@ pub fn sign(signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result(header: &Header, claims: &T, key: &[u8]) -> Result { - let encoded_header = to_jwt_part(&header)?; - let encoded_claims = to_jwt_part(&claims)?; - let signing_input = [encoded_header.as_ref(), encoded_claims.as_ref()].join("."); - let signature = sign(&*signing_input, key.as_ref(), header.alg)?; - - Ok([signing_input, signature].join(".")) -} - /// Compares the signature given with a re-computed signature for HMAC or using the public key -/// for RSA +/// for RSA. +/// +/// Only use this function if you want to do something other than JWT. /// /// `signature` is the signature part of a jwt (text after the second '.') /// @@ -125,38 +113,3 @@ pub fn verify(signature: &str, signing_input: &str, key: &[u8], algorithm: Algor }, } } - -/// Used in decode: takes the result of a rsplit and ensure we only get 2 parts -/// Errors if we don't -macro_rules! expect_two { - ($iter:expr) => {{ - let mut i = $iter; - match (i.next(), i.next(), i.next()) { - (Some(first), Some(second), None) => (first, second), - _ => return Err(ErrorKind::InvalidToken.into()) - } - }} -} - -/// Decode a token into a struct containing 2 fields: `claims` and `header`. -/// -/// If the token or its signature is invalid or the claims fail validation, it will return an error. -pub fn decode(token: &str, key: &[u8], algorithm: Algorithm, validation: Validation) -> Result> { - let (signature, signing_input) = expect_two!(token.rsplitn(2, '.')); - - if validation.validate_signature && !verify(signature, signing_input, key, algorithm)? { - return Err(ErrorKind::InvalidSignature.into()); - } - - let (claims, header) = expect_two!(signing_input.rsplitn(2, '.')); - - let header: Header = from_jwt_part(header)?; - if header.alg != algorithm { - return Err(ErrorKind::WrongAlgorithmHeader.into()); - } - let (decoded_claims, claims_map): (T, _) = from_jwt_part_claims(claims)?; - - validate(&claims_map, &validation)?; - - Ok(TokenData { header: header, claims: decoded_claims }) -} diff --git a/src/lib.rs b/src/lib.rs index 1a2567d..32c83f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ //! Create and parses JWT (JSON Web Tokens) //! +//! Documentation: [stable](https://docs.rs/jsonwebtoken/) #![recursion_limit = "300"] #![deny(missing_docs)] @@ -26,11 +27,100 @@ pub use crypto::{ Algorithm, sign, verify, - encode, - decode, }; pub use validation::Validation; + +use serde::de::Deserialize; +use serde::ser::Serialize; + +use errors::{Result, ErrorKind}; +use serialization::{TokenData, from_jwt_part, from_jwt_part_claims, to_jwt_part}; +use validation::{validate}; + + +/// Encode the header and claims given and sign the payload using the algorithm from the header and the key +/// +/// ```rust,ignore +/// #[macro_use] +/// extern crate serde_derive; +/// use jsonwebtoken::{encode, Algorithm, Header}; +/// +/// /// #[derive(Debug, Serialize, Deserialize)] +/// struct Claims { +/// sub: String, +/// company: String +/// } +/// +/// let my_claims = Claims { +/// sub: "b@b.com".to_owned(), +/// company: "ACME".to_owned() +/// }; +/// +/// // my_claims is a struct that implements Serialize +/// // This will create a JWT using HS256 as algorithm +/// let token = encode(&Header::default(), &my_claims, "secret".as_ref()).unwrap(); +/// ``` +pub fn encode(header: &Header, claims: &T, key: &[u8]) -> Result { + let encoded_header = to_jwt_part(&header)?; + let encoded_claims = to_jwt_part(&claims)?; + let signing_input = [encoded_header.as_ref(), encoded_claims.as_ref()].join("."); + let signature = sign(&*signing_input, key.as_ref(), header.alg)?; + + Ok([signing_input, signature].join(".")) +} + +/// Used in decode: takes the result of a rsplit and ensure we only get 2 parts +/// Errors if we don't +macro_rules! expect_two { + ($iter:expr) => {{ + let mut i = $iter; + match (i.next(), i.next(), i.next()) { + (Some(first), Some(second), None) => (first, second), + _ => return Err(ErrorKind::InvalidToken.into()) + } + }} +} + +/// Decode a token into a struct containing 2 fields: `claims` and `header`. +/// +/// If the token or its signature is invalid or the claims fail validation, it will return an error. +/// +/// ```rust,ignore +/// #[macro_use] +/// extern crate serde_derive; +/// use jsonwebtoken::{decode, Algorithm, Validation}; +/// +/// #[derive(Debug, Serialize, Deserialize)] +/// struct Claims { +/// sub: String, +/// company: String +/// } +/// +/// let token = "a.jwt.token".to_string(); +/// // Claims is a struct that implements Deserialize +/// let token_data = decode::(&token, "secret", Algorithm::HS256, &Validation::default()); +/// ``` +pub fn decode(token: &str, key: &[u8], algorithm: Algorithm, validation: Validation) -> Result> { + let (signature, signing_input) = expect_two!(token.rsplitn(2, '.')); + + if validation.validate_signature && !verify(signature, signing_input, key, algorithm)? { + return Err(ErrorKind::InvalidSignature.into()); + } + + let (claims, header) = expect_two!(signing_input.rsplitn(2, '.')); + + let header: Header = from_jwt_part(header)?; + if header.alg != algorithm { + return Err(ErrorKind::WrongAlgorithmHeader.into()); + } + let (decoded_claims, claims_map): (T, _) = from_jwt_part_claims(claims)?; + + validate(&claims_map, &validation)?; + + Ok(TokenData { header: header, claims: decoded_claims }) +} + // To consider: //pub mod prelude { // pub use crypto::{Algorithm, encode, decode};