Skip to content

Commit 97a2e09

Browse files
daniel-chambershasura-bot
authored andcommitted
Allow the use of all appropriate crypto algorithms that match the JWK retrieved from the URL (#1613)
### What This PR changes the behaviour of JWT validation when the user has configured getting their JWK from a URL in the AuthConfig. Previously we used the algorithm specified on the JWK and only validated the JWT if it was signed with that algorithm. However, in HGE v2, we did not look at this field, and instead validated the JWT with the algorithm it was signed with (so long as that algorithm was compatible with the key). This is better behaviour, IMHO, because the algorithm field on the JWK is optional (and customers often don't set it), plus I suspect it is more useful to specify which algorithm to use when _signing_ a JWT with that JWK, rather than _validating_ an existing signature. In this PR, we widen the acceptable algorithms to validate a JWT with to be all algorithms compatible with the JWK. However, this behaviour only applies to JWKs retrieved from a URL. For inline JWTs, we currently provide an `algorithm` property in the AuthConfig that specifies a single algorithm to use. I think this should be widened into a list to allow for multiple algorithms (and JWKs from a URL should also be able to optionally specify a list of acceptable algorithms), but that can be left to another PR. ### How There is a new function `get_acceptable_algorithms_for_key` that returns all acceptable algorithms for the specific JWK key type. It is used to set the acceptable algorithms to use during validation. V3_GIT_ORIGIN_REV_ID: 3c37a45b37611593b41b31968726923f45336bde
1 parent 0f3d615 commit 97a2e09

File tree

2 files changed

+38
-9
lines changed

2 files changed

+38
-9
lines changed

v3/changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020

2121
### Changed
2222

23+
- When JWKs are configured to be read from a URL in AuthConfig, we now no longer
24+
require the JWT to specify the `alg` property. We will validate the signature
25+
in a JWT so long as the algorithm used is supported by the JWK retrieved from
26+
the URL.
27+
2328
## [v2025.02.07]
2429

2530
### Added

v3/crates/auth/hasura-authn-jwt/src/jwt.rs

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ async fn get_decoding_key_from_jwk_url(
465465
http_client: &reqwest::Client,
466466
jwk_url: Url,
467467
jwt_authorization_header: &str,
468-
) -> Result<(jwt::Algorithm, jwt::DecodingKey), Error> {
468+
) -> Result<(Vec<jwt::Algorithm>, jwt::DecodingKey), Error> {
469469
let tracer = tracing_util::global_tracer();
470470
tracer
471471
.in_span_async("fetch_jwk", "Fetch JWK", SpanVisibility::Internal, || {
@@ -494,11 +494,8 @@ async fn get_decoding_key_from_jwk_url(
494494
.ok_or(InternalError::NoMatchingJWKFound { kid })?;
495495
let decoding_key = jwt::DecodingKey::from_jwk(jwk)
496496
.map_err(InternalError::JWTDecodingKeyError)?;
497-
let algorithm = jwk
498-
.common
499-
.algorithm
500-
.ok_or(InternalError::AlgorithmNotFoundInJWK)?;
501-
Ok((algorithm, decoding_key))
497+
let acceptable_algorithms = get_acceptable_algorithms_for_key(jwk);
498+
Ok((acceptable_algorithms, decoding_key))
502499
} else {
503500
Err(InternalError::UnsuccessfulJWKFetch(jwk_response.status()))?
504501
}
@@ -507,6 +504,32 @@ async fn get_decoding_key_from_jwk_url(
507504
.await
508505
}
509506

507+
fn get_acceptable_algorithms_for_key(jwk: &jwt::jwk::Jwk) -> Vec<jwt::Algorithm> {
508+
match &jwk.algorithm {
509+
// Elliptic curve family algorithms
510+
jsonwebtoken::jwk::AlgorithmParameters::EllipticCurve(_) => {
511+
vec![jwt::Algorithm::ES256, jwt::Algorithm::ES384]
512+
}
513+
// RSA family algorithms
514+
jsonwebtoken::jwk::AlgorithmParameters::RSA(_) => vec![
515+
jwt::Algorithm::RS256,
516+
jwt::Algorithm::RS384,
517+
jwt::Algorithm::RS512,
518+
jwt::Algorithm::PS256,
519+
jwt::Algorithm::PS384,
520+
jwt::Algorithm::PS512,
521+
],
522+
// HMAC family algorithms
523+
jsonwebtoken::jwk::AlgorithmParameters::OctetKey(_) => vec![
524+
jwt::Algorithm::HS256,
525+
jwt::Algorithm::HS384,
526+
jwt::Algorithm::HS512,
527+
],
528+
// Edwards-curve algorithms
529+
jsonwebtoken::jwk::AlgorithmParameters::OctetKeyPair(_) => vec![jwt::Algorithm::EdDSA],
530+
}
531+
}
532+
510533
fn get_claims_mapping_entry_value<T: for<'de> serde::Deserialize<'de>>(
511534
claim_name: String,
512535
claims_mapping_entry: JWTClaimsMappingEntry<T>,
@@ -531,14 +554,15 @@ pub(crate) async fn decode_and_parse_hasura_claims(
531554
jwt_config: JWTConfig,
532555
jwt: String,
533556
) -> Result<HasuraClaims, Error> {
534-
let (alg, decoding_key) = match jwt_config.key {
535-
JWTKey::Fixed(conf) => (conf.algorithm, get_decoding_key(&conf)?),
557+
let (acceptable_algorithms, decoding_key) = match jwt_config.key {
558+
JWTKey::Fixed(conf) => (vec![conf.algorithm], get_decoding_key(&conf)?),
536559
JWTKey::JwkFromUrl(jwk_url) => {
537560
get_decoding_key_from_jwk_url(http_client, jwk_url, &jwt).await?
538561
}
539562
};
540563

541-
let mut validation = Validation::new(alg);
564+
let mut validation = Validation::default();
565+
validation.algorithms = acceptable_algorithms;
542566

543567
// Additional validations according to the `jwt_config`.
544568
if let Some(aud) = jwt_config.audience {

0 commit comments

Comments
 (0)