Skip to content

Commit

Permalink
feat: manually verify iat claim
Browse files Browse the repository at this point in the history
The jsonwebtoken crate doesn't do this for us, unfortunately.

Co-authored-by: Tommy Trøen <[email protected]>
  • Loading branch information
tronghn and tommytroen committed Nov 11, 2024
1 parent dba29cb commit 11f5443
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 16 deletions.
17 changes: 6 additions & 11 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,8 @@ mod tests {
test_introspect_token_missing_kid(&address, &identity_provider_address).await;
test_introspect_token_missing_key_in_jwks(&address, &identity_provider_address).await;
test_introspect_token_is_expired(&address, &identity_provider_address).await;

// FIXME: these tests don't fail as expected; validation crate not working?
//test_introspect_token_is_issued_in_the_future(&address, &identity_provider_address).await;
//test_introspect_token_has_not_before_in_the_future(&address, &identity_provider_address).await;
test_introspect_token_is_issued_in_the_future(&address, &identity_provider_address).await;
test_introspect_token_has_not_before_in_the_future(&address, &identity_provider_address).await;

// TODO: implement these tests:
// * /token
Expand All @@ -185,7 +183,7 @@ mod tests {
// * [x] token is issued by unrecognized issuer
// * [x] token does not have kid (key id) in header
// * [x] token is signed with a key that is not in the jwks
// * [ ] invalid or expired timestamps in nbf, iat, exp
// * [x] invalid or expired timestamps in nbf, iat, exp
// * [ ] invalid or missing aud (for certain providers)
// * [ ] refreshing jwks fails
// * [ ] fetch / network error / reqwest error
Expand Down Expand Up @@ -291,7 +289,6 @@ mod tests {
.await;
}

// FIXME: this test doesn't fail as expected; validation crate not working?
async fn test_introspect_token_is_issued_in_the_future(
address: &str,
identity_provider_address: &str,
Expand All @@ -312,13 +309,12 @@ mod tests {
test_well_formed_json_request(
&format!("http://{}/api/v1/introspect", address),
IntrospectRequest { token },
IntrospectResponse::new_invalid("token is issued in the future"),
IntrospectResponse::new_invalid("invalid token: ImmatureSignature"),
StatusCode::OK,
)
.await;
}

// FIXME: this test doesn't fail as expected; validation crate not working?
async fn test_introspect_token_has_not_before_in_the_future(
address: &str,
identity_provider_address: &str,
Expand All @@ -329,18 +325,17 @@ mod tests {
"iss".into(),
format!("http://{}/maskinporten", identity_provider_address).into(),
),
("nbf".into(), (epoch_now_secs() - 120).into()),
("nbf".into(), (epoch_now_secs() + 120).into()),
("iat".into(), (epoch_now_secs()).into()),
("exp".into(), (epoch_now_secs() + 300).into()),
]),
"maskinporten",
);
println!("{}", token);

test_well_formed_json_request(
&format!("http://{}/api/v1/introspect", address),
IntrospectRequest { token },
IntrospectResponse::new_invalid("token has not before in the future"),
IntrospectResponse::new_invalid("invalid token: ImmatureSignature"),
StatusCode::OK,
)
.await;
Expand Down
21 changes: 16 additions & 5 deletions src/jwks.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use jsonwebkey as jwk;
use jsonwebtoken as jwt;
use jsonwebtoken::Validation;
use jsonwebtoken::{errors, Validation};
use serde::Deserialize;
use serde_json::Value;
use std::collections::HashMap;
use log::error;
use thiserror::Error;
use crate::claims::epoch_now_secs;

#[derive(Clone, Debug)]
pub struct Jwks {
Expand Down Expand Up @@ -112,12 +114,21 @@ impl Jwks {
Some(key) => key,
};

Ok(jwt::decode::<HashMap<String, Value>>(
let claims = jwt::decode::<HashMap<String, Value>>(
token,
&signing_key.key.to_decoding_key(),
&self.validation,
)
.map_err(Error::InvalidToken)?
.claims)
).map_err(Error::InvalidToken)?.claims;

// validate the `iat` claim manually as the jsonwebtoken crate does not do this
let iat = claims.get("iat").and_then(|v| v.as_u64()).ok_or_else(|| {
Error::InvalidToken(errors::ErrorKind::MissingRequiredClaim("iat".to_string()).into())
})?;

if iat > epoch_now_secs() + self.validation.leeway {
return Err(Error::InvalidToken(errors::ErrorKind::ImmatureSignature.into()));
}

Ok(claims)
}
}

0 comments on commit 11f5443

Please sign in to comment.