commit
df9449caae
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -1,5 +1,17 @@
|
|||
# Changelog
|
||||
|
||||
## 3.0.0 (unreleased)
|
||||
|
||||
### Breaking change
|
||||
- Remove `validate_signature` from `Validation`, use `decode_header` instead if you don't know the alg used
|
||||
- Make `typ` optional in header, some providers apparently don't use it
|
||||
|
||||
### Others
|
||||
|
||||
- Update ring & error-chain
|
||||
- Fix documentation about `leeway` being in seconds and not milliseconds
|
||||
- Add `decode_header` to only decode the header: replaces the use case of `validate_signature`
|
||||
|
||||
## 2.0.3 (2017-07-18)
|
||||
|
||||
- Make `TokenData` public
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "jsonwebtoken"
|
||||
version = "2.0.3"
|
||||
authors = ["Vincent Prouillet <vincent@wearewizards.io>"]
|
||||
version = "3.0.0"
|
||||
authors = ["Vincent Prouillet <prouillet.vincent@gmail.com>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
description = "Create and parse JWT in a strongly typed way."
|
||||
|
@ -10,11 +10,11 @@ repository = "https://github.com/Keats/rust-jwt"
|
|||
keywords = ["jwt", "web", "api", "token", "json"]
|
||||
|
||||
[dependencies]
|
||||
error-chain = { version = "0.10", default-features = false }
|
||||
error-chain = { version = "0.11", default-features = false }
|
||||
serde_json = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde = "1.0"
|
||||
ring = { version = "0.11.0", features = ["rsa_signing", "dev_urandom_fallback"] }
|
||||
ring = { version = "0.12.0", features = ["rsa_signing", "dev_urandom_fallback"] }
|
||||
base64 = "0.6"
|
||||
untrusted = "0.5"
|
||||
chrono = "0.4"
|
||||
|
|
82
README.md
82
README.md
|
@ -8,36 +8,51 @@
|
|||
Add the following to Cargo.toml:
|
||||
|
||||
```toml
|
||||
jsonwebtoken = "2"
|
||||
serde_derive = "1.0"
|
||||
jsonwebtoken = "3"
|
||||
serde_derive = "1"
|
||||
```
|
||||
|
||||
## How to use
|
||||
There is a complete example in `examples/claims.rs` but here's a quick one.
|
||||
Complete examples are available in the examples directory: a basic one and one with a custom header.
|
||||
|
||||
In terms of imports:
|
||||
In terms of imports and structs:
|
||||
```rust
|
||||
extern crate jsonwebtoken as jwt;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use jwt::{encode, decode, Header, Algorithm, Validation};
|
||||
```
|
||||
|
||||
Look at the examples directory for 2 examples: a basic one and one with a custom
|
||||
header.
|
||||
/// Our claims struct, it needs to derive `Serialize` and/or `Deserialize`
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct Claims {
|
||||
sub: String,
|
||||
company: String
|
||||
}
|
||||
```
|
||||
|
||||
### Encoding
|
||||
```rust
|
||||
let token = encode(&Header::default(), &my_claims, "secret".as_ref()).unwrap();
|
||||
```
|
||||
In that example, `my_claims` is an instance of a Claims struct that derives `Serialize` and `Deserialize`.
|
||||
The default algorithm is HS256.
|
||||
Look at custom headers section to see how to change that.
|
||||
|
||||
```rust
|
||||
let token = encode(&Header::default(), &my_claims, "secret".as_ref())?;
|
||||
```
|
||||
|
||||
#### Custom headers & changing algorithm
|
||||
All the parameters from the RFC are supported but the default header only has `typ` and `alg` set.
|
||||
If you want to set the `kid` parameter or change the algorithm for example:
|
||||
|
||||
```rust
|
||||
let mut header = Header::default();
|
||||
header.kid = Some("blabla".to_owned());
|
||||
header.alg = Algorithm::HS512;
|
||||
let token = encode(&header, &my_claims, "secret".as_ref())?;
|
||||
```
|
||||
Look at `examples/custom_header.rs` for a full working example.
|
||||
|
||||
### Decoding
|
||||
```rust
|
||||
let token = decode::<Claims>(&token, "secret", &Validation::default()).unwrap();
|
||||
let token = decode::<Claims>(&token, "secret", &Validation::default())?;
|
||||
// token is a struct with 2 params: header and claims
|
||||
```
|
||||
`decode` can error for a variety of reasons:
|
||||
|
@ -46,19 +61,31 @@ let token = decode::<Claims>(&token, "secret", &Validation::default()).unwrap();
|
|||
- error while decoding base64 or the result of decoding base64 is not valid UTF-8
|
||||
- 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
|
||||
those require setting the expected value.
|
||||
You can add some leeway to the `iat`, `exp` and `nbf` validation by setting the `leeway` parameter as shown in the example below as well
|
||||
as select allowed algorithms.
|
||||
In some cases, for example if you don't know the algorithm used, you will want to only decode the header:
|
||||
|
||||
```rust
|
||||
let header = decode_header(&token)?;
|
||||
```
|
||||
|
||||
This does not perform any validation on the token.
|
||||
|
||||
#### Validation
|
||||
This library validates automatically the `iat`, `exp` and `nbf` claims if present. You can also validate the `sub`, `iss` and `aud` but
|
||||
those require setting the expected value in the `Validation` struct.
|
||||
|
||||
Since validating time fields is always a bit tricky due to clock skew,
|
||||
you can add some leeway to the `iat`, `exp` and `nbf` validation by setting a `leeway` parameter.
|
||||
|
||||
Last but not least, if you are not using HS256 for the algorithm, you will need to update the `algorithms` field of the `Validation` struct
|
||||
to the one you are using.
|
||||
|
||||
```rust
|
||||
use jsonwebtoken::{Validation, Algorithm};
|
||||
|
||||
// Default valuation
|
||||
let validation = Validation::default();
|
||||
// Adding some leeway (in ms) for iat, exp and nbf checks
|
||||
let mut validation = Validation {leeway: 1000 * 60, ..Default::default()};
|
||||
// Adding some leeway (in seconds) for iat, exp and nbf checks
|
||||
let mut validation = Validation {leeway: 60, ..Default::default()};
|
||||
// Checking issuer
|
||||
let mut validation = Validation {iss: Some("issuer".to_string()), ..Default::default()};
|
||||
// Setting audience
|
||||
|
@ -69,21 +96,6 @@ validation.set_audience(&["Me", "You"]); // array of strings
|
|||
let mut validation = Validation {algorithms: Some(vec![Algorithm::HS256]), ..Default::default()};
|
||||
```
|
||||
|
||||
It's also possible to disable verifying the signature of a token by setting the `validate_signature` to `false`. This should
|
||||
only be done if you know what you are doing.
|
||||
|
||||
### Custom headers
|
||||
All the parameters from the RFC are supported but the default header only has `typ` and `alg` set: all the other fields are optional.
|
||||
If you want to set the `kid` parameter for example:
|
||||
|
||||
```rust
|
||||
let mut header = Header::default();
|
||||
header.kid = Some("blabla".to_owned());
|
||||
header.alg = Algorithm::HS512;
|
||||
let token = encode(&header, &my_claims, "secret".as_ref()).unwrap();
|
||||
```
|
||||
Look at `examples/custom_header.rs` for a full working example.
|
||||
|
||||
## Algorithms
|
||||
This library currently supports the following:
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@ pub struct Header {
|
|||
/// The type of JWS: it can only be "JWT" here
|
||||
///
|
||||
/// Defined in [RFC7515#4.1.9](https://tools.ietf.org/html/rfc7515#section-4.1.9).
|
||||
typ: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub typ: Option<String>,
|
||||
/// The algorithm used
|
||||
///
|
||||
/// Defined in [RFC7515#4.1.1](https://tools.ietf.org/html/rfc7515#section-4.1.1).
|
||||
|
@ -44,7 +45,7 @@ impl Header {
|
|||
/// Returns a JWT header with the algorithm given
|
||||
pub fn new(algorithm: Algorithm) -> Header {
|
||||
Header {
|
||||
typ: "JWT".to_string(),
|
||||
typ: Some("JWT".to_string()),
|
||||
alg: algorithm,
|
||||
cty: None,
|
||||
jku: None,
|
||||
|
|
21
src/lib.rs
21
src/lib.rs
|
@ -22,7 +22,7 @@ mod crypto;
|
|||
mod serialization;
|
||||
mod validation;
|
||||
|
||||
pub use header::{Header};
|
||||
pub use header::Header;
|
||||
pub use crypto::{
|
||||
Algorithm,
|
||||
sign,
|
||||
|
@ -107,7 +107,7 @@ pub fn decode<T: DeserializeOwned>(token: &str, key: &[u8], validation: &Validat
|
|||
let (claims, header) = expect_two!(signing_input.rsplitn(2, '.'));
|
||||
let header: Header = from_jwt_part(header)?;
|
||||
|
||||
if validation.validate_signature && !verify(signature, signing_input, key, header.alg)? {
|
||||
if !verify(signature, signing_input, key, header.alg)? {
|
||||
return Err(ErrorKind::InvalidSignature.into());
|
||||
}
|
||||
|
||||
|
@ -123,3 +123,20 @@ pub fn decode<T: DeserializeOwned>(token: &str, key: &[u8], validation: &Validat
|
|||
|
||||
Ok(TokenData { header: header, claims: decoded_claims })
|
||||
}
|
||||
|
||||
/// Decode a token and return the Header. This is not doing any kind of validation: it is meant to be
|
||||
/// used when you don't know which `alg` the token is using and want to check
|
||||
///
|
||||
/// If the token is invalid, it will return an error.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use jsonwebtoken::decode_header;
|
||||
///
|
||||
/// let token = "a.jwt.token".to_string();
|
||||
/// let header = decode_header(&token);
|
||||
/// ```
|
||||
pub fn decode_header(token: &str) -> Result<Header> {
|
||||
let (_, signing_input) = expect_two!(token.rsplitn(2, '.'));
|
||||
let (_, header) = expect_two!(signing_input.rsplitn(2, '.'));
|
||||
from_jwt_part(header)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ use crypto::Algorithm;
|
|||
/// let validation = Validation::default();
|
||||
///
|
||||
/// // Changing one parameter
|
||||
/// let mut validation = Validation {leeway: 1000 * 60, ..Default::default()};
|
||||
/// let mut validation = Validation {leeway: 60, ..Default::default()};
|
||||
///
|
||||
/// // Setting audience
|
||||
/// let mut validation = Validation::default();
|
||||
|
@ -27,17 +27,11 @@ use crypto::Algorithm;
|
|||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Validation {
|
||||
/// Add some leeway (in ms) to the `exp`, `iat` and `nbf` validation to
|
||||
/// Add some leeway (in seconds) to the `exp`, `iat` and `nbf` validation to
|
||||
/// account for clock skew.
|
||||
///
|
||||
/// Defaults to `0`.
|
||||
pub leeway: i64,
|
||||
/// Whether to actually validate the signature of the token.
|
||||
///
|
||||
/// WARNING: only set that to false if you know what you are doing.
|
||||
///
|
||||
/// Defaults to `true`.
|
||||
pub validate_signature: bool,
|
||||
/// Whether to validate the `exp` field.
|
||||
///
|
||||
/// It will return an error if the time in the `exp` field is past.
|
||||
|
@ -93,8 +87,6 @@ impl Default for Validation {
|
|||
Validation {
|
||||
leeway: 0,
|
||||
|
||||
validate_signature: true,
|
||||
|
||||
validate_exp: true,
|
||||
validate_iat: true,
|
||||
validate_nbf: true,
|
||||
|
|
|
@ -2,7 +2,7 @@ extern crate jsonwebtoken;
|
|||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use jsonwebtoken::{encode, decode, Algorithm, Header, sign, verify, Validation};
|
||||
use jsonwebtoken::{encode, decode, decode_header, Algorithm, Header, sign, verify, Validation};
|
||||
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
|
@ -97,9 +97,10 @@ fn decode_token_with_shuffled_header_fields() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn decode_without_validating_signature() {
|
||||
fn decode_header_only() {
|
||||
let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjb21wYW55IjoiMTIzNDU2Nzg5MCIsInN1YiI6IkpvaG4gRG9lIn0.S";
|
||||
let claims = decode::<Claims>(token, "secret".as_ref(), &Validation {validate_signature: false, ..Validation::default()});
|
||||
assert!(claims.is_ok());
|
||||
let header = decode_header(token).unwrap();
|
||||
assert_eq!(header.alg, Algorithm::HS256);
|
||||
assert_eq!(header.typ, Some("JWT".to_string()));
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue