Search in sources :

Example 6 with AccessTokenReWriteException

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;
}
Also used : HttpHeaders(org.springframework.http.HttpHeaders) RedirectionAction(com.forgerock.openbanking.common.model.rcs.RedirectionAction) OBErrorException(com.forgerock.openbanking.exceptions.OBErrorException) SignedJWT(com.nimbusds.jwt.SignedJWT) ResponseEntity(org.springframework.http.ResponseEntity) AMGateway(com.forgerock.openbanking.am.gateway.AMGateway) ParameterizedTypeReference(org.springframework.core.ParameterizedTypeReference) OBErrorResponseException(com.forgerock.openbanking.exceptions.OBErrorResponseException) AccessTokenReWriteException(com.forgerock.openbanking.common.error.exception.AccessTokenReWriteException) ParseException(java.text.ParseException)

Example 7 with AccessTokenReWriteException

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;
}
Also used : AMGateway(com.forgerock.openbanking.am.gateway.AMGateway) OBErrorResponseException(com.forgerock.openbanking.exceptions.OBErrorResponseException) AccessTokenReWriteException(com.forgerock.openbanking.common.error.exception.AccessTokenReWriteException) GrantType(com.forgerock.openbanking.constants.OIDCConstants.GrantType) AccessTokenResponse(com.forgerock.openbanking.model.oidc.AccessTokenResponse) PairClientIDAuthMethod(com.forgerock.openbanking.aspsp.as.service.PairClientIDAuthMethod) PreAuthorize(org.springframework.security.access.prepost.PreAuthorize)

Example 8 with AccessTokenReWriteException

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);
    }
}
Also used : Claims(com.forgerock.openbanking.model.claim.Claims) ArrayList(java.util.ArrayList) OBErrorException(com.forgerock.openbanking.exceptions.OBErrorException) IOException(java.io.IOException) SignedJWT(com.nimbusds.jwt.SignedJWT) ResponseEntity(org.springframework.http.ResponseEntity) Tpp(com.forgerock.openbanking.model.Tpp) AccessTokenReWriteException(com.forgerock.openbanking.common.error.exception.AccessTokenReWriteException) ConsentDecision(com.forgerock.openbanking.common.model.rcs.consentdecision.ConsentDecision) ParseException(java.text.ParseException) JOSEException(com.nimbusds.jose.JOSEException)

Example 9 with AccessTokenReWriteException

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)");
}
Also used : HttpHeaders(org.springframework.http.HttpHeaders) ResponseEntity(org.springframework.http.ResponseEntity) AccessTokenReWriteException(com.forgerock.openbanking.common.error.exception.AccessTokenReWriteException) Test(org.junit.Test)

Aggregations

AccessTokenReWriteException (com.forgerock.openbanking.common.error.exception.AccessTokenReWriteException)9 ResponseEntity (org.springframework.http.ResponseEntity)8 HttpHeaders (org.springframework.http.HttpHeaders)5 ParseException (java.text.ParseException)4 Test (org.junit.Test)4 AccessTokenResponse (com.forgerock.openbanking.model.oidc.AccessTokenResponse)3 AMGateway (com.forgerock.openbanking.am.gateway.AMGateway)2 OBErrorException (com.forgerock.openbanking.exceptions.OBErrorException)2 OBErrorResponseException (com.forgerock.openbanking.exceptions.OBErrorResponseException)2 SignedJWT (com.nimbusds.jwt.SignedJWT)2 PairClientIDAuthMethod (com.forgerock.openbanking.aspsp.as.service.PairClientIDAuthMethod)1 RedirectionAction (com.forgerock.openbanking.common.model.rcs.RedirectionAction)1 ConsentDecision (com.forgerock.openbanking.common.model.rcs.consentdecision.ConsentDecision)1 GrantType (com.forgerock.openbanking.constants.OIDCConstants.GrantType)1 Tpp (com.forgerock.openbanking.model.Tpp)1 Claims (com.forgerock.openbanking.model.claim.Claims)1 JOSEException (com.nimbusds.jose.JOSEException)1 IOException (java.io.IOException)1 ArrayList (java.util.ArrayList)1 Matcher (java.util.regex.Matcher)1