312 String authorizationCode = request.getParameter(
"code");
314 HttpSession session = request.getSession();
318 String requestState = request.getParameter(
"state");
319 if (storedState == null || !storedState.equals(requestState)) {
320 throw new AuthenticationServiceException(
"State parameter mismatch on return. Expected " + storedState +
" got " + requestState);
330 MultiValueMap<String, String> form =
new LinkedMultiValueMap<>();
331 form.add(
"grant_type",
"authorization_code");
332 form.add(
"code", authorizationCode);
336 if (codeVerifier != null) {
337 form.add(
"code_verifier", codeVerifier);
341 if (redirectUri != null) {
342 form.add(
"redirect_uri", redirectUri);
349 .useSystemProperties()
350 .setDefaultRequestConfig(RequestConfig.custom()
356 HttpComponentsClientHttpRequestFactory factory =
new HttpComponentsClientHttpRequestFactory(
httpClient);
358 RestTemplate restTemplate;
360 if (SECRET_BASIC.equals(clientConfig.getTokenEndpointAuthMethod())){
362 restTemplate =
new RestTemplate(factory) {
365 protected ClientHttpRequest createRequest(URI url, HttpMethod method)
throws IOException {
366 ClientHttpRequest httpRequest = super.createRequest(url, method);
367 httpRequest.getHeaders().add(
"Authorization",
368 String.format(
"Basic %s", Base64.encode(String.format(
"%s:%s",
369 UriUtils.encodePathSegment(clientConfig.getClientId(),
"UTF-8"),
370 UriUtils.encodePathSegment(clientConfig.getClientSecret(),
"UTF-8")))));
377 restTemplate =
new RestTemplate(factory);
379 if (SECRET_JWT.equals(clientConfig.getTokenEndpointAuthMethod()) || PRIVATE_KEY.equals(clientConfig.getTokenEndpointAuthMethod())) {
383 JWTSigningAndValidationService signer = null;
384 JWSAlgorithm alg = clientConfig.getTokenEndpointAuthSigningAlg();
386 if (SECRET_JWT.equals(clientConfig.getTokenEndpointAuthMethod()) &&
387 (JWSAlgorithm.HS256.equals(alg)
388 || JWSAlgorithm.HS384.equals(alg)
389 || JWSAlgorithm.HS512.equals(alg))) {
394 }
else if (PRIVATE_KEY.equals(clientConfig.getTokenEndpointAuthMethod())) {
404 if (signer == null) {
405 throw new AuthenticationServiceException(
"Couldn't find required signer service for use with private key auth.");
408 JWTClaimsSet.Builder claimsSet =
new JWTClaimsSet.Builder();
410 claimsSet.issuer(clientConfig.getClientId());
411 claimsSet.subject(clientConfig.getClientId());
412 claimsSet.audience(Lists.newArrayList(serverConfig.getTokenEndpointUri()));
413 claimsSet.jwtID(UUID.randomUUID().toString());
416 Date exp =
new Date(System.currentTimeMillis() + (60 * 1000));
417 claimsSet.expirationTime(exp);
419 Date now =
new Date(System.currentTimeMillis());
420 claimsSet.issueTime(now);
421 claimsSet.notBeforeTime(now);
423 JWSHeader header =
new JWSHeader(alg, null, null, null, null, null, null, null, null, null,
424 signer.getDefaultSignerKeyId(),
426 SignedJWT jwt =
new SignedJWT(header, claimsSet.build());
428 signer.signJwt(jwt, alg);
430 form.add(
"client_assertion_type",
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer");
431 form.add(
"client_assertion", jwt.serialize());
434 form.add(
"client_id", clientConfig.getClientId());
435 form.add(
"client_secret", clientConfig.getClientSecret());
440 logger.debug(
"tokenEndpointURI = " + serverConfig.getTokenEndpointUri());
441 logger.debug(
"form = " + form);
443 String jsonString = null;
446 jsonString = restTemplate.postForObject(serverConfig.getTokenEndpointUri(), form, String.class);
447 }
catch (RestClientException e) {
451 logger.error(
"Token Endpoint error response: " + e.getMessage());
453 throw new AuthenticationServiceException(
"Unable to obtain Access Token: " + e.getMessage());
456 logger.debug(
"from TokenEndpoint jsonString = " + jsonString);
458 JsonElement jsonRoot =
new JsonParser().parse(jsonString);
459 if (!jsonRoot.isJsonObject()) {
460 throw new AuthenticationServiceException(
"Token Endpoint did not return a JSON object: " + jsonRoot);
463 JsonObject tokenResponse = jsonRoot.getAsJsonObject();
465 if (tokenResponse.get(
"error") != null) {
469 String error = tokenResponse.get(
"error").getAsString();
471 logger.error(
"Token Endpoint returned: " + error);
473 throw new AuthenticationServiceException(
"Unable to obtain Access Token. Token Endpoint returned: " + error);
481 String accessTokenValue = null;
482 String idTokenValue = null;
483 String refreshTokenValue = null;
485 if (tokenResponse.has(
"access_token")) {
486 accessTokenValue = tokenResponse.get(
"access_token").getAsString();
488 throw new AuthenticationServiceException(
"Token Endpoint did not return an access_token: " + jsonString);
491 if (tokenResponse.has(
"id_token")) {
492 idTokenValue = tokenResponse.get(
"id_token").getAsString();
494 logger.error(
"Token Endpoint did not return an id_token");
495 throw new AuthenticationServiceException(
"Token Endpoint did not return an id_token");
498 if (tokenResponse.has(
"refresh_token")) {
499 refreshTokenValue = tokenResponse.get(
"refresh_token").getAsString();
503 JWT idToken = JWTParser.parse(idTokenValue);
506 JWTClaimsSet idClaims = idToken.getJWTClaimsSet();
509 JWTSigningAndValidationService jwtValidator = null;
511 Algorithm tokenAlg = idToken.getHeader().getAlgorithm();
513 Algorithm clientAlg = clientConfig.getIdTokenSignedResponseAlg();
515 if (clientAlg != null) {
516 if (!clientAlg.equals(tokenAlg)) {
517 throw new AuthenticationServiceException(
"Token algorithm " + tokenAlg +
" does not match expected algorithm " + clientAlg);
521 if (idToken instanceof PlainJWT) {
523 if (clientAlg == null) {
524 throw new AuthenticationServiceException(
"Unsigned ID tokens can only be used if explicitly configured in client.");
527 if (tokenAlg != null && !tokenAlg.equals(Algorithm.NONE)) {
528 throw new AuthenticationServiceException(
"Unsigned token received, expected signature with " + tokenAlg);
530 }
else if (idToken instanceof SignedJWT) {
532 SignedJWT signedIdToken = (SignedJWT)idToken;
534 if (tokenAlg.equals(JWSAlgorithm.HS256)
535 || tokenAlg.equals(JWSAlgorithm.HS384)
536 || tokenAlg.equals(JWSAlgorithm.HS512)) {
545 if (jwtValidator != null) {
546 if(!jwtValidator.validateSignature(signedIdToken)) {
547 throw new AuthenticationServiceException(
"Signature validation failed");
550 logger.error(
"No validation service found. Skipping signature validation");
551 throw new AuthenticationServiceException(
"Unable to find an appropriate signature validator for ID Token.");
556 if (idClaims.getIssuer() == null) {
557 throw new AuthenticationServiceException(
"Id Token Issuer is null");
558 }
else if (!idClaims.getIssuer().equals(serverConfig.getIssuer())){
559 throw new AuthenticationServiceException(
"Issuers do not match, expected " + serverConfig.getIssuer() +
" got " + idClaims.getIssuer());
563 if (idClaims.getExpirationTime() == null) {
564 throw new AuthenticationServiceException(
"Id Token does not have required expiration claim");
568 if (now.after(idClaims.getExpirationTime())) {
569 throw new AuthenticationServiceException(
"Id Token is expired: " + idClaims.getExpirationTime());
574 if (idClaims.getNotBeforeTime() != null) {
576 if (now.before(idClaims.getNotBeforeTime())){
577 throw new AuthenticationServiceException(
"Id Token not valid untill: " + idClaims.getNotBeforeTime());
582 if (idClaims.getIssueTime() == null) {
583 throw new AuthenticationServiceException(
"Id Token does not have required issued-at claim");
587 if (now.before(idClaims.getIssueTime())) {
588 throw new AuthenticationServiceException(
"Id Token was issued in the future: " + idClaims.getIssueTime());
593 if (idClaims.getAudience() == null) {
594 throw new AuthenticationServiceException(
"Id token audience is null");
595 }
else if (!idClaims.getAudience().contains(clientConfig.getClientId())) {
596 throw new AuthenticationServiceException(
"Audience does not match, expected " + clientConfig.getClientId() +
" got " + idClaims.getAudience());
600 String nonce = idClaims.getStringClaim(
"nonce");
601 if (Strings.isNullOrEmpty(nonce)) {
603 logger.error(
"ID token did not contain a nonce claim.");
605 throw new AuthenticationServiceException(
"ID token did not contain a nonce claim.");
609 if (!nonce.equals(storedNonce)) {
610 logger.error(
"Possible replay attack detected! The comparison of the nonce in the returned " 611 +
"ID Token to the session " +
NONCE_SESSION_VARIABLE +
" failed. Expected " + storedNonce +
" got " + nonce +
".");
613 throw new AuthenticationServiceException(
614 "Possible replay attack detected! The comparison of the nonce in the returned " 615 +
"ID Token to the session " +
NONCE_SESSION_VARIABLE +
" failed. Expected " + storedNonce +
" got " + nonce +
".");
620 PendingOIDCAuthenticationToken token =
new PendingOIDCAuthenticationToken(idClaims.getSubject(), idClaims.getIssuer(),
622 idToken, accessTokenValue, refreshTokenValue);
624 Authentication authentication = this.getAuthenticationManager().authenticate(token);
626 return authentication;
627 }
catch (ParseException e) {
628 throw new AuthenticationServiceException(
"Couldn't parse idToken: ", e);
JWKSetCacheService validationServices
Definition: OIDCAuthenticationFilter.java:113
JWSAlgorithm getDefaultSigningAlgorithm()
Map< String, String > getTokenOptions(ServerConfiguration server, RegisteredClient client, HttpServletRequest request)
static String getStoredState(HttpSession session)
Definition: OIDCAuthenticationFilter.java:709
int timeSkewAllowance
Definition: OIDCAuthenticationFilter.java:109
JWTSigningAndValidationService authenticationSignerService
Definition: OIDCAuthenticationFilter.java:121
static final String REDIRECT_URI_SESION_VARIABLE
Definition: OIDCAuthenticationFilter.java:98
ClientConfigurationService clients
Definition: OIDCAuthenticationFilter.java:134
JWTSigningAndValidationService getSymmetricValidtor(ClientDetailsEntity client)
Definition: SymmetricKeyJWTValidatorCacheService.java:72
static final String ISSUER_SESSION_VARIABLE
Definition: OIDCAuthenticationFilter.java:102
ServerConfigurationService servers
Definition: OIDCAuthenticationFilter.java:132
HttpClient httpClient
Definition: OIDCAuthenticationFilter.java:124
RegisteredClient getClientConfiguration(ServerConfiguration issuer)
JWTSigningAndValidationService getValidator(String jwksUri)
Definition: JWKSetCacheService.java:85
static final String NONCE_SESSION_VARIABLE
Definition: OIDCAuthenticationFilter.java:101
static String getStoredSessionString(HttpSession session, String key)
Definition: OIDCAuthenticationFilter.java:662
static String getStoredCodeVerifier(HttpSession session)
Definition: OIDCAuthenticationFilter.java:729
static String getStoredNonce(HttpSession session)
Definition: OIDCAuthenticationFilter.java:688
SymmetricKeyJWTValidatorCacheService symmetricCacheService
Definition: OIDCAuthenticationFilter.java:117
ServerConfiguration getServerConfiguration(String issuer)
AuthRequestOptionsService authOptions
Definition: OIDCAuthenticationFilter.java:136
int httpSocketTimeout
Definition: OIDCAuthenticationFilter.java:144