org.springframework.security.oauth2.common.exceptions.InvalidGrantException Java Examples

The following examples show how to use org.springframework.security.oauth2.common.exceptions.InvalidGrantException. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: PhonePasswordTokenGranter.java    From spring-cloud-shop with MIT License 8 votes vote down vote up
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {

    Map<String, String> parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters());
    String username = parameters.get("phone");
    String password = parameters.get("password");
    // Protect from downstream leaks of password
    parameters.remove("password");

    Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
    ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
    try {
        userAuth = authenticationManager.authenticate(userAuth);
    } catch (AccountStatusException | BadCredentialsException ase) {
        //covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
        throw new InvalidGrantException(ase.getMessage());
    } // If the username/password are wrong the spec says we should send 400/invalid grant

    if (userAuth == null || !userAuth.isAuthenticated()) {
        throw new InvalidGrantException("Could not authenticate user: " + username);
    }

    return new OAuth2Authentication(getRequestFactory().createOAuth2Request(client, tokenRequest), userAuth);
}
 
Example #2
Source File: MobileCodeTokenGranter.java    From springcloud-oauth2 with MIT License 6 votes vote down vote up
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
    Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
    String mobile = parameters.get("mobile");
    String code = parameters.get("code");
    Authentication userAuth = new MobileCodeAuthenticationToken(mobile,code);
    ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
    try {
        userAuth = authenticationManager.authenticate(userAuth);
    }
    catch (AccountStatusException ase) {
        //covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
        throw new InvalidGrantException(ase.getMessage());
    }
    catch (BadCredentialsException e) {
        // If the username/password are wrong the spec says we should send 400/invalid grant
        throw new InvalidGrantException(e.getMessage());
    }
    if (userAuth == null || !userAuth.isAuthenticated()) {
        throw new InvalidGrantException("Could not authenticate mobile: " + mobile);
    }

    OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
    return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
 
Example #3
Source File: GlobalExceptionHandler.java    From lion with Apache License 2.0 6 votes vote down vote up
/**
 * 声明要捕获的异常
 *
 * @param e 异常
 */
@ExceptionHandler(Exception.class)
public Result exceptionHandler(Exception e) {

    Result result;

    if (e instanceof LionException) {
        LionException lionException = (LionException) e;
        result = Result.failure(lionException.getCode(), lionException.getMessage());
    } else if (e instanceof InvalidTokenException) {
        result = Result.failure(ResponseCode.UNAUTHORIZED, "无效的 Access Token");
    } else if (e instanceof InvalidGrantException) {
        result = Result.failure(ResponseCode.UNAUTHORIZED, "无效的 Refresh Token");
    } else if (e instanceof AccessDeniedException) {
        result = Result.failure(ResponseCode.FORBIDDEN, "权限不足无法访问");
    } else {
        log.error("系统异常", e);
        result = Result.failure(e.getMessage());
    }

    return result;
}
 
Example #4
Source File: ChoerodonRedirectResolver.java    From oauth-server with Apache License 2.0 6 votes vote down vote up
@Override
public String resolveRedirect(String requestedRedirect, ClientDetails client) {

    Set<String> authorizedGrantTypes = client.getAuthorizedGrantTypes();
    if (authorizedGrantTypes.isEmpty()) {
        throw new InvalidGrantException("A client must have at least one authorized grant type.");
    }
    if (!containsRedirectGrantType(authorizedGrantTypes)) {
        throw new InvalidGrantException(
                "A redirect_uri can only be used by implicit or authorization_code grant types.");
    }

    Set<String> redirectUris = client.getRegisteredRedirectUri();

    if (redirectUris != null && !redirectUris.isEmpty()) {
        return obtainMatchingRedirect(redirectUris, requestedRedirect);
    } else if (StringUtils.hasText(requestedRedirect)) {
        return requestedRedirect;
    } else {
        throw new InvalidRequestException("A redirect_uri must be supplied.");
    }

}
 
Example #5
Source File: SmsTokenGranter.java    From cola with MIT License 6 votes vote down vote up
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
	Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
	String phoneNumber = parameters.get("phoneNumber");
	String credential = parameters.get("credential");
	String token = parameters.get("token");

	Authentication userAuth = new SmsAuthenticationToken(phoneNumber, credential, token);
	((AbstractAuthenticationToken) userAuth).setDetails(parameters);
	try {
		userAuth = authenticationManager.authenticate(userAuth);
	} catch (AccountStatusException | BadCredentialsException ase) {
		//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
		throw new InvalidGrantException(ase.getMessage());
	}
	if (userAuth == null || !userAuth.isAuthenticated()) {
		throw new InvalidGrantException("Could not authenticate user: " + phoneNumber);
	}

	OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
	return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
 
Example #6
Source File: AcTokenGranter.java    From cola with MIT License 6 votes vote down vote up
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
	Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
	String authorizationCode = parameters.get("authorizationCode");
	String provider = parameters.get("provider");

	Authentication userAuth = new AcAuthenticationToken(authorizationCode, provider);
	((AbstractAuthenticationToken) userAuth).setDetails(parameters);
	try {
		userAuth = authenticationManager.authenticate(userAuth);
	} catch (AccountStatusException | BadCredentialsException ase) {
		//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
		throw new InvalidGrantException(ase.getMessage());
	}
	if (userAuth == null || !userAuth.isAuthenticated()) {
		throw new InvalidGrantException("Could not authenticate user: " + authorizationCode);
	}

	OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
	return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
 
Example #7
Source File: OpenIdTokenGranter.java    From cola with MIT License 6 votes vote down vote up
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
	Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
	String openId = parameters.get("openid");
	String provider = parameters.get("provider");

	Authentication userAuth = new OpenIdAuthenticationToken(openId,provider);
	((AbstractAuthenticationToken) userAuth).setDetails(parameters);
	try {
		userAuth = authenticationManager.authenticate(userAuth);
	} catch (AccountStatusException | BadCredentialsException ase) {
		//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
		throw new InvalidGrantException(ase.getMessage());
	}
	if (userAuth == null || !userAuth.isAuthenticated()) {
		throw new InvalidGrantException("Could not authenticate user: " + openId);
	}

	OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
	return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
 
Example #8
Source File: CustomWebResponseExceptionTranslator.java    From codeway_service with GNU General Public License v3.0 5 votes vote down vote up
@Override
public ResponseEntity translate(Exception e) throws Exception {
       JsonData<Void> jsonData = JsonData.failed(StatusEnum.SYSTEM_ERROR);
       if (e instanceof InternalAuthenticationServiceException) {
           jsonData = JsonData.failed(StatusEnum.SYSTEM_ERROR);
       } else if (e instanceof InvalidGrantException) {
           jsonData = JsonData.failed(StatusEnum.LOGIN_ERROR);
       }
       return new ResponseEntity<>(jsonData, HttpStatus.OK);
   }
 
Example #9
Source File: CustomAuthenticationEntryPoint.java    From lion with Apache License 2.0 5 votes vote down vote up
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {

    log.error(authException.getMessage());

    response.setCharacterEncoding("UTF-8");
    response.setContentType("application/json;charset=UTF-8");

    Throwable cause = authException.getCause();
    if (cause instanceof InvalidTokenException) {
        response.getWriter().print(JsonUtil.jsonObj2Str(Result.failure(ResponseCode.UNAUTHORIZED, "无效的 Access Token")));
    } else if (cause instanceof InvalidGrantException) {
        response.getWriter().print(JsonUtil.jsonObj2Str(Result.failure(ResponseCode.UNAUTHORIZED, "无效的 Refresh Token")));
    } else if (cause instanceof AccessDeniedException) {
        response.getWriter().print(JsonUtil.jsonObj2Str(Result.failure(ResponseCode.FORBIDDEN, "权限不足无法访问")));
    } else {
        response.getWriter().print(JsonUtil.jsonObj2Str(Result.failure(ResponseCode.UNAUTHORIZED, "尚未认证无法访问")));
    }

    /*
    if (isAjaxRequest(request)) {
        response.sendError(HttpStatus.UNAUTHORIZED.value(), authException.getMessage());
    } else {
        response.sendRedirect("/login");
    }
    */

}
 
Example #10
Source File: CustomWebResponseExceptionTranslator.java    From codeway_service with GNU General Public License v3.0 5 votes vote down vote up
@Override
public ResponseEntity translate(Exception e) throws Exception {
       JsonData<Void> jsonData = JsonData.failed(StatusEnum.SYSTEM_ERROR);
       if (e instanceof InternalAuthenticationServiceException) {
           jsonData = JsonData.failed(StatusEnum.SYSTEM_ERROR);
       } else if (e instanceof InvalidGrantException) {
           jsonData = JsonData.failed(StatusEnum.LOGIN_ERROR);
       }
       return new ResponseEntity<>(jsonData, HttpStatus.OK);
   }
 
Example #11
Source File: MobileTokenGranter.java    From SpringCloud with Apache License 2.0 5 votes vote down vote up
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
    Map<String, String> parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters());
    String username = parameters.get("username");
    String password = parameters.get("password");
    // Protect from downstream leaks of password
    parameters.remove("password");

    Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
    MobileAuthenticationToken mobileAuthenticationToken = new MobileAuthenticationToken(userAuth);
    ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
    try {
        userAuth = this.authenticationManager.authenticate(mobileAuthenticationToken);
    } catch (AccountStatusException ase) {
        //covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
        throw new InvalidGrantException(ase.getMessage());
    } catch (BadCredentialsException e) {
        // If the username/password are wrong the spec says we should send 400/invalid grant
        throw new InvalidGrantException(e.getMessage());
    }
    if (userAuth == null || !userAuth.isAuthenticated()) {
        throw new InvalidGrantException("Could not authenticate user: " + username);
    }

    OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
    return new OAuth2Authentication(storedOAuth2Request, mobileAuthenticationToken);
}
 
Example #12
Source File: DefaultWebResponseExceptionTranslator.java    From spring-cloud-shop with MIT License 4 votes vote down vote up
private ResponseEntity handleOAuth2Exception(OAuth2Exception e) throws IOException {

        Response<String> result = new Response<>();
        result.setCode(ERROR_CODE_START);
        if (e instanceof InvalidClientException) {
            result.setMsg("用户名或这密码错误");
        } else if (e instanceof UnauthorizedClientException) {
            result.setMsg("未授权的ClientId");
        } else if (e instanceof InvalidGrantException) {
            result.setMsg("授权失败,用户名或者密码错误");
        } else if (e instanceof InvalidScopeException) {
            result.setMsg("授权客户端错误");
        } else if (e instanceof InvalidTokenException) {
            result.setMsg("授权token错误");
        } else if (e instanceof InvalidRequestException) {
            result.setMsg("授权请求错误");
        } else if (e instanceof RedirectMismatchException) {
            result.setMsg("redirect_uri未匹配");
        } else if (e instanceof UnsupportedGrantTypeException) {
            result.setMsg("不支持此授权类型");
        } else if (e instanceof UnsupportedResponseTypeException) {
            result.setMsg("不支持此类型的授权码");
        } else if (e instanceof UserDeniedAuthorizationException) {
            result.setMsg("您没有访问权限");
        } else {
            result.setCode(ERROR_CODE_START + 1);
            result.setMsg(e.getMessage());
        }

        int status = e.getHttpErrorCode();
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        if (status == HttpStatus.UNAUTHORIZED.value() || (e instanceof InsufficientScopeException)) {
            headers.set("WWW-Authenticate", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, e.getSummary()));
        }

        return new ResponseEntity<>(result, headers,
                HttpStatus.OK);

    }
 
Example #13
Source File: CustomAuthCodeTokenGranter.java    From OAuth-2.0-Cookbook with MIT License 4 votes vote down vote up
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {

    Map<String, String> parameters = tokenRequest.getRequestParameters();
    String authorizationCode = parameters.get("code");
    String redirectUri = parameters.get(OAuth2Utils.REDIRECT_URI);
    String codeVerifier = parameters.get("code_verifier");

    if (authorizationCode == null) {
        throw new InvalidRequestException("An authorization code must be supplied.");
    }

    OAuth2Authentication storedAuth = authorizationCodeServices.consumeAuthorizationCode(authorizationCode);
    if (storedAuth == null) {
        throw new InvalidGrantException("Invalid authorization code: " + authorizationCode);
    }

    OAuth2Request pendingOAuth2Request = storedAuth.getOAuth2Request();




    // Validates code verifier
    Map<String, String> pendingOauth2RequestParams = pendingOAuth2Request.getRequestParameters();
    String codeChallenge = pendingOauth2RequestParams.get("code_challenge");
    String codeChallengeMethod = pendingOauth2RequestParams.get("code_challenge_method");

    if (codeVerifier == null && codeChallenge != null) {
        // client is using PKCE but did not send the codeVerifier
        throw new InvalidRequestException(
                "Invalid authorization code for current token request.");
    }

    if (codeVerifier != null && codeChallenge != null) {
        String hashed = codeVerifier;
        if ("S256".equals(codeChallengeMethod)) {
            hashed = DigestUtils.sha256Hex(codeVerifier);
        }

        if (!hashed.equalsIgnoreCase(codeChallenge)) {
            throw new InvalidRequestException(
                    "Invalid authorization code for current token request.");
        }
    }



    // https://jira.springsource.org/browse/SECOAUTH-333
    // This might be null, if the authorization was done without the redirect_uri parameter
    String redirectUriApprovalParameter = pendingOAuth2Request.getRequestParameters().get(
            OAuth2Utils.REDIRECT_URI);

    if ((redirectUri != null || redirectUriApprovalParameter != null)
            && !pendingOAuth2Request.getRedirectUri().equals(redirectUri)) {
        throw new RedirectMismatchException("Redirect URI mismatch.");
    }

    String pendingClientId = pendingOAuth2Request.getClientId();
    String clientId = tokenRequest.getClientId();
    if (clientId != null && !clientId.equals(pendingClientId)) {
        // just a sanity check.
        throw new InvalidClientException("Client ID mismatch");
    }

    // Secret is not required in the authorization request, so it won't be available
    // in the pendingAuthorizationRequest. We do want to check that a secret is provided
    // in the token request, but that happens elsewhere.

    Map<String, String> combinedParameters = new HashMap<String, String>(pendingOAuth2Request
            .getRequestParameters());
    // Combine the parameters adding the new ones last so they override if there are any clashes
    combinedParameters.putAll(parameters);

    // Make a new stored request with the combined parameters
    OAuth2Request finalStoredOAuth2Request = pendingOAuth2Request.createOAuth2Request(combinedParameters);

    Authentication userAuth = storedAuth.getUserAuthentication();

    return new OAuth2Authentication(finalStoredOAuth2Request, userAuth);

}
 
Example #14
Source File: CustomResourceOwnerPasswordTokenGranter.java    From spring-auth-example with MIT License 4 votes vote down vote up
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client,
    TokenRequest tokenRequest) {

  Map<String, String> parameters = new LinkedHashMap<String, String>(
      tokenRequest.getRequestParameters());
  String username = parameters.get("username");
  String password = parameters.get("password");
  String clientId = client.getClientId();
  // Protect from downstream leaks of password
  parameters.remove("password");

  Authentication userAuth;
  if ("foo_app".equalsIgnoreCase(clientId)) {
    userAuth = new FooUsernamePasswordAuthenticationToken(username,
        password);
  } else if ("bar_app".equalsIgnoreCase(clientId)) {
    userAuth = new BarUsernamePasswordAuthenticationToken(username,
        password);
  } else {
    throw new InvalidGrantException("Unknown client: " + clientId);
  }

  ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
  try {
    userAuth = authenticationManager.authenticate(userAuth);
  } catch (AccountStatusException ase) {
    //covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
    throw new InvalidGrantException(ase.getMessage());
  } catch (BadCredentialsException e) {
    // If the username/password are wrong the spec says we should send 400/invalid grant
    throw new InvalidGrantException(e.getMessage());
  }
  if (userAuth == null || !userAuth.isAuthenticated()) {
    throw new InvalidGrantException(
        "Could not authenticate user: " + username);
  }

  OAuth2Request storedOAuth2Request = getRequestFactory()
      .createOAuth2Request(client, tokenRequest);
  return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
 
Example #15
Source File: CustomAuthorizationTokenServices.java    From Auth-service with MIT License 4 votes vote down vote up
@Override
@Transactional(noRollbackFor = {InvalidTokenException.class, InvalidGrantException.class})
public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
        throws AuthenticationException {

    if (!supportRefreshToken) {
        throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
    }

    OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue);
    if (refreshToken == null) {
        throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
    }

    OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(refreshToken);
    if (this.authenticationManager != null && !authentication.isClientOnly()) {
        Authentication user = new PreAuthenticatedAuthenticationToken(authentication.getUserAuthentication(), "", authentication.getAuthorities());

        // Overrides this section, because he followed std's oAuth2 refresh token mechanism to skip the scope of use check.changes
        //user = authenticationManager.authenticate(user);
        Object details = authentication.getDetails();
        authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user);
        authentication.setDetails(details);
    }
    String clientId = authentication.getOAuth2Request().getClientId();
    if (clientId == null || !clientId.equals(tokenRequest.getClientId())) {
        throw new InvalidGrantException("Wrong client for this refresh token: " + refreshTokenValue);
    }

    // clear out any access tokens already associated with the refresh
    // token.
    tokenStore.removeAccessTokenUsingRefreshToken(refreshToken);

    if (isExpired(refreshToken)) {
        tokenStore.removeRefreshToken(refreshToken);
        throw new InvalidTokenException("Invalid refresh token (expired): " + refreshToken);
    }

    authentication = createRefreshedAuthentication(authentication, tokenRequest);

    if (!reuseRefreshToken) {
        tokenStore.removeRefreshToken(refreshToken);
        refreshToken = createRefreshToken(authentication);
    }

    OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
    tokenStore.storeAccessToken(accessToken, authentication);
    if (!reuseRefreshToken) {
        tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication);
    }
    return accessToken;
}
 
Example #16
Source File: CustomAuthorizationTokenServices.java    From microservice-integration with MIT License 4 votes vote down vote up
@Override
@Transactional(noRollbackFor = {InvalidTokenException.class, InvalidGrantException.class})
public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
        throws AuthenticationException {

    if (!supportRefreshToken) {
        throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
    }

    OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue);
    if (refreshToken == null) {
        throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
    }

    OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(refreshToken);
    if (this.authenticationManager != null && !authentication.isClientOnly()) {
        Authentication user = new PreAuthenticatedAuthenticationToken(authentication.getUserAuthentication(), "", authentication.getAuthorities());

        // Overrides this section, because he followed std's oAuth2 refresh token mechanism to skip the scope of use check.changes
        //user = authenticationManager.authenticate(user);
        Object details = authentication.getDetails();
        authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user);
        authentication.setDetails(details);
    }
    String clientId = authentication.getOAuth2Request().getClientId();
    if (clientId == null || !clientId.equals(tokenRequest.getClientId())) {
        throw new InvalidGrantException("Wrong client for this refresh token: " + refreshTokenValue);
    }

    // clear out any access tokens already associated with the refresh
    // token.
    tokenStore.removeAccessTokenUsingRefreshToken(refreshToken);

    if (isExpired(refreshToken)) {
        tokenStore.removeRefreshToken(refreshToken);
        throw new InvalidTokenException("Invalid refresh token (expired): " + refreshToken);
    }

    authentication = createRefreshedAuthentication(authentication, tokenRequest);

    if (!reuseRefreshToken) {
        tokenStore.removeRefreshToken(refreshToken);
        refreshToken = createRefreshToken(authentication);
    }

    OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
    tokenStore.storeAccessToken(accessToken, authentication);
    if (!reuseRefreshToken) {
        tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication);
    }
    return accessToken;
}
 
Example #17
Source File: LessStrictRedirectUriAuthorizationCodeTokenGranter.java    From osiam with MIT License 4 votes vote down vote up
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {

    Map<String, String> parameters = tokenRequest.getRequestParameters();
    String authorizationCode = parameters.get("code");
    String redirectUri = parameters.get(OAuth2Utils.REDIRECT_URI);

    if (authorizationCode == null) {
        throw new InvalidRequestException("An authorization code must be supplied.");
    }

    OAuth2Authentication storedAuth = authorizationCodeServices.consumeAuthorizationCode(authorizationCode);
    if (storedAuth == null) {
        throw new InvalidGrantException("Invalid authorization code: " + authorizationCode);
    }

    OAuth2Request pendingOAuth2Request = storedAuth.getOAuth2Request();
    // https://jira.springsource.org/browse/SECOAUTH-333
    // This might be null, if the authorization was done without the redirect_uri parameter
    String redirectUriApprovalParameter = pendingOAuth2Request.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);

    if (redirectUriApprovalParameter != null && redirectUri == null
            || redirectUriApprovalParameter != null
            && !pendingOAuth2Request.getRedirectUri().startsWith(redirectUri)) {
        throw new RedirectMismatchException("Redirect URI mismatch.");
    }

    String pendingClientId = pendingOAuth2Request.getClientId();
    String clientId = tokenRequest.getClientId();
    if (clientId != null && !clientId.equals(pendingClientId)) {
        // just a sanity check.
        throw new InvalidClientException("Client ID mismatch");
    }

    // Secret is not required in the authorization request, so it won't be available
    // in the pendingAuthorizationRequest. We do want to check that a secret is provided
    // in the token request, but that happens elsewhere.

    Map<String, String> combinedParameters = new HashMap<>(pendingOAuth2Request.getRequestParameters());
    // Combine the parameters adding the new ones last so they override if there are any clashes
    combinedParameters.putAll(parameters);

    // Make a new stored request with the combined parameters
    OAuth2Request finalStoredOAuth2Request = pendingOAuth2Request.createOAuth2Request(combinedParameters);

    Authentication userAuth = storedAuth.getUserAuthentication();

    return new OAuth2Authentication(finalStoredOAuth2Request, userAuth);
}