Skip to content

Commit

Permalink
Merge branch 'master' into new-backends
Browse files Browse the repository at this point in the history
  • Loading branch information
Keats authored Sep 2, 2024
2 parents e061882 + c733c78 commit e5ba5a1
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
include:
- build: pinned
os: ubuntu-20.04
rust: 1.67.0
rust: 1.73.0
- build: stable
os: ubuntu-20.04
rust: stable
Expand Down
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "jsonwebtoken"
version = "9.2.0"
version = "9.3.0"
authors = ["Vincent Prouillet <hello@vincentprouillet.com>"]
license = "MIT"
readme = "README.md"
Expand All @@ -17,12 +17,12 @@ include = [
"README.md",
"CHANGELOG.md",
]
rust-version = "1.67.0"
rust-version = "1.73.0"

[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
base64 = "0.21.0"
base64 = "0.22"
# For PEM decoding
pem = { version = "3", optional = true }
simple_asn1 = { version = "0.6", optional = true }
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jsonwebtoken = "9"
serde = {version = "1.0", features = ["derive"] }
```

The minimum required Rust version (MSRV) is 1.67.
The minimum required Rust version (MSRV) is specified in the `rust-version` field in this project's [Cargo.toml](Cargo.toml).

## Algorithms
This library currently supports the following:
Expand Down Expand Up @@ -160,6 +160,8 @@ This library automatically validates the `exp` claim, and `nbf` is validated if
those require setting the expected values in the `Validation` struct. In the case of `aud`, if there is a value set in the token but
not in the `Validation`, the token will be rejected.

Validation is only made on present fields in the claims. It is possible to define the required claims, hence verifying that a JWT has a value for each of these claims before it is considered for validation. The required claims can be set in the `Validation` struct. The default option requires the `exp` claim to be present.

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 the `leeway` field.

Expand Down
1 change: 1 addition & 0 deletions examples/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ fn main() {
let mut validation = Validation::new(Algorithm::HS256);
validation.sub = Some("b@b.com".to_string());
validation.set_audience(&["me"]);
validation.set_required_spec_claims(&["exp", "sub", "aud"]);
let token_data = match decode::<Claims>(&token, &DecodingKey::from_secret(key), &validation) {
Ok(c) => c,
Err(err) => match *err.kind() {
Expand Down
50 changes: 49 additions & 1 deletion src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ use crate::errors::{new_error, ErrorKind, Result};
/// // or issuer
/// validation.set_issuer(&["Me"]); // a single string
/// validation.set_issuer(&["Me", "You"]); // array of strings
/// // Setting required claims
/// validation.set_required_spec_claims(&["exp", "iss", "aud"]);
/// ```
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Validation {
Expand All @@ -39,6 +41,13 @@ pub struct Validation {
///
/// Defaults to `60`.
pub leeway: u64,
/// Reject a token some time (in seconds) before the `exp` to prevent
/// expiration in transit over the network.
///
/// The value is the inverse of `leeway`, subtracting from the validation time.
///
/// Defaults to `0`.
pub reject_tokens_expiring_in_less_than: u64,
/// Whether to validate the `exp` field.
///
/// It will return an error if the time in the `exp` field is past.
Expand All @@ -49,29 +58,44 @@ pub struct Validation {
///
/// It will return an error if the current timestamp is before the time in the `nbf` field.
///
/// Validation only happens if `nbf` claim is present in the token.
/// Adding `nbf` to `required_spec_claims` will make it required.
///
/// Defaults to `false`.
pub validate_nbf: bool,
/// Whether to validate the `aud` field.
///
/// It will return an error if the `aud` field is not a member of the audience provided.
///
/// Validation only happens if `aud` claim is present in the token.
/// Adding `aud` to `required_spec_claims` will make it required.
///
/// Defaults to `true`. Very insecure to turn this off. Only do this if you know what you are doing.
pub validate_aud: bool,
/// Validation will check that the `aud` field is a member of the
/// audience provided and will error otherwise.
/// Use `set_audience` to set it
///
/// Validation only happens if `aud` claim is present in the token.
/// Adding `aud` to `required_spec_claims` will make it required.
///
/// Defaults to `None`.
pub aud: Option<HashSet<String>>,
/// If it contains a value, the validation will check that the `iss` field is a member of the
/// iss provided and will error otherwise.
/// Use `set_issuer` to set it
///
/// Validation only happens if `iss` claim is present in the token.
/// Adding `iss` to `required_spec_claims` will make it required.
///
/// Defaults to `None`.
pub iss: Option<HashSet<String>>,
/// If it contains a value, the validation will check that the `sub` field is the same as the
/// one provided and will error otherwise.
///
/// Validation only happens if `sub` claim is present in the token.
/// Adding `sub` to `required_spec_claims` will make it required.
///
/// Defaults to `None`.
pub sub: Option<String>,
/// The validation will check that the `alg` of the header is contained
Expand All @@ -94,6 +118,7 @@ impl Validation {
required_spec_claims: required_claims,
algorithms: vec![alg],
leeway: 60,
reject_tokens_expiring_in_less_than: 0,

validate_exp: true,
validate_nbf: false,
Expand Down Expand Up @@ -250,7 +275,8 @@ pub(crate) fn validate(claims: ClaimsForValidation, options: &Validation) -> Res
if options.validate_exp || options.validate_nbf {
let now = get_current_timestamp();

if matches!(claims.exp, TryParse::Parsed(exp) if options.validate_exp && exp < now - options.leeway)
if matches!(claims.exp, TryParse::Parsed(exp) if options.validate_exp
&& exp - options.reject_tokens_expiring_in_less_than < now - options.leeway )
{
return Err(new_error(ErrorKind::ExpiredSignature));
}
Expand Down Expand Up @@ -371,6 +397,17 @@ mod tests {
assert!(res.is_ok());
}

#[test]
#[wasm_bindgen_test]
fn exp_in_future_but_in_rejection_period_fails() {
let claims = json!({ "exp": get_current_timestamp() + 500 });
let mut validation = Validation::new(Algorithm::HS256);
validation.leeway = 0;
validation.reject_tokens_expiring_in_less_than = 501;
let res = validate(deserialize_claims(&claims), &validation);
assert!(res.is_err());
}

#[test]
#[wasm_bindgen_test]
fn exp_float_in_future_ok() {
Expand All @@ -379,6 +416,17 @@ mod tests {
assert!(res.is_ok());
}

#[test]
#[wasm_bindgen_test]
fn exp_float_in_future_but_in_rejection_period_fails() {
let claims = json!({ "exp": (get_current_timestamp() as f64) + 500.123 });
let mut validation = Validation::new(Algorithm::HS256);
validation.leeway = 0;
validation.reject_tokens_expiring_in_less_than = 501;
let res = validate(deserialize_claims(&claims), &validation);
assert!(res.is_err());
}

#[test]
#[wasm_bindgen_test]
fn exp_in_past_fails() {
Expand Down
2 changes: 1 addition & 1 deletion tests/rsa/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ openssl x509 -req -sha256 -days 358000 -in certificate_rsa_pkcs1.csr -signkey pr
### RSA PKCS8

```
openssl genpkey -algorithm RSA -out private_rsa_pkcs8.pem -pkeyopt rsa_keygen_bits:2048`
openssl genpkey -algorithm RSA -out private_rsa_pkcs8.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in private_rsa_pkcs8.pem -out public_rsa_pkcs.pem
openssl req -new -key private_rsa_pkcs8.key -out certificate_rsa_pkcs8.csr
openssl x509 -req -sha256 -days 358000 -in certificate_rsa_pkcs8.csr -signkey private_rsa_pkcs8.key -out certificate_rsa_pkcs8.crt
Expand Down

0 comments on commit e5ba5a1

Please sign in to comment.