Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bugfix/audience_validation #3778

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,51 +20,72 @@ class MultipleIssuersJwtDecoder implements JwtDecoder {
private final Map<String, NimbusJwtDecoder> decoderMap;

MultipleIssuersJwtDecoder(List<ResourceServerProperties> properties) {
this.decoderMap = properties.stream().collect(Collectors.toMap(
ResourceServerProperties::getIssuerUri,
props -> {
NimbusJwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(props.getIssuerUri());
jwtDecoder.setJwtValidator(oAuth2TokenValidator(props));
return jwtDecoder;
}
));
decoderMap = properties
.stream()
.collect(Collectors.toMap(
ResourceServerProperties::getIssuerUri,
MultipleIssuersJwtDecoder::getValidatingDecoder
));
}

private static NimbusJwtDecoder getValidatingDecoder(ResourceServerProperties properties) {
NimbusJwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(properties.getIssuerUri());
jwtDecoder.setJwtValidator(oAuth2TokenValidator(properties));
return jwtDecoder;
}

@Override
public Jwt decode(String token) throws JwtException {
try {

var issuer = JWTParser
.parse(token)
.getJWTClaimsSet()
.getIssuer();
return decoderMap
.get(issuer)
.decode(token);
if (issuer == null || !decoderMap.containsKey(issuer)) {
throw new JwtException("JWT decoder for issuer %s not found".formatted(issuer));
}
var decoder = decoderMap.get(issuer);
log.info("Decoding token with issuer {} using decoder {}", issuer, decoder.getClass().getSimpleName());
return decoder.decode(token);

} catch (ParseException e) {
log.error("Feil ved parsing av token", e);
log.error("Error in offset {} when parsing token", e.getErrorOffset(), e);
throw new JwtException("Feil ved parsing av token", e);
} catch (JwtValidationException e) {
log.error("Feil ved validering av token", e);
log.error("Error(s) validating token: {}", e.getErrors(), e);
throw e;
} catch (Exception e) {
log.error("Ukjent feil", e);
throw e;
log.error("Unexpected failure", e);
throw new JwtException("Unexpected failure", e);
}
}

private OAuth2TokenValidator<Jwt> oAuth2TokenValidator(ResourceServerProperties properties) {
OAuth2TokenValidator<Jwt> issuerValidator = JwtValidators.createDefaultWithIssuer(properties.getIssuerUri());
OAuth2TokenValidator<Jwt> audienceValidator = token ->
token.getAudience().stream().anyMatch(audience -> properties.getAcceptedAudience().contains(audience)) ?
OAuth2TokenValidatorResult.success() :
OAuth2TokenValidatorResult.failure(createError(
String.format("Fant ikke påkrevd audience %s i tokenet.", properties.getAcceptedAudience())
));
return new DelegatingOAuth2TokenValidator<>(issuerValidator, audienceValidator);
private static OAuth2TokenValidator<Jwt> oAuth2TokenValidator(ResourceServerProperties properties) {
return new DelegatingOAuth2TokenValidator<>(
issuerValidator(properties.getIssuerUri()),
audienceValidator(properties.getAcceptedAudience())
);
}

private static OAuth2TokenValidator<Jwt> issuerValidator(String issuerUri) {
return JwtValidators.createDefaultWithIssuer(issuerUri); // Note that this creates and adds a default audience validator.
}

private OAuth2Error createError(String msg) {
return new OAuth2Error("invalid_token", msg, null);
private static OAuth2TokenValidator<Jwt> audienceValidator(List<String> acceptedAudience) {
return token -> {
var audience = token.getAudience();
var audienceIsAccepted = audience
.stream()
.anyMatch(acceptedAudience::contains);
if (audienceIsAccepted) {
log.info("Token audience {} is accepted by {}", audience, acceptedAudience);
return OAuth2TokenValidatorResult.success();
}
var message = "Token audience %s is not accepted by %s".formatted(audience, acceptedAudience);
log.warn(message);
return OAuth2TokenValidatorResult.failure(new OAuth2Error("invalid_token", message, null));
};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,54 +16,76 @@

@Slf4j
class MultipleIssuersJwtDecoder implements JwtDecoder {

private final Map<String, NimbusJwtDecoder> decoderMap;

MultipleIssuersJwtDecoder(List<ResourceServerProperties> properties) {
this.decoderMap = properties.stream().collect(Collectors.toMap(
ResourceServerProperties::getIssuerUri,
props -> {
NimbusJwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(props.getIssuerUri());
jwtDecoder.setJwtValidator(oAuth2TokenValidator(props));
return jwtDecoder;
}
));
decoderMap = properties
.stream()
.collect(Collectors.toMap(
ResourceServerProperties::getIssuerUri,
MultipleIssuersJwtDecoder::getValidatingDecoder
));
}

private static NimbusJwtDecoder getValidatingDecoder(ResourceServerProperties properties) {
NimbusJwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(properties.getIssuerUri());
jwtDecoder.setJwtValidator(oAuth2TokenValidator(properties));
return jwtDecoder;
}

@Override
public Jwt decode(String token) throws JwtException {
try {

var issuer = JWTParser
.parse(token)
.getJWTClaimsSet()
.getIssuer();
return decoderMap
.get(issuer)
.decode(token);
if (issuer == null || !decoderMap.containsKey(issuer)) {
throw new JwtException("JWT decoder for issuer %s not found".formatted(issuer));
}
var decoder = decoderMap.get(issuer);
log.info("Decoding token with issuer {} using decoder {}", issuer, decoder.getClass().getSimpleName());
return decoder.decode(token);

} catch (ParseException e) {
log.error("Feil ved parsing av token", e);
log.error("Error in offset {} when parsing token", e.getErrorOffset(), e);
throw new JwtException("Feil ved parsing av token", e);
} catch (JwtValidationException e) {
log.error("Feil ved validering av token", e);
log.error("Error(s) validating token: {}", e.getErrors(), e);
throw e;
} catch (Exception e) {
log.error("Ukjent feil", e);
throw e;
log.error("Unexpected failure", e);
throw new JwtException("Unexpected failure", e);
}
}

private OAuth2TokenValidator<Jwt> oAuth2TokenValidator(ResourceServerProperties properties) {
OAuth2TokenValidator<Jwt> issuerValidator = JwtValidators.createDefaultWithIssuer(properties.getIssuerUri());
OAuth2TokenValidator<Jwt> audienceValidator = token ->
token.getAudience().stream().anyMatch(audience -> properties.getAcceptedAudience().contains(audience)) ?
OAuth2TokenValidatorResult.success() :
OAuth2TokenValidatorResult.failure(createError(
String.format("Fant ikke påkrevd audience %s i tokenet.", properties.getAcceptedAudience())
));
return new DelegatingOAuth2TokenValidator<>(issuerValidator, audienceValidator);
private static OAuth2TokenValidator<Jwt> oAuth2TokenValidator(ResourceServerProperties properties) {
return new DelegatingOAuth2TokenValidator<>(
issuerValidator(properties.getIssuerUri()),
audienceValidator(properties.getAcceptedAudience())
);
}

private static OAuth2TokenValidator<Jwt> issuerValidator(String issuerUri) {
return JwtValidators.createDefaultWithIssuer(issuerUri); // Note that this creates and adds a default audience validator.
}

private OAuth2Error createError(String msg) {
return new OAuth2Error("invalid_token", msg, null);
private static OAuth2TokenValidator<Jwt> audienceValidator(List<String> acceptedAudience) {
return token -> {
var audience = token.getAudience();
var audienceIsAccepted = audience
.stream()
.anyMatch(acceptedAudience::contains);
if (audienceIsAccepted) {
log.info("Token audience {} is accepted by {}", audience, acceptedAudience);
return OAuth2TokenValidatorResult.success();
}
var message = "Token audience %s is not accepted by %s".formatted(audience, acceptedAudience);
log.warn(message);
return OAuth2TokenValidatorResult.failure(new OAuth2Error("invalid_token", message, null));
};
}

}