Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions ci/cloudformation/auth/function/amc-authorize.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ Resources:
!If [UseSubEnvironment, !Ref SubEnvironment, !Ref Environment],
amcAuthorizeUri,
]
AMC_JWKS_URL:
!FindInMap [
EnvironmentConfiguration,
!If [UseSubEnvironment, !Ref SubEnvironment, !Ref Environment],
amcJwksUrl,
]
AMC_CLIENT_ID:
!FindInMap [
EnvironmentConfiguration,
Expand Down Expand Up @@ -57,10 +63,6 @@ Resources:
- arn:aws:kms:${AWS::Region}:${AWS::AccountId}:alias/${env}-auth-to-amc-signing-key
- env:
!If [UseSubEnvironment, !Ref SubEnvironment, !Ref Environment]
AUTH_TO_AMC_PUBLIC_ENCRYPTION_KEY: !Sub
- "{{resolve:secretsmanager:/deploy/${env}/auth_to_amc_public_encryption_key}}"
- env:
!If [UseSubEnvironment, !Ref SubEnvironment, !Ref Environment]
AUTH_TO_ACCOUNT_MANAGEMENT_API_AUDIENCE: !If
- IsNotProduction
- !If
Expand Down Expand Up @@ -101,7 +103,6 @@ Resources:
LogGroup: !Ref AMCAuthorizeFunctionLogGroup
Policies:
- !Ref LambdaBasicExecutionPolicy
- !Ref AuthToAMCPublicEncryptionKeyParameterPolicy
- !Ref AuthToAMCSigningKeyPolicy
- !Ref AuthToAccountManagementSigningKeyPolicy
- !Ref DynamoAuthSessionStoreReadAccessPolicy
Expand Down
27 changes: 8 additions & 19 deletions ci/cloudformation/auth/parent.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ Mappings:
amcCreatePasskeyRedirectUri: https://signin.authdev1.dev.account.gov.uk/create-passkey-callback
supportPasskeys: true
bulkEmailSendingEnabled: "true"
amcJwksUrl: https://amcstub.signin.authdev1.dev.account.gov.uk/.well-known/amc-jwks.json
authdev2:
accessTokenStoreTableEncryptionKey: arn:aws:kms:eu-west-2:653994557586:key/ff8d97e6-69d6-4ea1-81c0-0de8d8bcac85
accountModifiersTableEncryptionKey: arn:aws:kms:eu-west-2:653994557586:key/a99e39a4-0a63-4816-b222-6e7c6c318b32
Expand Down Expand Up @@ -295,6 +296,7 @@ Mappings:
amcCreatePasskeyRedirectUri: https://signin.authdev2.dev.account.gov.uk/create-passkey-callback
supportPasskeys: true
bulkEmailSendingEnabled: "true"
amcJwksUrl: https://amcstub.signin.authdev2.dev.account.gov.uk/.well-known/amc-jwks.json
authdev3:
accessTokenStoreTableEncryptionKey: arn:aws:kms:eu-west-2:653994557586:key/8a67ba56-4eb2-4bfb-8254-5cc1c92a5db5
accountModifiersTableEncryptionKey: arn:aws:kms:eu-west-2:653994557586:key/ce9680d3-953a-4b6a-9098-a0380d0afc70
Expand Down Expand Up @@ -348,6 +350,7 @@ Mappings:
supportPasskeys: true
amcCreatePasskeyRedirectUri: https://signin.authdev3.dev.account.gov.uk/create-passkey-callback
bulkEmailSendingEnabled: "true"
amcJwksUrl: https://amcstub.signin.authdev3.dev.account.gov.uk/.well-known/amc-jwks.json
dev:
accessTokenStoreTableEncryptionKey: arn:aws:kms:eu-west-2:653994557586:key/f5e5d059-1581-4c5c-8d60-e168fd8a9a84
accountModifiersTableEncryptionKey: arn:aws:kms:eu-west-2:653994557586:key/4b475855-e71a-43a5-a836-c6cb37e9cb22
Expand Down Expand Up @@ -407,6 +410,7 @@ Mappings:
amcCreatePasskeyRedirectUri: https://signin.dev.account.gov.uk/create-passkey-callback
supportPasskeys: true
bulkEmailSendingEnabled: "true"
amcJwksUrl: https://amcstub.signin.dev.account.gov.uk/.well-known/amc-jwks.json
build:
accessTokenStoreTableEncryptionKey: arn:aws:kms:eu-west-2:761723964695:key/e8fdebb6-2d33-4d0c-825c-2b5c874522d8
accountModifiersTableEncryptionKey: arn:aws:kms:eu-west-2:761723964695:key/4932c4d3-bf86-4f39-a32c-b82faf9a2574
Expand Down Expand Up @@ -471,6 +475,7 @@ Mappings:
supportPasskeys: false
amcCreatePasskeyRedirectUri: https://signin.build.account.gov.uk/create-passkey-callback
bulkEmailSendingEnabled: "false"
amcJwksUrl: https://amcstub.signin.build.account.gov.uk/.well-known/amc-jwks.json
staging:
accessTokenStoreTableEncryptionKey: arn:aws:kms:eu-west-2:758531536632:key/35cfcec1-e8f1-4ffc-950d-80e836c594ca
accountModifiersTableEncryptionKey: arn:aws:kms:eu-west-2:758531536632:key/01f3d1a6-e66f-4eb7-a3d7-412437c8f01d
Expand Down Expand Up @@ -536,6 +541,7 @@ Mappings:
supportPasskeys: false
amcCreatePasskeyRedirectUri: https://signin.staging.account.gov.uk/create-passkey-callback
bulkEmailSendingEnabled: "true"
amcJwksUrl: https://kvys7gq7h4.execute-api.eu-west-2.amazonaws.com/v1/.well-known/jwks.json
Comment thread
BeckaL marked this conversation as resolved.
integration:
accessTokenStoreTableEncryptionKey: arn:aws:kms:eu-west-2:761723964695:key/11fdf47e-ce54-4d47-a9cf-7544489a112a
accountModifiersTableEncryptionKey: arn:aws:kms:eu-west-2:761723964695:key/1e526227-051b-4078-ae10-46bda94e71c4
Expand Down Expand Up @@ -601,6 +607,7 @@ Mappings:
supportPasskeys: false
amcCreatePasskeyRedirectUri: https://signin.integration.account.gov.uk/create-passkey-callback
bulkEmailSendingEnabled: "false"
amcJwksUrl: https://rxkn8gwqb8.execute-api.eu-west-2.amazonaws.com/v1/.well-known/jwks.json
production:
accessTokenStoreTableEncryptionKey: arn:aws:kms:eu-west-2:172348255554:key/730472f0-c7ba-4bda-a411-08cdaa65fe8a
accountModifiersTableEncryptionKey: arn:aws:kms:eu-west-2:172348255554:key/075774c2-b836-452f-9684-dcd742146f81
Expand Down Expand Up @@ -666,6 +673,7 @@ Mappings:
supportPasskeys: false
amcCreatePasskeyRedirectUri: https://signin.account.gov.uk/create-passkey-callback
bulkEmailSendingEnabled: "false"
amcJwksUrl: https://hkzy726xx0.execute-api.eu-west-2.amazonaws.com/v1/.well-known/jwks.json
LambdaConfiguration:
account-recovery:
RunbookLink: "https://govukverify.atlassian.net/l/cp/LfLKwP4s"
Expand Down Expand Up @@ -2971,25 +2979,6 @@ Resources:
- kms:GetPublicKey
Resource: !GetAtt AuthToAccountDataSigningKey.Arn

AuthToAMCPublicEncryptionKeyParameterPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: "IAM policy for accessing Auth to AMC public encryption key from Secrets Manager"
Path: !Sub
- "/${Env}/auth-to-amc/"
- Env: !If [UseSubEnvironment, !Ref SubEnvironment, !Ref Environment]
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: AllowAccessToPublicEncryptionKey
Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource: !Sub
- "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:/deploy/${env}/auth_to_amc_public_encryption_key-*"
- env:
!If [UseSubEnvironment, !Ref SubEnvironment, !Ref Environment]

NotifyCallbackFunctionParameterPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ public enum JwtFailureReason {
TRANSCODING_ERROR("transcoding_error"),
SIGNING_ERROR("signing_error"),
ENCRYPTION_ERROR("encryption_error"),
UNKNOWN_JWT_ENCRYPTING_ERROR("unknown_jwt_encrypting_error");
UNKNOWN_JWT_ENCRYPTING_ERROR("unknown_jwt_encrypting_error"),
JWKS_RETRIEVAL_ERROR("jwks_retrieval_error");

private final String value;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public static ErrorResponseWithStatus toHttpResponse(JwtFailureReason failureRea
500, ErrorResponse.AMC_UNKNOWN_JWT_SIGNING_ERROR);
case UNKNOWN_JWT_ENCRYPTING_ERROR -> new ErrorResponseWithStatus(
500, ErrorResponse.AMC_UNKNOWN_JWT_ENCRYPTING_ERROR);
case JWKS_RETRIEVAL_ERROR -> new ErrorResponseWithStatus(
500, ErrorResponse.AMC_JWKS_RETRIEVAL_ERROR);
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,18 @@
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.KeySourceException;
import com.nimbusds.jose.jwk.JWKMatcher;
import com.nimbusds.jose.jwk.JWKSelector;
import com.nimbusds.jose.jwk.KeyType;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.jwk.source.JWKSourceBuilder;
import com.nimbusds.jose.proc.SecurityContext;
import com.nimbusds.oauth2.sdk.id.State;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import uk.gov.di.authentication.frontendapi.entity.amc.AMCAuthorizationUrlAndCookie;
import uk.gov.di.authentication.frontendapi.entity.amc.AMCAuthorizeRequest;
import uk.gov.di.authentication.frontendapi.entity.amc.AMCAuthorizeResponse;
Expand All @@ -26,6 +37,8 @@
import uk.gov.di.authentication.shared.services.KmsConnectionService;
import uk.gov.di.authentication.shared.state.UserContext;

import java.net.MalformedURLException;
import java.security.interfaces.RSAPublicKey;
import java.time.Clock;
import java.util.List;

Expand All @@ -34,6 +47,9 @@

public class AMCAuthorizeHandler extends BaseFrontendHandler<AMCAuthorizeRequest> {
private final AMCService amcService;
private final JWKSource<SecurityContext> jwkSource;

private static final Logger LOG = LogManager.getLogger(AMCAuthorizeHandler.class);
private final DynamoAmcStateService dynamoAmcStateService;

public AMCAuthorizeHandler() {
Expand All @@ -42,6 +58,17 @@ public AMCAuthorizeHandler() {

public AMCAuthorizeHandler(ConfigurationService configurationService) {
super(AMCAuthorizeRequest.class, configurationService);
try {
this.jwkSource =
JWKSourceBuilder.create(configurationService.getAmcJwksUrl())
.retrying(true)
.refreshAheadCache(false)
.cache(true)
.rateLimited(false)
.build();
} catch (MalformedURLException e) {
throw new IllegalStateException("Invalid AMC JWKS URL: " + e.getMessage(), e);
}
this.amcService =
new AMCService(
configurationService,
Expand All @@ -62,13 +89,15 @@ public AMCAuthorizeHandler(
AuthenticationService authenticationService,
AuthSessionService authSessionService,
AMCService amcService,
JWKSource<SecurityContext> jwkSource,
DynamoAmcStateService amcStateService) {
super(
AMCAuthorizeRequest.class,
configurationService,
authenticationService,
authSessionService);
this.amcService = amcService;
this.jwkSource = jwkSource;
this.dynamoAmcStateService = amcStateService;
}

Expand Down Expand Up @@ -98,14 +127,18 @@ public APIGatewayProxyResponseEvent handleRequestWithUserContext(
dynamoAmcStateService.store(state.getValue(), userContext.getClientSessionId());

Result<JwtFailureReason, AMCAuthorizationUrlAndCookie> result =
amcService.buildAuthorizationResult(
authSessionItem.getInternalCommonSubjectId(),
transportJwtConfig.scope(),
authSessionItem,
userProfile.getPublicSubjectID(),
transportJwtConfig.redirectUri(),
accessTokenConfigsForJourneyType,
state);
getAMCPublicEncryptionKey()
.flatMap(
publicEncryptionKey ->
amcService.buildAuthorizationResult(
authSessionItem.getInternalCommonSubjectId(),
transportJwtConfig.scope(),
authSessionItem,
userProfile.getPublicSubjectID(),
transportJwtConfig.redirectUri(),
accessTokenConfigsForJourneyType,
publicEncryptionKey,
state));

return result.fold(
AMCFailureHttpMapper::toApiGatewayProxyErrorResponse,
Expand All @@ -119,4 +152,30 @@ public APIGatewayProxyResponseEvent handleRequestWithUserContext(
}
});
}

private Result<JwtFailureReason, RSAPublicKey> getAMCPublicEncryptionKey() {
LOG.info("Retrieving RSA encryption JWK from AMC JWKS endpoint for auth -> AMC encryption");
try {
return Result.success(
this.jwkSource
.get(
new JWKSelector(
new JWKMatcher.Builder().keyType(KeyType.RSA).build()),
null)
.stream()
.map(RSAKey.class::cast)
.findFirst()
.orElseThrow(
() ->
new KeySourceException(
"No RSA key found on the JWKS endpoint"))
.toRSAPublicKey());
} catch (KeySourceException e) {
LOG.error("Could not retrieve JWKS", e);
return Result.failure(JwtFailureReason.JWKS_RETRIEVAL_ERROR);
} catch (JOSEException e) {
LOG.error("Could not parse JWK", e);
return Result.failure(JwtFailureReason.JWKS_RETRIEVAL_ERROR);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package uk.gov.di.authentication.frontendapi.services;

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jwt.EncryptedJWT;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
Expand Down Expand Up @@ -65,13 +64,14 @@
this.jwtService = jwtService;
}

public Result<JwtFailureReason, AMCAuthorizationUrlAndCookie> buildAuthorizationResult(

Check warning on line 67 in frontend-api/src/main/java/uk/gov/di/authentication/frontendapi/services/AMCService.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Method has 8 parameters, which is greater than 7 authorized.

See more on https://sonarcloud.io/project/issues?id=govuk-one-login_authentication-api&issues=AZ1sY4N-eCvyWxz8JS3G&open=AZ1sY4N-eCvyWxz8JS3G&pullRequest=8080
String internalPairwiseSubject,
AMCScope amcScope,
AuthSessionItem authSessionItem,
String publicSubject,
String amcRedirectUri,
List<AccessTokenConfig> accessTokenConfigs,
RSAPublicKey publicEncryptionKey,
State state) {
LOG.info("Building AMC authorization URL");

Expand All @@ -82,6 +82,7 @@
authSessionItem,
publicSubject,
accessTokenConfigs,
publicEncryptionKey,
state)
.map(
encryptedJWTAndAmcCookie -> {
Expand Down Expand Up @@ -163,13 +164,14 @@

private record EncryptedJWTAndAmcCookie(EncryptedJWT encryptedJWT, String amcCookie) {}

private Result<JwtFailureReason, EncryptedJWTAndAmcCookie> createTransportJWTAndAmcCookie(

Check warning on line 167 in frontend-api/src/main/java/uk/gov/di/authentication/frontendapi/services/AMCService.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Method has 8 parameters, which is greater than 7 authorized.

See more on https://sonarcloud.io/project/issues?id=govuk-one-login_authentication-api&issues=AZ1sY4N-eCvyWxz8JS3H&open=AZ1sY4N-eCvyWxz8JS3H&pullRequest=8080
String internalPairwiseSubject,
AMCScope amcScope,
String amcRedirectUri,
AuthSessionItem authSessionItem,
String publicSubject,
List<AccessTokenConfig> accessTokenConfigs,
RSAPublicKey publicEncryptionKey,
State state) {
Date issueTime = nowClock.now();
Date expiryDate = nowClock.nowPlus(CLIENT_ASSERTION_LIFETIME, ChronoUnit.MINUTES);
Expand Down Expand Up @@ -213,23 +215,12 @@
})
.flatMap(
signedJWT -> {
try {
var hashedCookie =
HashHelper.hashSha256String(signedJWT.serialize());
RSAPublicKey publicKey =
JWK.parseFromPEMEncodedObjects(
configurationService
.getAuthToAMCPublicEncryptionKey())
.toRSAKey()
.toRSAPublicKey();
return encryptJWT(signedJWT, publicKey)
.map(
encryptedJWT ->
new EncryptedJWTAndAmcCookie(
encryptedJWT, hashedCookie));
} catch (JOSEException e) {
return Result.failure(JwtFailureReason.JWT_ENCODING_ERROR);
}
var hashedCookie = HashHelper.hashSha256String(signedJWT.serialize());
return encryptJWT(signedJWT, publicEncryptionKey)
.map(
encryptedJWT ->
new EncryptedJWTAndAmcCookie(
encryptedJWT, hashedCookie));
});
}

Expand Down
Loading
Loading