Reorganise a bit and add more docs

This commit is contained in:
Vincent Prouillet 2017-04-13 16:36:32 +09:00
parent 3e3c752974
commit d144227256
4 changed files with 102 additions and 58 deletions

View File

@ -42,7 +42,7 @@ let token = decode::<Claims>(&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()};

View File

@ -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::<Claims>(&token, key.as_ref(), Algorithm::HS512, Validation::default()) {
Ok(c) => c,

View File

@ -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<String> {
match algorithm {
Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => {
@ -78,18 +74,10 @@ pub fn sign(signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result<Str
}
}
/// Encode the header and claims given and sign the payload using the algorithm from the header and the key
pub fn encode<T: Serialize>(header: &Header, claims: &T, key: &[u8]) -> Result<String> {
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<T: Deserialize>(token: &str, key: &[u8], algorithm: Algorithm, validation: Validation) -> Result<TokenData<T>> {
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 })
}

View File

@ -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<T: Serialize>(header: &Header, claims: &T, key: &[u8]) -> Result<String> {
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::<Claims>(&token, "secret", Algorithm::HS256, &Validation::default());
/// ```
pub fn decode<T: Deserialize>(token: &str, key: &[u8], algorithm: Algorithm, validation: Validation) -> Result<TokenData<T>> {
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};