Search in sources :

Example 41 with Subject

use of com.nimbusds.oauth2.sdk.id.Subject in project di-authentication-api by alphagov.

the class TokenIntegrationTest method shouldCallTokenResourceAndReturn400WhenClientIdParameterDoesNotMatch.

@Test
void shouldCallTokenResourceAndReturn400WhenClientIdParameterDoesNotMatch() throws Exception {
    KeyPair keyPair = KeyPairHelper.GENERATE_RSA_KEY_PAIR();
    Scope scope = new Scope(OIDCScopeValue.OPENID.getValue(), OIDCScopeValue.OFFLINE_ACCESS.getValue());
    setUpDynamo(keyPair, scope, new Subject());
    var response = generateTokenRequest(keyPair, scope, Optional.of("Cl.Cm"), Optional.empty(), Optional.of(DIFFERENT_CLIENT_ID));
    assertThat(response, hasStatus(400));
    assertThat(response, hasBody(OAuth2Error.INVALID_CLIENT.toJSONObject().toJSONString()));
}
Also used : KeyPair(java.security.KeyPair) Scope(com.nimbusds.oauth2.sdk.Scope) Subject(com.nimbusds.oauth2.sdk.id.Subject) Test(org.junit.jupiter.api.Test) ApiGatewayHandlerIntegrationTest(uk.gov.di.authentication.sharedtest.basetest.ApiGatewayHandlerIntegrationTest) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest)

Example 42 with Subject

use of com.nimbusds.oauth2.sdk.id.Subject in project di-authentication-api by alphagov.

the class TokenIntegrationTest method shouldCallTokenResourceAndOnlyReturnAccessTokenWithoutOfflineAccessScope.

@Test
void shouldCallTokenResourceAndOnlyReturnAccessTokenWithoutOfflineAccessScope() throws Exception {
    KeyPair keyPair = KeyPairHelper.GENERATE_RSA_KEY_PAIR();
    Scope scope = new Scope(OIDCScopeValue.OPENID.getValue());
    setUpDynamo(keyPair, scope, new Subject());
    var response = generateTokenRequest(keyPair, scope, Optional.empty(), Optional.empty(), Optional.of(CLIENT_ID));
    assertThat(response, hasStatus(200));
    JSONObject jsonResponse = JSONObjectUtils.parse(response.getBody());
    assertNull(TokenResponse.parse(jsonResponse).toSuccessResponse().getTokens().getRefreshToken());
    assertNotNull(TokenResponse.parse(jsonResponse).toSuccessResponse().getTokens().getBearerAccessToken());
    assertNoAuditEventsReceived(auditTopic);
}
Also used : KeyPair(java.security.KeyPair) Scope(com.nimbusds.oauth2.sdk.Scope) JSONObject(net.minidev.json.JSONObject) Subject(com.nimbusds.oauth2.sdk.id.Subject) Test(org.junit.jupiter.api.Test) ApiGatewayHandlerIntegrationTest(uk.gov.di.authentication.sharedtest.basetest.ApiGatewayHandlerIntegrationTest) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest)

Example 43 with Subject

use of com.nimbusds.oauth2.sdk.id.Subject in project di-authentication-api by alphagov.

the class TokenHandler method tokenRequestHandler.

public APIGatewayProxyResponseEvent tokenRequestHandler(APIGatewayProxyRequestEvent input, Context context) {
    return isWarming(input).orElseGet(() -> {
        LOG.info("Token request received");
        Optional<ErrorObject> invalidRequestParamError = tokenService.validateTokenRequestParams(input.getBody());
        if (invalidRequestParamError.isPresent()) {
            LOG.warn("Invalid Token Request. ErrorCode: {}. ErrorDescription: {}", invalidRequestParamError.get().getCode(), invalidRequestParamError.get().getDescription());
            return generateApiGatewayProxyResponse(400, invalidRequestParamError.get().toJSONObject().toJSONString());
        }
        Map<String, String> requestBody = parseRequestBody(input.getBody());
        addAnnotation("grant_type", requestBody.get("grant_type"));
        String clientID;
        ClientRegistry client;
        try {
            clientID = tokenService.getClientIDFromPrivateKeyJWT(input.getBody()).orElseThrow();
            attachLogFieldToLogs(CLIENT_ID, clientID);
            addAnnotation("client_id", clientID);
            client = clientService.getClient(clientID).orElseThrow();
        } catch (NoSuchElementException e) {
            LOG.warn("Invalid client or client not found in Client Registry");
            return generateApiGatewayProxyResponse(400, OAuth2Error.INVALID_CLIENT.toJSONObject().toJSONString());
        }
        String baseUrl = configurationService.getOidcApiBaseURL().orElseThrow(() -> {
            LOG.error("Application was not configured with baseURL");
            return new RuntimeException("Application was not configured with baseURL");
        });
        String tokenUrl = buildURI(baseUrl, TOKEN_PATH).toString();
        Optional<ErrorObject> invalidPrivateKeyJwtError = segmentedFunctionCall("validatePrivateKeyJWT", () -> tokenService.validatePrivateKeyJWT(input.getBody(), client.getPublicKey(), tokenUrl, clientID));
        if (invalidPrivateKeyJwtError.isPresent()) {
            LOG.warn("Private Key JWT is not valid for Client ID: {}", clientID);
            return generateApiGatewayProxyResponse(400, invalidPrivateKeyJwtError.get().toJSONObject().toJSONString());
        }
        if (requestBody.get("grant_type").equals(GrantType.REFRESH_TOKEN.getValue())) {
            LOG.info("Processing refresh token request");
            return segmentedFunctionCall("processRefreshTokenRequest", () -> processRefreshTokenRequest(requestBody, client.getScopes(), new RefreshToken(requestBody.get("refresh_token")), clientID));
        }
        AuthCodeExchangeData authCodeExchangeData;
        try {
            authCodeExchangeData = segmentedFunctionCall("authorisationCodeService", () -> authorisationCodeService.getExchangeDataForCode(requestBody.get("code")).orElseThrow());
        } catch (NoSuchElementException e) {
            LOG.warn("Could not retrieve client session ID from code", e);
            return generateApiGatewayProxyResponse(400, OAuth2Error.INVALID_GRANT.toJSONObject().toJSONString());
        }
        updateAttachedLogFieldToLogs(CLIENT_SESSION_ID, authCodeExchangeData.getClientSessionId());
        ClientSession clientSession = authCodeExchangeData.getClientSession();
        AuthenticationRequest authRequest;
        try {
            authRequest = AuthenticationRequest.parse(clientSession.getAuthRequestParams());
        } catch (ParseException e) {
            LOG.warn("Could not parse authentication request from client session", e);
            throw new RuntimeException(format("Unable to parse Auth Request\n Auth Request Params: %s \n Exception: %s", clientSession.getAuthRequestParams(), e));
        }
        var authRequestRedirectURI = isDocCheckingAppUserWithSubjectId(clientSession) ? getRequestObjectClaim(authRequest, "redirect_uri", String.class) : authRequest.getRedirectionURI().toString();
        if (!authRequestRedirectURI.equals(requestBody.get("redirect_uri"))) {
            LOG.warn("Redirect URI for auth request ({}) does not match redirect URI for request body ({})", authRequestRedirectURI, requestBody.get("redirect_uri"));
            return generateApiGatewayProxyResponse(400, OAuth2Error.INVALID_GRANT.toJSONObject().toJSONString());
        }
        Map<String, Object> additionalTokenClaims = new HashMap<>();
        if (authRequest.getNonce() != null) {
            additionalTokenClaims.put("nonce", authRequest.getNonce());
        }
        String vot = clientSession.getEffectiveVectorOfTrust().retrieveVectorOfTrustForToken();
        OIDCClaimsRequest claimsRequest = null;
        if (Objects.nonNull(clientSession.getEffectiveVectorOfTrust().getLevelOfConfidence()) && Objects.nonNull(authRequest.getOIDCClaims())) {
            claimsRequest = authRequest.getOIDCClaims();
        }
        var isConsentRequired = client.isConsentRequired() && !clientSession.getEffectiveVectorOfTrust().containsLevelOfConfidence();
        final OIDCClaimsRequest finalClaimsRequest = claimsRequest;
        OIDCTokenResponse tokenResponse;
        if (isDocCheckingAppUserWithSubjectId(clientSession)) {
            LOG.info("Doc Checking App User with SubjectId: true");
            Scope scope = new Scope(getRequestObjectScopeClaim(authRequest));
            tokenResponse = segmentedFunctionCall("generateTokenResponse", () -> tokenService.generateTokenResponse(clientID, clientSession.getDocAppSubjectId(), scope, additionalTokenClaims, clientSession.getDocAppSubjectId(), vot, null, false, finalClaimsRequest, true));
        } else {
            UserProfile userProfile = dynamoService.getUserProfileByEmail(authCodeExchangeData.getEmail());
            Subject subject = ClientSubjectHelper.getSubject(userProfile, client, dynamoService);
            tokenResponse = segmentedFunctionCall("generateTokenResponse", () -> tokenService.generateTokenResponse(clientID, new Subject(userProfile.getSubjectID()), authRequest.getScope(), additionalTokenClaims, subject, vot, userProfile.getClientConsent(), isConsentRequired, finalClaimsRequest, false));
        }
        clientSessionService.saveClientSession(authCodeExchangeData.getClientSessionId(), clientSession.setIdTokenHint(tokenResponse.getOIDCTokens().getIDToken().serialize()));
        LOG.info("Successfully generated tokens");
        return generateApiGatewayProxyResponse(200, tokenResponse.toJSONObject().toJSONString());
    });
}
Also used : UserProfile(uk.gov.di.authentication.shared.entity.UserProfile) HashMap(java.util.HashMap) ErrorObject(com.nimbusds.oauth2.sdk.ErrorObject) OIDCTokenResponse(com.nimbusds.openid.connect.sdk.OIDCTokenResponse) Subject(com.nimbusds.oauth2.sdk.id.Subject) AuthCodeExchangeData(uk.gov.di.authentication.shared.entity.AuthCodeExchangeData) OIDCClaimsRequest(com.nimbusds.openid.connect.sdk.OIDCClaimsRequest) RefreshToken(com.nimbusds.oauth2.sdk.token.RefreshToken) Scope(com.nimbusds.oauth2.sdk.Scope) ClientSession(uk.gov.di.authentication.shared.entity.ClientSession) ClientRegistry(uk.gov.di.authentication.shared.entity.ClientRegistry) ErrorObject(com.nimbusds.oauth2.sdk.ErrorObject) ParseException(com.nimbusds.oauth2.sdk.ParseException) AuthenticationRequest(com.nimbusds.openid.connect.sdk.AuthenticationRequest) NoSuchElementException(java.util.NoSuchElementException)

Example 44 with Subject

use of com.nimbusds.oauth2.sdk.id.Subject in project di-authentication-api by alphagov.

the class TokenHandler method processRefreshTokenRequest.

private APIGatewayProxyResponseEvent processRefreshTokenRequest(Map<String, String> requestBody, List<String> clientScopes, RefreshToken currentRefreshToken, String clientId) {
    boolean refreshTokenSignatureValid = tokenValidationService.validateRefreshTokenSignatureAndExpiry(currentRefreshToken);
    if (!refreshTokenSignatureValid) {
        return generateApiGatewayProxyResponse(400, OAuth2Error.INVALID_GRANT.toJSONObject().toJSONString());
    }
    Subject subject;
    List<String> scopes;
    String jti;
    try {
        SignedJWT signedJwt = SignedJWT.parse(currentRefreshToken.getValue());
        subject = new Subject(signedJwt.getJWTClaimsSet().getSubject());
        scopes = (List<String>) signedJwt.getJWTClaimsSet().getClaim("scope");
        jti = signedJwt.getJWTClaimsSet().getJWTID();
    } catch (java.text.ParseException e) {
        LOG.warn("Unable to parse RefreshToken");
        return generateApiGatewayProxyResponse(400, new ErrorObject(OAuth2Error.INVALID_GRANT_CODE, "Invalid Refresh token").toJSONObject().toJSONString());
    }
    boolean areScopesValid = tokenValidationService.validateRefreshTokenScopes(clientScopes, scopes);
    if (!areScopesValid) {
        return generateApiGatewayProxyResponse(400, OAuth2Error.INVALID_SCOPE.toJSONObject().toJSONString());
    }
    String redisKey = REFRESH_TOKEN_PREFIX + jti;
    Optional<String> refreshToken = Optional.ofNullable(redisConnectionService.popValue(redisKey));
    RefreshTokenStore tokenStore;
    try {
        tokenStore = objectMapper.readValue(refreshToken.get(), RefreshTokenStore.class);
    } catch (JsonException | NoSuchElementException | IllegalArgumentException e) {
        LOG.warn("Refresh token not found with given key");
        return generateApiGatewayProxyResponse(400, new ErrorObject(OAuth2Error.INVALID_GRANT_CODE, "Invalid Refresh token").toJSONObject().toJSONString());
    }
    if (!tokenStore.getRefreshToken().equals(currentRefreshToken.getValue())) {
        LOG.warn("Refresh token store does not contain Refresh token in request");
        return generateApiGatewayProxyResponse(400, new ErrorObject(OAuth2Error.INVALID_GRANT_CODE, "Invalid Refresh token").toJSONObject().toJSONString());
    }
    OIDCTokenResponse tokenResponse = tokenService.generateRefreshTokenResponse(clientId, new Subject(tokenStore.getInternalSubjectId()), scopes, subject);
    LOG.info("Generating successful RefreshToken response");
    return generateApiGatewayProxyResponse(200, tokenResponse.toJSONObject().toJSONString());
}
Also used : JsonException(uk.gov.di.authentication.shared.serialization.Json.JsonException) RefreshTokenStore(uk.gov.di.authentication.shared.entity.RefreshTokenStore) ErrorObject(com.nimbusds.oauth2.sdk.ErrorObject) OIDCTokenResponse(com.nimbusds.openid.connect.sdk.OIDCTokenResponse) SignedJWT(com.nimbusds.jwt.SignedJWT) Subject(com.nimbusds.oauth2.sdk.id.Subject) Context(com.amazonaws.services.lambda.runtime.Context) NoSuchElementException(java.util.NoSuchElementException)

Example 45 with Subject

use of com.nimbusds.oauth2.sdk.id.Subject in project di-authentication-api by alphagov.

the class UserInfoService method populateUserInfo.

public UserInfo populateUserInfo(AccessTokenInfo accessTokenInfo, boolean identityEnabled) {
    LOG.info("Populating UserInfo");
    var userInfo = new UserInfo(new Subject(accessTokenInfo.getSubject()));
    if (accessTokenInfo.getScopes().contains(CustomScopeValue.DOC_CHECKING_APP.getValue())) {
        return populateDocAppUserInfo(accessTokenInfo, userInfo);
    }
    var userProfile = authenticationService.getUserProfileFromSubject(accessTokenInfo.getAccessTokenStore().getInternalSubjectId());
    if (accessTokenInfo.getScopes().contains(OIDCScopeValue.EMAIL.getValue())) {
        userInfo.setEmailAddress(userProfile.getEmail());
        userInfo.setEmailVerified(userProfile.isEmailVerified());
    }
    if (accessTokenInfo.getScopes().contains(OIDCScopeValue.PHONE.getValue())) {
        userInfo.setPhoneNumber(userProfile.getPhoneNumber());
        userInfo.setPhoneNumberVerified(userProfile.isPhoneNumberVerified());
    }
    if (accessTokenInfo.getScopes().contains(CustomScopeValue.GOVUK_ACCOUNT.getValue())) {
        userInfo.setClaim("legacy_subject_id", userProfile.getLegacySubjectID());
    }
    if (identityEnabled && Objects.nonNull(accessTokenInfo.getIdentityClaims())) {
        return populateIdentityInfo(accessTokenInfo, userInfo);
    } else {
        LOG.info("No identity claims present");
        return userInfo;
    }
}
Also used : UserInfo(com.nimbusds.openid.connect.sdk.claims.UserInfo) Subject(com.nimbusds.oauth2.sdk.id.Subject)

Aggregations

Subject (com.nimbusds.oauth2.sdk.id.Subject)59 Test (org.junit.jupiter.api.Test)36 SignedJWT (com.nimbusds.jwt.SignedJWT)22 Date (java.util.Date)22 ApiGatewayHandlerIntegrationTest (uk.gov.di.authentication.sharedtest.basetest.ApiGatewayHandlerIntegrationTest)19 UserProfile (uk.gov.di.authentication.shared.entity.UserProfile)18 KeyPair (java.security.KeyPair)16 BearerAccessToken (com.nimbusds.oauth2.sdk.token.BearerAccessToken)15 JWTClaimsSet (com.nimbusds.jwt.JWTClaimsSet)13 ParseException (com.nimbusds.oauth2.sdk.ParseException)12 Scope (com.nimbusds.oauth2.sdk.Scope)12 APIGatewayProxyRequestEvent (com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent)11 APIGatewayProxyResponseEvent (com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent)11 AccessToken (com.nimbusds.oauth2.sdk.token.AccessToken)10 ECKeyGenerator (com.nimbusds.jose.jwk.gen.ECKeyGenerator)9 ParameterizedTest (org.junit.jupiter.params.ParameterizedTest)9 ECDSASigner (com.nimbusds.jose.crypto.ECDSASigner)8 Issuer (com.nimbusds.oauth2.sdk.id.Issuer)8 IDTokenClaimsSet (com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet)8 LocalDateTime (java.time.LocalDateTime)8