Search in sources :

Example 1 with AuthRequestError

use of uk.gov.di.authentication.oidc.entity.AuthRequestError in project di-authentication-api by alphagov.

the class RequestObjectService method validateRequestObject.

public Optional<AuthRequestError> validateRequestObject(AuthenticationRequest authRequest) {
    var clientId = authRequest.getClientID().toString();
    attachLogFieldToLogs(CLIENT_ID, clientId);
    var client = dynamoClientService.getClient(clientId).orElse(null);
    try {
        if (Objects.isNull(client)) {
            var errorMsg = "No Client found with given ClientID";
            LOG.warn(errorMsg);
            throw new RuntimeException(errorMsg);
        }
        var signedJWT = (SignedJWT) authRequest.getRequestObject();
        var signatureValid = isSignatureValid(signedJWT, client.getPublicKey());
        if (!signatureValid) {
            LOG.error("Invalid Signature on request JWT");
            throw new RuntimeException();
        }
        var jwtClaimsSet = signedJWT.getJWTClaimsSet();
        if (client.getRedirectUrls().stream().filter(Objects::nonNull).noneMatch(s -> s.equals(jwtClaimsSet.getClaim("redirect_uri")))) {
            throw new RuntimeException("Invalid Redirect URI in request JWT");
        }
        var redirectURI = URI.create((String) jwtClaimsSet.getClaim("redirect_uri"));
        if (Boolean.FALSE.equals(client.getClientType().equals(ClientType.APP.getValue()))) {
            LOG.warn("ClientType of client is not 'app'");
            return Optional.of(new AuthRequestError(OAuth2Error.UNAUTHORIZED_CLIENT, redirectURI));
        }
        if (!authRequest.getResponseType().toString().equals(ResponseType.CODE.toString())) {
            LOG.warn("Unsupported responseType included in request. Expected responseType of code");
            return Optional.of(new AuthRequestError(OAuth2Error.UNSUPPORTED_RESPONSE_TYPE, redirectURI));
        }
        if (requestContainsInvalidScopes(authRequest.getScope().toStringList(), client, false)) {
            LOG.warn("Invalid scopes in authRequest. Scopes in request: {}", authRequest.getScope().toStringList());
            return Optional.of(new AuthRequestError(OAuth2Error.INVALID_SCOPE, redirectURI));
        }
        if (Objects.isNull(jwtClaimsSet.getClaim("client_id")) || !jwtClaimsSet.getClaim("client_id").toString().equals(authRequest.getClientID().getValue())) {
            return Optional.of(new AuthRequestError(OAuth2Error.UNAUTHORIZED_CLIENT, redirectURI));
        }
        if (Objects.nonNull(jwtClaimsSet.getClaim("request")) || Objects.nonNull(jwtClaimsSet.getClaim("request_uri"))) {
            LOG.warn("request or request_uri claim should not be incldued in request JWT");
            return Optional.of(new AuthRequestError(OAuth2Error.INVALID_REQUEST, redirectURI));
        }
        if (Objects.isNull(jwtClaimsSet.getAudience()) || !jwtClaimsSet.getAudience().contains(buildURI(configurationService.getOidcApiBaseURL().orElseThrow(), "/authorize").toString())) {
            LOG.warn("Invalid or missing audience");
            return Optional.of(new AuthRequestError(OAuth2Error.ACCESS_DENIED, redirectURI));
        }
        if (Objects.isNull(jwtClaimsSet.getIssuer()) || !jwtClaimsSet.getIssuer().equals(client.getClientID())) {
            LOG.warn("Invalid or missing issuer");
            return Optional.of(new AuthRequestError(OAuth2Error.UNAUTHORIZED_CLIENT, redirectURI));
        }
        if (!ResponseType.CODE.toString().equals(jwtClaimsSet.getClaim("response_type"))) {
            LOG.warn("Unsupported responseType included in request JWT. Expected responseType of code");
            return Optional.of(new AuthRequestError(OAuth2Error.UNSUPPORTED_RESPONSE_TYPE, redirectURI));
        }
        if (Objects.isNull(jwtClaimsSet.getClaim("scope")) || requestContainsInvalidScopes(Scope.parse(jwtClaimsSet.getClaim("scope").toString()).toStringList(), client, true)) {
            LOG.warn("Invalid scopes in request JWT");
            return Optional.of(new AuthRequestError(OAuth2Error.INVALID_SCOPE, redirectURI));
        }
        if (Objects.isNull(jwtClaimsSet.getClaim("state"))) {
            LOG.warn("State is missing from authRequest");
            return Optional.of(new AuthRequestError(new ErrorObject(OAuth2Error.INVALID_REQUEST_CODE, "Request is missing state parameter"), redirectURI));
        }
    } catch (ParseException e) {
        throw new RuntimeException(e);
    }
    LOG.info("RequestObject has passed initial validation");
    return Optional.empty();
}
Also used : AuthRequestError(uk.gov.di.authentication.oidc.entity.AuthRequestError) ErrorObject(com.nimbusds.oauth2.sdk.ErrorObject) SignedJWT(com.nimbusds.jwt.SignedJWT) ParseException(java.text.ParseException)

Example 2 with AuthRequestError

use of uk.gov.di.authentication.oidc.entity.AuthRequestError in project di-authentication-api by alphagov.

the class AuthorisationHandlerTest method shouldReturn400WhenAuthorisationRequestContainsInvalidScope.

@Test
void shouldReturn400WhenAuthorisationRequestContainsInvalidScope() {
    when(authorizationService.validateAuthRequest(any(AuthenticationRequest.class))).thenReturn(Optional.of(new AuthRequestError(OAuth2Error.INVALID_SCOPE, URI.create("http://localhost:8080"))));
    APIGatewayProxyRequestEvent event = new APIGatewayProxyRequestEvent();
    event.setQueryStringParameters(Map.of("client_id", "test-id", "redirect_uri", "http://localhost:8080", "scope", "email,openid,profile,non-existent-scope", "response_type", "code"));
    event.setRequestContext(new ProxyRequestContext().withIdentity(new RequestIdentity().withSourceIp("123.123.123.123")));
    APIGatewayProxyResponseEvent response = makeHandlerRequest(event);
    assertThat(response, hasStatus(302));
    assertEquals("http://localhost:8080?error=invalid_scope&error_description=Invalid%2C+unknown+or+malformed+scope", response.getHeaders().get(ResponseHeaders.LOCATION));
    verify(auditService).submitAuditEvent(AUTHORISATION_REQUEST_ERROR, AWS_REQUEST_ID, "", "", "", "", "123.123.123.123", "", PERSISTENT_SESSION_ID, pair("description", OAuth2Error.INVALID_SCOPE.getDescription()));
}
Also used : RequestIdentity(com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent.RequestIdentity) ProxyRequestContext(com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent.ProxyRequestContext) AuthRequestError(uk.gov.di.authentication.oidc.entity.AuthRequestError) APIGatewayProxyRequestEvent(com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent) AuthenticationRequest(com.nimbusds.openid.connect.sdk.AuthenticationRequest) APIGatewayProxyResponseEvent(com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent) Test(org.junit.jupiter.api.Test) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest)

Example 3 with AuthRequestError

use of uk.gov.di.authentication.oidc.entity.AuthRequestError in project di-authentication-api by alphagov.

the class AuthorisationHandlerTest method shouldReturnErrorWhenRequestObjectIsInvalid.

@ParameterizedTest
@MethodSource("expectedErrorObjects")
void shouldReturnErrorWhenRequestObjectIsInvalid(ErrorObject errorObject) {
    when(configService.isDocAppApiEnabled()).thenReturn(true);
    when(requestObjectService.validateRequestObject(any(AuthenticationRequest.class))).thenReturn(Optional.of(new AuthRequestError(errorObject, URI.create("http://localhost:8080"))));
    var event = new APIGatewayProxyRequestEvent();
    event.setQueryStringParameters(Map.of("client_id", "test-id", "redirect_uri", "http://localhost:8080", "scope", "openid", "response_type", "code", "request", new PlainJWT(new JWTClaimsSet.Builder().build()).serialize()));
    event.setRequestContext(new ProxyRequestContext().withIdentity(new RequestIdentity().withSourceIp("123.123.123.123")));
    var response = makeHandlerRequest(event);
    var expectedURI = new AuthenticationErrorResponse(URI.create("http://localhost:8080"), errorObject, null, null).toURI().toString();
    assertThat(response, hasStatus(302));
    assertEquals(expectedURI, response.getHeaders().get(ResponseHeaders.LOCATION));
    verify(auditService).submitAuditEvent(AUTHORISATION_REQUEST_ERROR, AWS_REQUEST_ID, "", "", "", "", "123.123.123.123", "", PERSISTENT_SESSION_ID, pair("description", errorObject.getDescription()));
}
Also used : RequestIdentity(com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent.RequestIdentity) PlainJWT(com.nimbusds.jwt.PlainJWT) ProxyRequestContext(com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent.ProxyRequestContext) AuthenticationErrorResponse(com.nimbusds.openid.connect.sdk.AuthenticationErrorResponse) AuthRequestError(uk.gov.di.authentication.oidc.entity.AuthRequestError) APIGatewayProxyRequestEvent(com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent) JWTClaimsSet(com.nimbusds.jwt.JWTClaimsSet) AuthenticationRequest(com.nimbusds.openid.connect.sdk.AuthenticationRequest) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) MethodSource(org.junit.jupiter.params.provider.MethodSource)

Example 4 with AuthRequestError

use of uk.gov.di.authentication.oidc.entity.AuthRequestError in project di-authentication-api by alphagov.

the class AuthorizationService method validateAuthRequest.

public Optional<AuthRequestError> validateAuthRequest(AuthenticationRequest authRequest) {
    var clientId = authRequest.getClientID().toString();
    attachLogFieldToLogs(CLIENT_ID, clientId);
    Optional<ClientRegistry> client = dynamoClientService.getClient(clientId);
    if (client.isEmpty()) {
        var errorMsg = "No Client found with given ClientID";
        LOG.warn(errorMsg);
        throw new RuntimeException(errorMsg);
    }
    if (!client.get().getRedirectUrls().contains(authRequest.getRedirectionURI().toString())) {
        LOG.warn("Invalid Redirect URI in request {}", authRequest.getRedirectionURI());
        throw new RuntimeException(format("Invalid Redirect in request %s", authRequest.getRedirectionURI().toString()));
    }
    var redirectURI = authRequest.getRedirectionURI();
    if (authRequest.getRequestURI() != null) {
        return Optional.of(new AuthRequestError(OAuth2Error.REQUEST_URI_NOT_SUPPORTED, redirectURI));
    }
    if (authRequest.getRequestObject() != null) {
        return Optional.of(new AuthRequestError(OAuth2Error.REQUEST_NOT_SUPPORTED, redirectURI));
    }
    if (!authRequest.getResponseType().toString().equals(ResponseType.CODE.toString())) {
        LOG.warn("Unsupported responseType included in request. Expected responseType of code");
        return Optional.of(new AuthRequestError(OAuth2Error.UNSUPPORTED_RESPONSE_TYPE, redirectURI));
    }
    if (!areScopesValid(authRequest.getScope().toStringList(), client.get())) {
        LOG.warn("Invalid scopes in authRequest. Scopes in request: {}", authRequest.getScope().toStringList());
        return Optional.of(new AuthRequestError(OAuth2Error.INVALID_SCOPE, redirectURI));
    }
    if (!areClaimsValid(authRequest.getOIDCClaims(), client.get())) {
        LOG.warn("Invalid claims in authRequest. Claims in request: {}", authRequest.getOIDCClaims().toJSONString());
        return Optional.of(new AuthRequestError(new ErrorObject(OAuth2Error.INVALID_REQUEST_CODE, "Request contains invalid claims"), redirectURI));
    }
    if (authRequest.getNonce() == null) {
        LOG.warn("Nonce is missing from authRequest");
        return Optional.of(new AuthRequestError(new ErrorObject(OAuth2Error.INVALID_REQUEST_CODE, "Request is missing nonce parameter"), redirectURI));
    }
    if (authRequest.getState() == null) {
        LOG.warn("State is missing from authRequest");
        return Optional.of(new AuthRequestError(new ErrorObject(OAuth2Error.INVALID_REQUEST_CODE, "Request is missing state parameter"), redirectURI));
    }
    List<String> authRequestVtr = authRequest.getCustomParameter(VTR_PARAM);
    try {
        var vectorOfTrust = VectorOfTrust.parseFromAuthRequestAttribute(authRequestVtr);
        if (vectorOfTrust.containsLevelOfConfidence() && !ipvCapacityService.isIPVCapacityAvailable()) {
            return Optional.of(new AuthRequestError(OAuth2Error.TEMPORARILY_UNAVAILABLE, redirectURI));
        }
    } catch (IllegalArgumentException e) {
        LOG.warn("vtr in AuthRequest is not valid. vtr in request: {}. IllegalArgumentException: {}", authRequestVtr, e);
        return Optional.of(new AuthRequestError(new ErrorObject(OAuth2Error.INVALID_REQUEST_CODE, "Request vtr not valid"), redirectURI));
    }
    return Optional.empty();
}
Also used : AuthRequestError(uk.gov.di.authentication.oidc.entity.AuthRequestError) ErrorObject(com.nimbusds.oauth2.sdk.ErrorObject) ClientRegistry(uk.gov.di.authentication.shared.entity.ClientRegistry)

Example 5 with AuthRequestError

use of uk.gov.di.authentication.oidc.entity.AuthRequestError in project di-authentication-api by alphagov.

the class AuthorisationHandler method authoriseRequestHandler.

public APIGatewayProxyResponseEvent authoriseRequestHandler(APIGatewayProxyRequestEvent input, Context context) {
    return isWarming(input).orElseGet(() -> {
        var persistentSessionId = authorizationService.getExistingOrCreateNewPersistentSessionId(input.getHeaders());
        var ipAddress = IpAddressHelper.extractIpAddress(input);
        auditService.submitAuditEvent(OidcAuditableEvent.AUTHORISATION_REQUEST_RECEIVED, context.getAwsRequestId(), AuditService.UNKNOWN, AuditService.UNKNOWN, AuditService.UNKNOWN, AuditService.UNKNOWN, ipAddress, AuditService.UNKNOWN, persistentSessionId);
        attachLogFieldToLogs(PERSISTENT_SESSION_ID, persistentSessionId);
        attachLogFieldToLogs(AWS_REQUEST_ID, context.getAwsRequestId());
        LOG.info("Received authentication request");
        Map<String, List<String>> queryStringParameters;
        AuthenticationRequest authRequest;
        try {
            queryStringParameters = input.getQueryStringParameters().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> List.of(entry.getValue())));
            authRequest = AuthenticationRequest.parse(queryStringParameters);
        } catch (ParseException e) {
            if (e.getRedirectionURI() == null) {
                LOG.warn("Authentication request could not be parsed: redirect URI or Client ID is missing from auth request");
                throw new RuntimeException("Redirect URI or ClientID is missing from auth request", e);
            }
            LOG.warn("Authentication request could not be parsed", e);
            return generateErrorResponse(e.getRedirectionURI(), e.getState(), e.getResponseMode(), e.getErrorObject(), context, ipAddress, persistentSessionId);
        } catch (NullPointerException e) {
            LOG.warn("No query string parameters are present in the Authentication request", e);
            throw new RuntimeException("No query string parameters are present in the Authentication request", e);
        }
        Optional<AuthRequestError> authRequestError;
        if (authRequest.getRequestObject() != null && configurationService.isDocAppApiEnabled()) {
            LOG.info("RequestObject auth request received");
            authRequestError = requestObjectService.validateRequestObject(authRequest);
        } else {
            authRequestError = authorizationService.validateAuthRequest(authRequest);
        }
        return authRequestError.map(e -> generateErrorResponse(e.getRedirectURI(), authRequest.getState(), authRequest.getResponseMode(), e.getErrorObject(), context, ipAddress, persistentSessionId)).orElseGet(() -> getOrCreateSessionAndRedirect(queryStringParameters, sessionService.getSessionFromSessionCookie(input.getHeaders()), authRequest, context, ipAddress, persistentSessionId));
    });
}
Also used : AuthRequestError(uk.gov.di.authentication.oidc.entity.AuthRequestError) Prompt(com.nimbusds.openid.connect.sdk.Prompt) RequestObjectService(uk.gov.di.authentication.oidc.services.RequestObjectService) SessionService(uk.gov.di.authentication.shared.services.SessionService) URISyntaxException(java.net.URISyntaxException) LocalDateTime(java.time.LocalDateTime) Context(com.amazonaws.services.lambda.runtime.Context) ConfigurationService(uk.gov.di.authentication.shared.services.ConfigurationService) InstrumentationHelper.segmentedFunctionCall(uk.gov.di.authentication.shared.helpers.InstrumentationHelper.segmentedFunctionCall) RequestHandler(com.amazonaws.services.lambda.runtime.RequestHandler) ResponseMode(com.nimbusds.oauth2.sdk.ResponseMode) ResponseHeaders(uk.gov.di.authentication.shared.entity.ResponseHeaders) APIGatewayProxyRequestEvent(com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent) Session(uk.gov.di.authentication.shared.entity.Session) CLIENT_SESSION_ID(uk.gov.di.authentication.shared.helpers.LogLineHelper.LogFieldName.CLIENT_SESSION_ID) PERSISTENT_SESSION_ID(uk.gov.di.authentication.shared.helpers.LogLineHelper.LogFieldName.PERSISTENT_SESSION_ID) Map(java.util.Map) ParseException(com.nimbusds.oauth2.sdk.ParseException) URI(java.net.URI) AWS_REQUEST_ID(uk.gov.di.authentication.shared.helpers.LogLineHelper.LogFieldName.AWS_REQUEST_ID) CLIENT_ID(uk.gov.di.authentication.shared.helpers.LogLineHelper.LogFieldName.CLIENT_ID) LogLineHelper.updateAttachedSessionIdToLogs(uk.gov.di.authentication.shared.helpers.LogLineHelper.updateAttachedSessionIdToLogs) MetadataPair.pair(uk.gov.di.authentication.shared.services.AuditService.MetadataPair.pair) WarmerHelper.isWarming(uk.gov.di.authentication.shared.helpers.WarmerHelper.isWarming) AuthenticationErrorResponse(com.nimbusds.openid.connect.sdk.AuthenticationErrorResponse) OIDCError(com.nimbusds.openid.connect.sdk.OIDCError) URIBuilder(org.apache.http.client.utils.URIBuilder) OidcAuditableEvent(uk.gov.di.authentication.oidc.domain.OidcAuditableEvent) IpAddressHelper(uk.gov.di.authentication.shared.helpers.IpAddressHelper) APIGatewayProxyResponseEvent(com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent) ClientSession(uk.gov.di.authentication.shared.entity.ClientSession) AuditService(uk.gov.di.authentication.shared.services.AuditService) LogLineHelper.attachLogFieldToLogs(uk.gov.di.authentication.shared.helpers.LogLineHelper.attachLogFieldToLogs) State(com.nimbusds.oauth2.sdk.id.State) ErrorObject(com.nimbusds.oauth2.sdk.ErrorObject) CookieHelper(uk.gov.di.authentication.shared.helpers.CookieHelper) Collectors(java.util.stream.Collectors) ClientSessionService(uk.gov.di.authentication.shared.services.ClientSessionService) Objects(java.util.Objects) List(java.util.List) Logger(org.apache.logging.log4j.Logger) AuthenticationRequest(com.nimbusds.openid.connect.sdk.AuthenticationRequest) LogLineHelper.attachSessionIdToLogs(uk.gov.di.authentication.shared.helpers.LogLineHelper.attachSessionIdToLogs) LogLineHelper.updateAttachedLogFieldToLogs(uk.gov.di.authentication.shared.helpers.LogLineHelper.updateAttachedLogFieldToLogs) Optional(java.util.Optional) LogManager(org.apache.logging.log4j.LogManager) AuthorizationService(uk.gov.di.authentication.oidc.services.AuthorizationService) AuthRequestError(uk.gov.di.authentication.oidc.entity.AuthRequestError) List(java.util.List) ParseException(com.nimbusds.oauth2.sdk.ParseException) AuthenticationRequest(com.nimbusds.openid.connect.sdk.AuthenticationRequest)

Aggregations

AuthRequestError (uk.gov.di.authentication.oidc.entity.AuthRequestError)5 APIGatewayProxyRequestEvent (com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent)3 ErrorObject (com.nimbusds.oauth2.sdk.ErrorObject)3 AuthenticationRequest (com.nimbusds.openid.connect.sdk.AuthenticationRequest)3 ProxyRequestContext (com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent.ProxyRequestContext)2 RequestIdentity (com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent.RequestIdentity)2 APIGatewayProxyResponseEvent (com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent)2 AuthenticationErrorResponse (com.nimbusds.openid.connect.sdk.AuthenticationErrorResponse)2 ParameterizedTest (org.junit.jupiter.params.ParameterizedTest)2 Context (com.amazonaws.services.lambda.runtime.Context)1 RequestHandler (com.amazonaws.services.lambda.runtime.RequestHandler)1 JWTClaimsSet (com.nimbusds.jwt.JWTClaimsSet)1 PlainJWT (com.nimbusds.jwt.PlainJWT)1 SignedJWT (com.nimbusds.jwt.SignedJWT)1 ParseException (com.nimbusds.oauth2.sdk.ParseException)1 ResponseMode (com.nimbusds.oauth2.sdk.ResponseMode)1 State (com.nimbusds.oauth2.sdk.id.State)1 OIDCError (com.nimbusds.openid.connect.sdk.OIDCError)1 Prompt (com.nimbusds.openid.connect.sdk.Prompt)1 URI (java.net.URI)1