use of com.forgerock.openbanking.common.error.exception.AccessTokenReWriteException in project openbanking-aspsp by OpenBankingToolkit.
the class AuthorisationApiController method getAuthorisation.
/**
* getAuthorisation - Implementation of the /authorize OIDC Connect endpoint.
* @param responseType required = true
* @param clientId required = true
* @param state required = false
* @param nonce required = false
* @param scopes required = false
* @param redirectUri required = false,
* @param requestParametersSerialised required = true)
* @param isHeadlessEnabled required = false, defaultValue = "false"
* @param username required = false, defaultValue = ""
* @param password required = false, defaultValue = ""
* @param ssoToken (required = false)
* @param body required = false
* @return A <code>ResponseEntity</code> containing the result of authorization request
* @throws OBErrorResponseException or OBErrorException when errors occur that prevent authorization
*/
@Override
public ResponseEntity getAuthorisation(String responseType, String clientId, String state, String nonce, String scopes, String redirectUri, String requestParametersSerialised, boolean isHeadlessEnabled, String username, String password, String ssoToken, MultiValueMap body, HttpServletRequest request) throws OBErrorResponseException, OBErrorException {
// Initialisation the response entity, it will be overwritten with am response.
ResponseEntity responseEntity = ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
try {
// FAPI compliant ('code id_token'): https://github.com/ForgeCloud/ob-deploy/issues/674
if (!discoveryConfig.getSupportedResponseTypes().contains(responseType)) {
log.error("The response types requested '" + responseType + "' don't match with the response types " + "supported '" + discoveryConfig.getSupportedResponseTypes() + "' by as-api");
throw new OBErrorResponseException(OBRIErrorType.REQUEST_RESPONSE_TYPE_MISMATCH.getHttpStatus(), OBRIErrorResponseCategory.REQUEST_INVALID, OBRIErrorType.REQUEST_RESPONSE_TYPE_MISMATCH.toOBError1(responseType, discoveryConfig.getSupportedResponseTypes().toString()));
}
SignedJWT requestParameterJwt = validateRequestParameter(responseType, clientId, state, nonce, scopes, redirectUri, requestParametersSerialised);
requestParametersSerialised = requestParameterJwt.serialize();
try {
state = getState(state, requestParameterJwt);
} catch (ParseException e) {
throw new OBErrorResponseException(OBRIErrorType.REQUEST_PARAMETER_JWT_INVALID.getHttpStatus(), OBRIErrorResponseCategory.REQUEST_INVALID, OBRIErrorType.REQUEST_PARAMETER_JWT_INVALID.toOBError1(e.getMessage()));
}
AMGateway amGateway = amGatewayService.getAmGateway(requestParametersSerialised);
if (isHeadlessAlwaysEnabled || isHeadlessEnabled) {
log.debug("getAuthorisation() performing headless authorisation");
responseEntity = headLessAuthorisationService.getAuthorisation(amGateway, responseType, clientId, state, nonce, scopes, redirectUri, requestParametersSerialised, username, password);
} else {
log.debug("getAuthorisation() delegating authorisation to AM");
HashMap<String, String> queryParameters = new HashMap<>();
queryParameters.put("request", requestParametersSerialised);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Cookie", cookieName + "=" + ssoToken);
responseEntity = amGateway.toAM(request, httpHeaders, queryParameters, new ParameterizedTypeReference<String>() {
}, body);
}
log.debug("getAuthorisation() responseEntity {}", responseEntity);
// re-write it to appear as a fragment
if (hasQueryParamIdToken(responseEntity)) {
responseEntity = convertQueryToFragment(responseEntity.getHeaders().getLocation(), responseEntity.getHeaders(), state);
return responseEntity;
}
// Rewriting the response as we need to re-sign the id token. We can assume the id_token will exist as a fragment
if (hasFragmentIdToken(responseEntity)) {
try {
responseEntity = this.jwtOverridingService.rewriteIdTokenFragmentInLocationHeader(responseEntity);
tokenUsageService.incrementTokenUsage(TokenUsage.ID_TOKEN);
} catch (AccessTokenReWriteException e) {
String supportUID = UUID.randomUUID().toString();
log.info("getAuthorisation() Failed to re-write the id_token", e);
throw new OBErrorResponseException(OBRIErrorType.AUTHORIZE_INVALID_ID_TOKEN.getHttpStatus(), OBRIErrorResponseCategory.ACCESS_TOKEN, OBRIErrorType.AUTHORIZE_INVALID_ID_TOKEN.toOBError1(supportUID));
}
} else {
log.debug("responseEntity {} is null or is not a redirection", responseEntity);
}
} catch (OBErrorResponseException | OBErrorException obException) {
log.error("Authorisation error '{}', building the redirect action", obException.getMessage());
if (redirectUri != null && state != null) {
RedirectionAction redirectionAction = buildRedirectionAction(obException, redirectUri, state);
return ResponseEntity.status(HttpStatus.FOUND).header("Location", redirectionAction.getRedirectUri()).build();
}
}
return responseEntity;
}
use of com.forgerock.openbanking.common.error.exception.AccessTokenReWriteException in project openbanking-aspsp by OpenBankingToolkit.
the class AccessTokenApiController method getAccessToken.
@Override
@PreAuthorize("hasAnyAuthority('ROLE_PISP', 'ROLE_AISP', 'ROLE_CBPII')")
public ResponseEntity getAccessToken(MultiValueMap<String, String> paramMap, String authorization, Principal principal, HttpServletRequest request) throws OBErrorResponseException, OBErrorException {
log.debug("getAccessToken(), paramMap {}", paramMap);
PairClientIDAuthMethod clientIDAuthMethod = matlsRequestVerificationService.verifyMATLSMatchesRequest(paramMap, authorization, principal);
AMGateway amGateway = this.amGateway;
// The token endpoint can also be used as audience, as per OIDC spec
if (clientIDAuthMethod.getAuthMethod() == PRIVATE_KEY_JWT) {
String clientAssertion = paramMap.getFirst(CLIENT_ASSERTION);
if (clientAssertion == null || clientAssertion.isBlank()) {
log.debug("getAccessToken() clientAssertion was null or blank");
throw new OBErrorResponseException(OBRIErrorType.ACCESS_TOKEN_INVALID.getHttpStatus(), OBRIErrorResponseCategory.ACCESS_TOKEN, OBRIErrorType.ACCESS_TOKEN_INVALID.toOBError1("No client_assertion in body"));
}
amGateway = amGatewayService.getAmGateway(clientAssertion);
}
// can throw a UnsupportedOIDCGrantTypeException
GrantType grantType = GrantType.fromType(paramMap.getFirst(OIDCConstants.OIDCClaim.GRANT_TYPE));
ResponseEntity<AccessTokenResponse> responseEntity = getAccessToken(paramMap, request, clientIDAuthMethod, amGateway, grantType);
try {
responseEntity = jwtOverridingService.rewriteAccessTokenResponseIdToken(responseEntity);
} catch (AccessTokenReWriteException e) {
log.debug("Failed to rewrite the access token response's id_token.", e);
String supportUID = UUID.randomUUID().toString();
throw new OBErrorResponseException(OBRIErrorType.ACCESS_TOKEN_INVALID_ID_TOKEN.getHttpStatus(), OBRIErrorResponseCategory.ACCESS_TOKEN, OBRIErrorType.ACCESS_TOKEN_INVALID_ID_TOKEN.toOBError1(supportUID));
}
return responseEntity;
}
use of com.forgerock.openbanking.common.error.exception.AccessTokenReWriteException in project openbanking-aspsp by OpenBankingToolkit.
the class RCSConsentDecisionApiController method decision.
/**
* @param consentDecisionSerialised
* @param ssoToken
* @return
* @throws OBErrorException
*/
@Override
public ResponseEntity decision(@RequestBody String consentDecisionSerialised, @CookieValue(value = "${am.cookie.name}") String ssoToken) throws OBErrorException {
log.debug("decisionAccountSharing() consentDecisionSerialised is {}", consentDecisionSerialised);
// Send a Consent response JWT to the initial request, which is define in the code
if (consentDecisionSerialised == null || consentDecisionSerialised.isEmpty()) {
log.debug("Consent decision is empty");
return rcsErrorService.error(OBRIErrorType.RCS_CONSENT_DECISION_EMPTY);
}
ConsentDecision consentDecision;
try {
consentDecision = objectMapper.readValue(consentDecisionSerialised, ConsentDecision.class);
} catch (IOException e) {
log.error("Remote consent decisions invalid", e);
throw new OBErrorException(OBRIErrorType.RCS_CONSENT_DECISIONS_FORMAT, e.getMessage());
}
String consentRequestJwt = consentDecision.getConsentJwt();
if (consentRequestJwt == null || consentRequestJwt.isEmpty() || consentRequestJwt.isBlank()) {
log.error("Remote consent decisions invalid - consentRequestJwt is null ");
throw new OBErrorException(OBRIErrorType.RCS_CONSENT_DECISIONS_FORMAT, "consentRequestJwt was null or " + "empty");
}
try {
try {
log.debug("Received an accept consent request");
// TODO check token but ignore if it's expired
// cryptoApiClient.validateJws(consentDecision.getConsentJwt(),
// amOpenBankingConfiguration.getIssuerID(), amOpenBankingConfiguration.jwksUri);
SignedJWT consentContextJwt = (SignedJWT) JWTParser.parse(consentRequestJwt);
boolean decision = RCSConstants.Decision.ALLOW.equals(consentDecision.getDecision());
log.debug("The decision is '{}'", decision);
// here is a good time to actually save that the consent has been approved by our resource owner
Claims claims = JwsClaimsUtils.getClaims(consentContextJwt);
String intentId = claims.getIdTokenClaims().get(OpenBankingConstants.IdTokenClaim.INTENT_ID).getValue();
String csrf = consentContextJwt.getJWTClaimsSet().getStringClaim(RCSConstants.Claims.CSRF);
String clientId = consentContextJwt.getJWTClaimsSet().getStringClaim(RCSConstants.Claims.CLIENT_ID);
List<String> scopes = new ArrayList<>(consentContextJwt.getJWTClaimsSet().getJSONObjectClaim(RCSConstants.Claims.SCOPES).keySet());
String redirectUri = consentContextJwt.getJWTClaimsSet().getStringClaim(OIDCConstants.OIDCClaim.CONSENT_APPROVAL_REDIRECT_URI);
ConsentDecisionDelegate consentDecisionDelegate = intentTypeService.getConsentDecision(intentId);
if (consentDecisionDelegate == null) {
log.error("No Consent Decision Delegate available from the intent type Service.");
throw new OBErrorException(OBRIErrorType.RCS_CONSENT_REQUEST_INVALID, "Invalid intent ID? '" + intentId + "'");
}
// Verify consent is own by the right TPP
String tppIdBehindConsent = consentDecisionDelegate.getTppIdBehindConsent();
Optional<Tpp> isTpp = tppStoreService.findById(tppIdBehindConsent);
if (isTpp.isEmpty()) {
log.error("The TPP '{}' that created this intent id '{}' doesn't exist anymore.", tppIdBehindConsent, intentId);
return rcsErrorService.error(OBRIErrorType.RCS_CONSENT_REQUEST_NOT_FOUND_TPP, tppIdBehindConsent, intentId, clientId);
}
if (!clientId.equals(isTpp.get().getClientId())) {
log.error("The TPP '{}' created the account request '{}' but it's TPP '{}' that is trying to get" + " consent for it.", tppIdBehindConsent, intentId, clientId);
throw new OBErrorException(OBRIErrorType.RCS_CONSENT_REQUEST_INVALID_CONSENT, tppIdBehindConsent, intentId, clientId);
}
// Verify consent decision is send by the same user
Map<String, String> profile = userProfileService.getProfile(ssoToken, amOpenBankingConfiguration.endpointUserProfile, amOpenBankingConfiguration.cookieName);
String username = profile.get(amOpenBankingConfiguration.userProfileId);
String userIdBehindConsent = consentDecisionDelegate.getUserIDBehindConsent();
if (!username.equals(userIdBehindConsent)) {
log.error("The consent was associated with user '{}' but now, its user '{}' that " + "send the consent decision.", userIdBehindConsent, username);
throw new OBErrorException(OBRIErrorType.RCS_CONSENT_DECISION_INVALID_USER, userIdBehindConsent, username);
}
// Call the right decision delegate, cased on the intent type
consentDecisionDelegate.consentDecision(consentDecisionSerialised, decision);
log.debug("Redirect the resource owner to the original oauth2/openid request but this time, with the " + "consent response jwt '{}'.", consentContextJwt.toString());
String consentJwt = rcsService.generateRCSConsentResponse(rcsConfiguration, amOpenBankingConfiguration, csrf, decision, scopes, clientId);
ResponseEntity responseEntity = rcsService.sendRCSResponseToAM(ssoToken, RedirectionAction.builder().redirectUri(redirectUri).consentJwt(consentJwt).requestMethod(HttpMethod.POST).build());
log.debug("Response received from AM: {}", responseEntity);
if (responseEntity.getStatusCode() != HttpStatus.FOUND) {
log.error("When sending the consent response {} to AM, it failed to returned a 302. response '{}' ", consentJwt, responseEntity);
throw new OBErrorException(OBRIErrorType.RCS_CONSENT_RESPONSE_FAILURE);
} else if (locationContainsError(responseEntity.getHeaders().getLocation())) {
log.error("When sending the consent response {} to AM, it failed. response '{}' ", consentJwt, responseEntity);
return rcsErrorService.invalidConsentError(responseEntity.getHeaders().getLocation());
}
ResponseEntity rewrittenResponseEntity = null;
try {
rewrittenResponseEntity = jwtOverridingService.rewriteIdTokenFragmentInLocationHeader(responseEntity);
} catch (AccessTokenReWriteException e) {
log.info("decisionAccountSharing() Failed to re-write id_token", e);
throw new OBErrorException(OBRIErrorType.RCS_CONSENT_RESPONSE_FAILURE);
}
String location = rewrittenResponseEntity.getHeaders().getFirst("Location");
log.debug("The redirection to the consent page should be in the location '{}'", location);
return ResponseEntity.ok(RedirectionAction.builder().redirectUri(location).build());
} catch (JOSEException e) {
log.error("Could not generate consent context JWT", e);
throw new OBErrorException(OBRIErrorType.RCS_CONSENT_RESPONSE_FAILURE);
} catch (ParseException e) {
log.error("Could not parse the JWT", e);
throw new OBErrorException(OBRIErrorType.RCS_CONSENT_REQUEST_FORMAT);
} catch (IOException e) {
log.error("Remote consent decisions invalid", e);
throw new OBErrorException(OBRIErrorType.RCS_CONSENT_DECISIONS_FORMAT, e.getMessage());
}
} catch (OBErrorException e) {
return rcsErrorService.invalidConsentError(consentRequestJwt, e);
}
}
use of com.forgerock.openbanking.common.error.exception.AccessTokenReWriteException in project openbanking-aspsp by OpenBankingToolkit.
the class JwtOverridingServiceTest method shouldFailWhenStatusNotOK_rewriteAccessTokenResponseIdToken.
@Test
public void shouldFailWhenStatusNotOK_rewriteAccessTokenResponseIdToken() {
// Given
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Location", "https://location");
ResponseEntity responseEntity = new ResponseEntity(HttpStatus.FOUND);
// Then
AccessTokenReWriteException accessTokenReWriteException = catchThrowableOfType(() -> this.jwtOverridingService.rewriteAccessTokenResponseIdToken(responseEntity), AccessTokenReWriteException.class);
// When
assertThat(accessTokenReWriteException.getMessage()).contains("Expected 200 (OK)");
}
Aggregations