rustfmt
This commit is contained in:
parent
d63af8d6e0
commit
8f0809318d
|
@ -1,23 +1,20 @@
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
extern crate test;
|
|
||||||
extern crate jsonwebtoken as jwt;
|
extern crate jsonwebtoken as jwt;
|
||||||
|
extern crate test;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
|
||||||
use jwt::{encode, decode, Header, Validation};
|
use jwt::{decode, encode, Header, Validation};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
struct Claims {
|
struct Claims {
|
||||||
sub: String,
|
sub: String,
|
||||||
company: String
|
company: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn bench_encode(b: &mut test::Bencher) {
|
fn bench_encode(b: &mut test::Bencher) {
|
||||||
let claim = Claims {
|
let claim = Claims { sub: "b@b.com".to_owned(), company: "ACME".to_owned() };
|
||||||
sub: "b@b.com".to_owned(),
|
|
||||||
company: "ACME".to_owned()
|
|
||||||
};
|
|
||||||
|
|
||||||
b.iter(|| encode(&Header::default(), &claim, "secret".as_ref()));
|
b.iter(|| encode(&Header::default(), &claim, "secret".as_ref()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,8 @@ extern crate jsonwebtoken as jwt;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
|
||||||
use jwt::{encode, decode, Header, Algorithm, Validation};
|
use jwt::errors::ErrorKind;
|
||||||
use jwt::errors::{ErrorKind};
|
use jwt::{decode, encode, Algorithm, Header, Validation};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
struct Claims {
|
struct Claims {
|
||||||
|
@ -14,11 +13,8 @@ struct Claims {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let my_claims = Claims {
|
let my_claims =
|
||||||
sub: "b@b.com".to_owned(),
|
Claims { sub: "b@b.com".to_owned(), company: "ACME".to_owned(), exp: 10000000000 };
|
||||||
company: "ACME".to_owned(),
|
|
||||||
exp: 10000000000,
|
|
||||||
};
|
|
||||||
let key = "secret";
|
let key = "secret";
|
||||||
|
|
||||||
let mut header = Header::default();
|
let mut header = Header::default();
|
||||||
|
@ -27,17 +23,18 @@ fn main() {
|
||||||
|
|
||||||
let token = match encode(&header, &my_claims, key.as_ref()) {
|
let token = match encode(&header, &my_claims, key.as_ref()) {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(_) => panic!() // in practice you would return the error
|
Err(_) => panic!(), // in practice you would return the error
|
||||||
};
|
};
|
||||||
println!("{:?}", token);
|
println!("{:?}", token);
|
||||||
|
|
||||||
let token_data = match decode::<Claims>(&token, key.as_ref(), &Validation::new(Algorithm::HS512)) {
|
let token_data =
|
||||||
Ok(c) => c,
|
match decode::<Claims>(&token, key.as_ref(), &Validation::new(Algorithm::HS512)) {
|
||||||
Err(err) => match *err.kind() {
|
Ok(c) => c,
|
||||||
ErrorKind::InvalidToken => panic!(), // Example on how to handle a specific error
|
Err(err) => match *err.kind() {
|
||||||
_ => panic!()
|
ErrorKind::InvalidToken => panic!(), // Example on how to handle a specific error
|
||||||
}
|
_ => panic!(),
|
||||||
};
|
},
|
||||||
|
};
|
||||||
println!("{:?}", token_data.claims);
|
println!("{:?}", token_data.claims);
|
||||||
println!("{:?}", token_data.header);
|
println!("{:?}", token_data.header);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,8 @@ extern crate jsonwebtoken as jwt;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
|
||||||
use jwt::{encode, decode, Header, Validation};
|
use jwt::errors::ErrorKind;
|
||||||
use jwt::errors::{ErrorKind};
|
use jwt::{decode, encode, Header, Validation};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
struct Claims {
|
struct Claims {
|
||||||
|
@ -14,28 +13,22 @@ struct Claims {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let my_claims = Claims {
|
let my_claims =
|
||||||
sub: "b@b.com".to_owned(),
|
Claims { sub: "b@b.com".to_owned(), company: "ACME".to_owned(), exp: 10000000000 };
|
||||||
company: "ACME".to_owned(),
|
|
||||||
exp: 10000000000,
|
|
||||||
};
|
|
||||||
let key = "secret";
|
let key = "secret";
|
||||||
let token = match encode(&Header::default(), &my_claims, key.as_ref()) {
|
let token = match encode(&Header::default(), &my_claims, key.as_ref()) {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(_) => panic!() // in practice you would return the error
|
Err(_) => panic!(), // in practice you would return the error
|
||||||
};
|
};
|
||||||
|
|
||||||
let validation = Validation {
|
let validation = Validation { sub: Some("b@b.com".to_string()), ..Validation::default() };
|
||||||
sub: Some("b@b.com".to_string()),
|
|
||||||
..Validation::default()
|
|
||||||
};
|
|
||||||
let token_data = match decode::<Claims>(&token, key.as_ref(), &validation) {
|
let token_data = match decode::<Claims>(&token, key.as_ref(), &validation) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(err) => match *err.kind() {
|
Err(err) => match *err.kind() {
|
||||||
ErrorKind::InvalidToken => panic!("Token is invalid"), // Example on how to handle a specific error
|
ErrorKind::InvalidToken => panic!("Token is invalid"), // Example on how to handle a specific error
|
||||||
ErrorKind::InvalidIssuer => panic!("Issuer is invalid"), // Example on how to handle a specific error
|
ErrorKind::InvalidIssuer => panic!("Issuer is invalid"), // Example on how to handle a specific error
|
||||||
_ => panic!("Some other errors")
|
_ => panic!("Some other errors"),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
println!("{:?}", token_data.claims);
|
println!("{:?}", token_data.claims);
|
||||||
println!("{:?}", token_data.header);
|
println!("{:?}", token_data.header);
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
use_small_heuristics = "max"
|
|
@ -1,13 +1,12 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use base64;
|
use base64;
|
||||||
use ring::{rand, digest, hmac, signature};
|
|
||||||
use ring::constant_time::verify_slices_are_equal;
|
use ring::constant_time::verify_slices_are_equal;
|
||||||
use untrusted;
|
use ring::{digest, hmac, rand, signature};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use untrusted;
|
||||||
|
|
||||||
use errors::{Result, Error, ErrorKind, new_error};
|
use errors::{new_error, Error, ErrorKind, Result};
|
||||||
|
|
||||||
|
|
||||||
/// The algorithms supported for signing/verifying
|
/// The algorithms supported for signing/verifying
|
||||||
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
|
||||||
|
@ -53,9 +52,7 @@ fn sign_hmac(alg: &'static digest::Algorithm, key: &[u8], signing_input: &str) -
|
||||||
let signing_key = hmac::SigningKey::new(alg, key);
|
let signing_key = hmac::SigningKey::new(alg, key);
|
||||||
let digest = hmac::sign(&signing_key, signing_input.as_bytes());
|
let digest = hmac::sign(&signing_key, signing_input.as_bytes());
|
||||||
|
|
||||||
Ok(
|
Ok(base64::encode_config::<hmac::Signature>(&digest, base64::URL_SAFE_NO_PAD))
|
||||||
base64::encode_config::<hmac::Signature>(&digest, base64::URL_SAFE_NO_PAD)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The actual RSA signing + encoding
|
/// The actual RSA signing + encoding
|
||||||
|
@ -70,18 +67,17 @@ fn sign_rsa(alg: Algorithm, key: &[u8], signing_input: &str) -> Result<String> {
|
||||||
|
|
||||||
let key_pair = Arc::new(
|
let key_pair = Arc::new(
|
||||||
signature::RSAKeyPair::from_der(untrusted::Input::from(key))
|
signature::RSAKeyPair::from_der(untrusted::Input::from(key))
|
||||||
.map_err(|_| ErrorKind::InvalidRsaKey)?
|
.map_err(|_| ErrorKind::InvalidRsaKey)?,
|
||||||
);
|
);
|
||||||
let mut signing_state = signature::RSASigningState::new(key_pair)
|
let mut signing_state =
|
||||||
.map_err(|_| ErrorKind::InvalidRsaKey)?;
|
signature::RSASigningState::new(key_pair).map_err(|_| ErrorKind::InvalidRsaKey)?;
|
||||||
let mut signature = vec![0; signing_state.key_pair().public_modulus_len()];
|
let mut signature = vec![0; signing_state.key_pair().public_modulus_len()];
|
||||||
let rng = rand::SystemRandom::new();
|
let rng = rand::SystemRandom::new();
|
||||||
signing_state.sign(ring_alg, &rng, signing_input.as_bytes(), &mut signature)
|
signing_state
|
||||||
|
.sign(ring_alg, &rng, signing_input.as_bytes(), &mut signature)
|
||||||
.map_err(|_| ErrorKind::InvalidRsaKey)?;
|
.map_err(|_| ErrorKind::InvalidRsaKey)?;
|
||||||
|
|
||||||
Ok(
|
Ok(base64::encode_config::<[u8]>(&signature, base64::URL_SAFE_NO_PAD))
|
||||||
base64::encode_config::<[u8]>(&signature, base64::URL_SAFE_NO_PAD)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take the payload of a JWT, sign it using the algorithm given and return
|
/// Take the payload of a JWT, sign it using the algorithm given and return
|
||||||
|
@ -94,12 +90,19 @@ pub fn sign(signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result<Str
|
||||||
Algorithm::HS384 => sign_hmac(&digest::SHA384, key, signing_input),
|
Algorithm::HS384 => sign_hmac(&digest::SHA384, key, signing_input),
|
||||||
Algorithm::HS512 => sign_hmac(&digest::SHA512, key, signing_input),
|
Algorithm::HS512 => sign_hmac(&digest::SHA512, key, signing_input),
|
||||||
|
|
||||||
Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 => sign_rsa(algorithm, key, signing_input),
|
Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 => {
|
||||||
|
sign_rsa(algorithm, key, signing_input)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See Ring RSA docs for more details
|
/// See Ring RSA docs for more details
|
||||||
fn verify_rsa(alg: &signature::RSAParameters, signature: &str, signing_input: &str, key: &[u8]) -> Result<bool> {
|
fn verify_rsa(
|
||||||
|
alg: &signature::RSAParameters,
|
||||||
|
signature: &str,
|
||||||
|
signing_input: &str,
|
||||||
|
key: &[u8],
|
||||||
|
) -> Result<bool> {
|
||||||
let signature_bytes = base64::decode_config(signature, base64::URL_SAFE_NO_PAD)?;
|
let signature_bytes = base64::decode_config(signature, base64::URL_SAFE_NO_PAD)?;
|
||||||
let public_key_der = untrusted::Input::from(key);
|
let public_key_der = untrusted::Input::from(key);
|
||||||
let message = untrusted::Input::from(signing_input.as_bytes());
|
let message = untrusted::Input::from(signing_input.as_bytes());
|
||||||
|
@ -118,15 +121,26 @@ fn verify_rsa(alg: &signature::RSAParameters, signature: &str, signing_input: &s
|
||||||
/// `signature` is the signature part of a jwt (text after the second '.')
|
/// `signature` is the signature part of a jwt (text after the second '.')
|
||||||
///
|
///
|
||||||
/// `signing_input` is base64(header) + "." + base64(claims)
|
/// `signing_input` is base64(header) + "." + base64(claims)
|
||||||
pub fn verify(signature: &str, signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result<bool> {
|
pub fn verify(
|
||||||
|
signature: &str,
|
||||||
|
signing_input: &str,
|
||||||
|
key: &[u8],
|
||||||
|
algorithm: Algorithm,
|
||||||
|
) -> Result<bool> {
|
||||||
match algorithm {
|
match algorithm {
|
||||||
Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => {
|
Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => {
|
||||||
// we just re-sign the data with the key and compare if they are equal
|
// we just re-sign the data with the key and compare if they are equal
|
||||||
let signed = sign(signing_input, key, algorithm)?;
|
let signed = sign(signing_input, key, algorithm)?;
|
||||||
Ok(verify_slices_are_equal(signature.as_ref(), signed.as_ref()).is_ok())
|
Ok(verify_slices_are_equal(signature.as_ref(), signed.as_ref()).is_ok())
|
||||||
},
|
}
|
||||||
Algorithm::RS256 => verify_rsa(&signature::RSA_PKCS1_2048_8192_SHA256, signature, signing_input, key),
|
Algorithm::RS256 => {
|
||||||
Algorithm::RS384 => verify_rsa(&signature::RSA_PKCS1_2048_8192_SHA384, signature, signing_input, key),
|
verify_rsa(&signature::RSA_PKCS1_2048_8192_SHA256, signature, signing_input, key)
|
||||||
Algorithm::RS512 => verify_rsa(&signature::RSA_PKCS1_2048_8192_SHA512, signature, signing_input, key),
|
}
|
||||||
|
Algorithm::RS384 => {
|
||||||
|
verify_rsa(&signature::RSA_PKCS1_2048_8192_SHA384, signature, signing_input, key)
|
||||||
|
}
|
||||||
|
Algorithm::RS512 => {
|
||||||
|
verify_rsa(&signature::RSA_PKCS1_2048_8192_SHA512, signature, signing_input, key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ pub(crate) fn new_error(kind: ErrorKind) -> Error {
|
||||||
Error(Box::new(kind))
|
Error(Box::new(kind))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// A type alias for `Result<T, jsonwebtoken::Error>`.
|
/// A type alias for `Result<T, jsonwebtoken::Error>`.
|
||||||
pub type Result<T> = result::Result<T, Error>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
|
@ -43,7 +42,6 @@ pub enum ErrorKind {
|
||||||
InvalidAlgorithmName,
|
InvalidAlgorithmName,
|
||||||
|
|
||||||
// validation error
|
// validation error
|
||||||
|
|
||||||
/// When a token’s `exp` claim indicates that it has expired
|
/// When a token’s `exp` claim indicates that it has expired
|
||||||
ExpiredSignature,
|
ExpiredSignature,
|
||||||
/// When a token’s `iss` claim does not match the expected issuer
|
/// When a token’s `iss` claim does not match the expected issuer
|
||||||
|
@ -60,7 +58,6 @@ pub enum ErrorKind {
|
||||||
InvalidAlgorithm,
|
InvalidAlgorithm,
|
||||||
|
|
||||||
// 3rd party errors
|
// 3rd party errors
|
||||||
|
|
||||||
/// An error happened when decoding some base64 text
|
/// An error happened when decoding some base64 text
|
||||||
Base64(base64::DecodeError),
|
Base64(base64::DecodeError),
|
||||||
/// An error happened while serializing/deserializing JSON
|
/// An error happened while serializing/deserializing JSON
|
||||||
|
@ -68,7 +65,6 @@ pub enum ErrorKind {
|
||||||
/// Some of the text was invalid UTF-8
|
/// Some of the text was invalid UTF-8
|
||||||
Utf8(::std::string::FromUtf8Error),
|
Utf8(::std::string::FromUtf8Error),
|
||||||
|
|
||||||
|
|
||||||
/// Hints that destructuring should not be exhaustive.
|
/// Hints that destructuring should not be exhaustive.
|
||||||
///
|
///
|
||||||
/// This enum may grow additional variants, so this makes sure clients
|
/// This enum may grow additional variants, so this makes sure clients
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crypto::Algorithm;
|
use crypto::Algorithm;
|
||||||
|
|
||||||
|
|
||||||
/// A basic JWT header, the alg defaults to HS256 and typ is automatically
|
/// A basic JWT header, the alg defaults to HS256 and typ is automatically
|
||||||
/// set to `JWT`. All the other fields are optional.
|
/// set to `JWT`. All the other fields are optional.
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
|
38
src/lib.rs
38
src/lib.rs
|
@ -6,37 +6,31 @@
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
extern crate serde_json;
|
|
||||||
extern crate serde;
|
|
||||||
extern crate base64;
|
extern crate base64;
|
||||||
extern crate ring;
|
|
||||||
extern crate untrusted;
|
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
|
extern crate ring;
|
||||||
|
extern crate serde;
|
||||||
|
extern crate serde_json;
|
||||||
|
extern crate untrusted;
|
||||||
|
|
||||||
|
mod crypto;
|
||||||
/// All the errors, generated using error-chain
|
/// All the errors, generated using error-chain
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
mod header;
|
mod header;
|
||||||
mod crypto;
|
|
||||||
mod serialization;
|
mod serialization;
|
||||||
mod validation;
|
mod validation;
|
||||||
|
|
||||||
|
pub use crypto::{sign, verify, Algorithm};
|
||||||
pub use header::Header;
|
pub use header::Header;
|
||||||
pub use crypto::{
|
|
||||||
Algorithm,
|
|
||||||
sign,
|
|
||||||
verify,
|
|
||||||
};
|
|
||||||
pub use validation::Validation;
|
|
||||||
pub use serialization::TokenData;
|
pub use serialization::TokenData;
|
||||||
|
pub use validation::Validation;
|
||||||
|
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::ser::Serialize;
|
use serde::ser::Serialize;
|
||||||
|
|
||||||
use errors::{Result, ErrorKind, new_error};
|
use errors::{new_error, ErrorKind, Result};
|
||||||
use serialization::{from_jwt_part, from_jwt_part_claims, to_jwt_part};
|
use serialization::{from_jwt_part, from_jwt_part_claims, to_jwt_part};
|
||||||
use validation::{validate};
|
use validation::validate;
|
||||||
|
|
||||||
|
|
||||||
/// Encode the header and claims given and sign the payload using the algorithm from the header and the key
|
/// Encode the header and claims given and sign the payload using the algorithm from the header and the key
|
||||||
///
|
///
|
||||||
|
@ -76,9 +70,9 @@ macro_rules! expect_two {
|
||||||
let mut i = $iter;
|
let mut i = $iter;
|
||||||
match (i.next(), i.next(), i.next()) {
|
match (i.next(), i.next(), i.next()) {
|
||||||
(Some(first), Some(second), None) => (first, second),
|
(Some(first), Some(second), None) => (first, second),
|
||||||
_ => return Err(new_error(ErrorKind::InvalidToken))
|
_ => return Err(new_error(ErrorKind::InvalidToken)),
|
||||||
}
|
}
|
||||||
}}
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decode a token into a struct containing 2 fields: `claims` and `header`.
|
/// Decode a token into a struct containing 2 fields: `claims` and `header`.
|
||||||
|
@ -100,7 +94,11 @@ macro_rules! expect_two {
|
||||||
/// // Claims is a struct that implements Deserialize
|
/// // Claims is a struct that implements Deserialize
|
||||||
/// let token_data = decode::<Claims>(&token, "secret", &Validation::new(Algorithm::HS256));
|
/// let token_data = decode::<Claims>(&token, "secret", &Validation::new(Algorithm::HS256));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn decode<T: DeserializeOwned>(token: &str, key: &[u8], validation: &Validation) -> Result<TokenData<T>> {
|
pub fn decode<T: DeserializeOwned>(
|
||||||
|
token: &str,
|
||||||
|
key: &[u8],
|
||||||
|
validation: &Validation,
|
||||||
|
) -> Result<TokenData<T>> {
|
||||||
let (signature, signing_input) = expect_two!(token.rsplitn(2, '.'));
|
let (signature, signing_input) = expect_two!(token.rsplitn(2, '.'));
|
||||||
let (claims, header) = expect_two!(signing_input.rsplitn(2, '.'));
|
let (claims, header) = expect_two!(signing_input.rsplitn(2, '.'));
|
||||||
let header: Header = from_jwt_part(header)?;
|
let header: Header = from_jwt_part(header)?;
|
||||||
|
@ -113,7 +111,7 @@ pub fn decode<T: DeserializeOwned>(token: &str, key: &[u8], validation: &Validat
|
||||||
return Err(new_error(ErrorKind::InvalidAlgorithm));
|
return Err(new_error(ErrorKind::InvalidAlgorithm));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (decoded_claims, claims_map): (T, _) = from_jwt_part_claims(claims)?;
|
let (decoded_claims, claims_map): (T, _) = from_jwt_part_claims(claims)?;
|
||||||
validate(&claims_map, validation)?;
|
validate(&claims_map, validation)?;
|
||||||
|
|
||||||
Ok(TokenData { header, claims: decoded_claims })
|
Ok(TokenData { header, claims: decoded_claims })
|
||||||
|
@ -143,7 +141,7 @@ pub fn dangerous_unsafe_decode<T: DeserializeOwned>(token: &str) -> Result<Token
|
||||||
let (claims, header) = expect_two!(signing_input.rsplitn(2, '.'));
|
let (claims, header) = expect_two!(signing_input.rsplitn(2, '.'));
|
||||||
let header: Header = from_jwt_part(header)?;
|
let header: Header = from_jwt_part(header)?;
|
||||||
|
|
||||||
let (decoded_claims, _): (T, _) = from_jwt_part_claims(claims)?;
|
let (decoded_claims, _): (T, _) = from_jwt_part_claims(claims)?;
|
||||||
|
|
||||||
Ok(TokenData { header, claims: decoded_claims })
|
Ok(TokenData { header, claims: decoded_claims })
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,19 @@
|
||||||
use base64;
|
use base64;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::ser::Serialize;
|
use serde::ser::Serialize;
|
||||||
use serde_json::{from_str, to_string, Value};
|
|
||||||
use serde_json::map::Map;
|
use serde_json::map::Map;
|
||||||
|
use serde_json::{from_str, to_string, Value};
|
||||||
|
|
||||||
use errors::{Result};
|
use errors::Result;
|
||||||
use header::Header;
|
use header::Header;
|
||||||
|
|
||||||
|
|
||||||
/// The return type of a successful call to decode
|
/// The return type of a successful call to decode
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TokenData<T> {
|
pub struct TokenData<T> {
|
||||||
/// The decoded JWT header
|
/// The decoded JWT header
|
||||||
pub header: Header,
|
pub header: Header,
|
||||||
/// The decoded JWT claims
|
/// The decoded JWT claims
|
||||||
pub claims: T
|
pub claims: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serializes to JSON and encodes to base64
|
/// Serializes to JSON and encodes to base64
|
||||||
|
@ -32,11 +31,13 @@ pub fn from_jwt_part<B: AsRef<str>, T: DeserializeOwned>(encoded: B) -> Result<T
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decodes from base64 and deserializes from JSON to a struct AND a hashmap
|
/// Decodes from base64 and deserializes from JSON to a struct AND a hashmap
|
||||||
pub fn from_jwt_part_claims<B: AsRef<str>, T: DeserializeOwned>(encoded: B) -> Result<(T, Map<String, Value>)> {
|
pub fn from_jwt_part_claims<B: AsRef<str>, T: DeserializeOwned>(
|
||||||
|
encoded: B,
|
||||||
|
) -> Result<(T, Map<String, Value>)> {
|
||||||
let decoded = base64::decode_config(encoded.as_ref(), base64::URL_SAFE_NO_PAD)?;
|
let decoded = base64::decode_config(encoded.as_ref(), base64::URL_SAFE_NO_PAD)?;
|
||||||
let s = String::from_utf8(decoded)?;
|
let s = String::from_utf8(decoded)?;
|
||||||
|
|
||||||
let claims: T = from_str(&s)?;
|
let claims: T = from_str(&s)?;
|
||||||
let map: Map<_,_> = from_str(&s)?;
|
let map: Map<_, _> = from_str(&s)?;
|
||||||
Ok((claims, map))
|
Ok((claims, map))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use serde::ser::Serialize;
|
use serde::ser::Serialize;
|
||||||
use serde_json::{Value, from_value, to_value};
|
|
||||||
use serde_json::map::Map;
|
use serde_json::map::Map;
|
||||||
|
use serde_json::{from_value, to_value, Value};
|
||||||
|
|
||||||
use errors::{Result, ErrorKind, new_error};
|
|
||||||
use crypto::Algorithm;
|
use crypto::Algorithm;
|
||||||
|
use errors::{new_error, ErrorKind, Result};
|
||||||
|
|
||||||
/// Contains the various validations that are applied after decoding a token.
|
/// Contains the various validations that are applied after decoding a token.
|
||||||
///
|
///
|
||||||
|
@ -107,8 +106,6 @@ impl Default for Validation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub fn validate(claims: &Map<String, Value>, options: &Validation) -> Result<()> {
|
pub fn validate(claims: &Map<String, Value>, options: &Validation) -> Result<()> {
|
||||||
let now = Utc::now().timestamp();
|
let now = Utc::now().timestamp();
|
||||||
|
|
||||||
|
@ -175,12 +172,11 @@ pub fn validate(claims: &Map<String, Value>, options: &Validation) -> Result<()>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use serde_json::{to_value};
|
|
||||||
use serde_json::map::Map;
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
use serde_json::map::Map;
|
||||||
|
use serde_json::to_value;
|
||||||
|
|
||||||
use super::{validate, Validation};
|
use super::{validate, Validation};
|
||||||
|
|
||||||
|
@ -190,7 +186,8 @@ mod tests {
|
||||||
fn iat_in_past_ok() {
|
fn iat_in_past_ok() {
|
||||||
let mut claims = Map::new();
|
let mut claims = Map::new();
|
||||||
claims.insert("iat".to_string(), to_value(Utc::now().timestamp() - 10000).unwrap());
|
claims.insert("iat".to_string(), to_value(Utc::now().timestamp() - 10000).unwrap());
|
||||||
let validation = Validation { validate_exp: false, validate_iat: true, ..Validation::default() };
|
let validation =
|
||||||
|
Validation { validate_exp: false, validate_iat: true, ..Validation::default() };
|
||||||
let res = validate(&claims, &validation);
|
let res = validate(&claims, &validation);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
}
|
}
|
||||||
|
@ -199,7 +196,8 @@ mod tests {
|
||||||
fn iat_in_future_fails() {
|
fn iat_in_future_fails() {
|
||||||
let mut claims = Map::new();
|
let mut claims = Map::new();
|
||||||
claims.insert("iat".to_string(), to_value(Utc::now().timestamp() + 100000).unwrap());
|
claims.insert("iat".to_string(), to_value(Utc::now().timestamp() + 100000).unwrap());
|
||||||
let validation = Validation { validate_exp: false, validate_iat: true, ..Validation::default() };
|
let validation =
|
||||||
|
Validation { validate_exp: false, validate_iat: true, ..Validation::default() };
|
||||||
let res = validate(&claims, &validation);
|
let res = validate(&claims, &validation);
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
|
|
||||||
|
@ -248,10 +246,7 @@ mod tests {
|
||||||
fn exp_in_past_but_in_leeway_ok() {
|
fn exp_in_past_but_in_leeway_ok() {
|
||||||
let mut claims = Map::new();
|
let mut claims = Map::new();
|
||||||
claims.insert("exp".to_string(), to_value(Utc::now().timestamp() - 500).unwrap());
|
claims.insert("exp".to_string(), to_value(Utc::now().timestamp() - 500).unwrap());
|
||||||
let validation = Validation {
|
let validation = Validation { leeway: 1000 * 60, ..Default::default() };
|
||||||
leeway: 1000 * 60,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let res = validate(&claims, &validation);
|
let res = validate(&claims, &validation);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
}
|
}
|
||||||
|
@ -272,7 +267,8 @@ mod tests {
|
||||||
fn nbf_in_past_ok() {
|
fn nbf_in_past_ok() {
|
||||||
let mut claims = Map::new();
|
let mut claims = Map::new();
|
||||||
claims.insert("nbf".to_string(), to_value(Utc::now().timestamp() - 10000).unwrap());
|
claims.insert("nbf".to_string(), to_value(Utc::now().timestamp() - 10000).unwrap());
|
||||||
let validation = Validation { validate_exp: false, validate_nbf: true, ..Validation::default() };
|
let validation =
|
||||||
|
Validation { validate_exp: false, validate_nbf: true, ..Validation::default() };
|
||||||
let res = validate(&claims, &validation);
|
let res = validate(&claims, &validation);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
}
|
}
|
||||||
|
@ -281,7 +277,8 @@ mod tests {
|
||||||
fn nbf_in_future_fails() {
|
fn nbf_in_future_fails() {
|
||||||
let mut claims = Map::new();
|
let mut claims = Map::new();
|
||||||
claims.insert("nbf".to_string(), to_value(Utc::now().timestamp() + 100000).unwrap());
|
claims.insert("nbf".to_string(), to_value(Utc::now().timestamp() + 100000).unwrap());
|
||||||
let validation = Validation { validate_exp: false, validate_nbf: true, ..Validation::default() };
|
let validation =
|
||||||
|
Validation { validate_exp: false, validate_nbf: true, ..Validation::default() };
|
||||||
let res = validate(&claims, &validation);
|
let res = validate(&claims, &validation);
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
|
|
||||||
|
@ -405,10 +402,7 @@ mod tests {
|
||||||
fn aud_string_ok() {
|
fn aud_string_ok() {
|
||||||
let mut claims = Map::new();
|
let mut claims = Map::new();
|
||||||
claims.insert("aud".to_string(), to_value("Everyone").unwrap());
|
claims.insert("aud".to_string(), to_value("Everyone").unwrap());
|
||||||
let mut validation = Validation {
|
let mut validation = Validation { validate_exp: false, ..Validation::default() };
|
||||||
validate_exp: false,
|
|
||||||
..Validation::default()
|
|
||||||
};
|
|
||||||
validation.set_audience(&"Everyone");
|
validation.set_audience(&"Everyone");
|
||||||
let res = validate(&claims, &validation);
|
let res = validate(&claims, &validation);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
|
@ -418,10 +412,7 @@ mod tests {
|
||||||
fn aud_array_of_string_ok() {
|
fn aud_array_of_string_ok() {
|
||||||
let mut claims = Map::new();
|
let mut claims = Map::new();
|
||||||
claims.insert("aud".to_string(), to_value(["UserA", "UserB"]).unwrap());
|
claims.insert("aud".to_string(), to_value(["UserA", "UserB"]).unwrap());
|
||||||
let mut validation = Validation {
|
let mut validation = Validation { validate_exp: false, ..Validation::default() };
|
||||||
validate_exp: false,
|
|
||||||
..Validation::default()
|
|
||||||
};
|
|
||||||
validation.set_audience(&["UserA", "UserB"]);
|
validation.set_audience(&["UserA", "UserB"]);
|
||||||
let res = validate(&claims, &validation);
|
let res = validate(&claims, &validation);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
|
@ -431,10 +422,7 @@ mod tests {
|
||||||
fn aud_type_mismatch_fails() {
|
fn aud_type_mismatch_fails() {
|
||||||
let mut claims = Map::new();
|
let mut claims = Map::new();
|
||||||
claims.insert("aud".to_string(), to_value("Everyone").unwrap());
|
claims.insert("aud".to_string(), to_value("Everyone").unwrap());
|
||||||
let mut validation = Validation {
|
let mut validation = Validation { validate_exp: false, ..Validation::default() };
|
||||||
validate_exp: false,
|
|
||||||
..Validation::default()
|
|
||||||
};
|
|
||||||
validation.set_audience(&["UserA", "UserB"]);
|
validation.set_audience(&["UserA", "UserB"]);
|
||||||
let res = validate(&claims, &validation);
|
let res = validate(&claims, &validation);
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
|
@ -449,10 +437,7 @@ mod tests {
|
||||||
fn aud_correct_type_not_matching_fails() {
|
fn aud_correct_type_not_matching_fails() {
|
||||||
let mut claims = Map::new();
|
let mut claims = Map::new();
|
||||||
claims.insert("aud".to_string(), to_value("Everyone").unwrap());
|
claims.insert("aud".to_string(), to_value("Everyone").unwrap());
|
||||||
let mut validation = Validation {
|
let mut validation = Validation { validate_exp: false, ..Validation::default() };
|
||||||
validate_exp: false,
|
|
||||||
..Validation::default()
|
|
||||||
};
|
|
||||||
validation.set_audience(&"None");
|
validation.set_audience(&"None");
|
||||||
let res = validate(&claims, &validation);
|
let res = validate(&claims, &validation);
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
|
@ -466,10 +451,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn aud_missing_fails() {
|
fn aud_missing_fails() {
|
||||||
let claims = Map::new();
|
let claims = Map::new();
|
||||||
let mut validation = Validation {
|
let mut validation = Validation { validate_exp: false, ..Validation::default() };
|
||||||
validate_exp: false,
|
|
||||||
..Validation::default()
|
|
||||||
};
|
|
||||||
validation.set_audience(&"None");
|
validation.set_audience(&"None");
|
||||||
let res = validate(&claims, &validation);
|
let res = validate(&claims, &validation);
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
|
|
10
tests/lib.rs
10
tests/lib.rs
|
@ -3,8 +3,11 @@ extern crate jsonwebtoken;
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
|
|
||||||
use jsonwebtoken::{encode, decode, decode_header, dangerous_unsafe_decode, Algorithm, Header, sign, verify, Validation};
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
use jsonwebtoken::{
|
||||||
|
dangerous_unsafe_decode, decode, decode_header, encode, sign, verify, Algorithm, Header,
|
||||||
|
Validation,
|
||||||
|
};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
|
@ -75,7 +78,8 @@ fn decode_token_missing_parts() {
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "InvalidSignature")]
|
#[should_panic(expected = "InvalidSignature")]
|
||||||
fn decode_token_invalid_signature() {
|
fn decode_token_invalid_signature() {
|
||||||
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUifQ.wrong";
|
let token =
|
||||||
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUifQ.wrong";
|
||||||
let claims = decode::<Claims>(token, "secret".as_ref(), &Validation::default());
|
let claims = decode::<Claims>(token, "secret".as_ref(), &Validation::default());
|
||||||
claims.unwrap();
|
claims.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -141,7 +145,7 @@ fn does_validation_in_right_order() {
|
||||||
exp: Utc::now().timestamp() + 10000,
|
exp: Utc::now().timestamp() + 10000,
|
||||||
};
|
};
|
||||||
let token = encode(&Header::default(), &my_claims, "secret".as_ref()).unwrap();
|
let token = encode(&Header::default(), &my_claims, "secret".as_ref()).unwrap();
|
||||||
let v = Validation {
|
let v = Validation {
|
||||||
leeway: 5,
|
leeway: 5,
|
||||||
validate_exp: true,
|
validate_exp: true,
|
||||||
iss: Some("iss no check".to_string()),
|
iss: Some("iss no check".to_string()),
|
||||||
|
|
21
tests/rsa.rs
21
tests/rsa.rs
|
@ -3,8 +3,8 @@ extern crate jsonwebtoken;
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
|
|
||||||
use jsonwebtoken::{encode, decode, Algorithm, Header, sign, verify, Validation};
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
use jsonwebtoken::{decode, encode, sign, verify, Algorithm, Header, Validation};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
struct Claims {
|
struct Claims {
|
||||||
|
@ -15,12 +15,14 @@ struct Claims {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn round_trip_sign_verification() {
|
fn round_trip_sign_verification() {
|
||||||
let encrypted = sign("hello world", include_bytes!("private_rsa_key.der"), Algorithm::RS256).unwrap();
|
let encrypted =
|
||||||
let is_valid = verify(&encrypted, "hello world", include_bytes!("public_rsa_key.der"), Algorithm::RS256).unwrap();
|
sign("hello world", include_bytes!("private_rsa_key.der"), Algorithm::RS256).unwrap();
|
||||||
|
let is_valid =
|
||||||
|
verify(&encrypted, "hello world", include_bytes!("public_rsa_key.der"), Algorithm::RS256)
|
||||||
|
.unwrap();
|
||||||
assert!(is_valid);
|
assert!(is_valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn round_trip_claim() {
|
fn round_trip_claim() {
|
||||||
let my_claims = Claims {
|
let my_claims = Claims {
|
||||||
|
@ -28,8 +30,15 @@ fn round_trip_claim() {
|
||||||
company: "ACME".to_string(),
|
company: "ACME".to_string(),
|
||||||
exp: Utc::now().timestamp() + 10000,
|
exp: Utc::now().timestamp() + 10000,
|
||||||
};
|
};
|
||||||
let token = encode(&Header::new(Algorithm::RS256), &my_claims, include_bytes!("private_rsa_key.der")).unwrap();
|
let token =
|
||||||
let token_data = decode::<Claims>(&token, include_bytes!("public_rsa_key.der"), &Validation::new(Algorithm::RS256)).unwrap();
|
encode(&Header::new(Algorithm::RS256), &my_claims, include_bytes!("private_rsa_key.der"))
|
||||||
|
.unwrap();
|
||||||
|
let token_data = decode::<Claims>(
|
||||||
|
&token,
|
||||||
|
include_bytes!("public_rsa_key.der"),
|
||||||
|
&Validation::new(Algorithm::RS256),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(my_claims, token_data.claims);
|
assert_eq!(my_claims, token_data.claims);
|
||||||
assert!(token_data.header.kid.is_none());
|
assert!(token_data.header.kid.is_none());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue