Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ public TokenHandler(ConfigurationService configurationService) {
this.tokenClientAuthValidatorFactory =
new TokenClientAuthValidatorFactory(
new DynamoClientService(configurationService),
new ClientSignatureValidationService(configurationService));
new ClientSignatureValidationService(configurationService),
configurationService);
this.metrics = new Metrics(configurationService);
this.auditService = new AuditService(configurationService);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import java.util.Objects;
import java.util.Optional;

import static uk.gov.di.orchestration.shared.utils.ClientUtils.getTokenAuthMethodOrDefault;

public abstract class BaseAuthorizeValidator {

protected static final String VTR_PARAM = "vtr";
Expand Down Expand Up @@ -205,8 +207,9 @@ protected Optional<ErrorObject> errorIfIdentityLoCAndIdentityUnsupported(

protected void logIdentityJourneyRequestWithInsufficientlySecureTokenAuthMethod(
List<VectorOfTrust> vtrList, ClientRegistry client) {
if (requestContainsIdentityLoC(vtrList)
&& ("client_secret_post".equals(client.getTokenAuthMethod()))) {
String tokenAuthMethod = getTokenAuthMethodOrDefault(client, configurationService);

if (requestContainsIdentityLoC(vtrList) && ("client_secret_post".equals(tokenAuthMethod))) {
LOG.info(
"Request contains level of confidence values for an identity journey but the tokenAuthMethod is incompatible.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,10 @@ public long getSyncWaitForSpotTimeout() {
return Long.parseLong(System.getenv().getOrDefault("SYNC_WAIT_FOR_SPOT_TIMEOUT", "5000"));
Comment thread
Louisasa marked this conversation as resolved.
}

public boolean isUseDefaultTokenAuthMethod() {
return getFlagOrFalse("USE_DEFAULT_TOKEN_AUTH_METHOD");
}

public String getNotifyTemplateId(String templateName) {
return System.getenv(templateName);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package uk.gov.di.orchestration.shared.utils;

import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod;
import uk.gov.di.orchestration.shared.entity.ClientRegistry;
import uk.gov.di.orchestration.shared.services.ConfigurationService;

import java.util.Objects;

public class ClientUtils {
private ClientUtils() {}

public static String getTokenAuthMethodOrDefault(
ClientRegistry clientRegistry, ConfigurationService configurationService) {
var tokenAuthMethod = clientRegistry.getTokenAuthMethod();
if (Objects.isNull(tokenAuthMethod) && configurationService.isUseDefaultTokenAuthMethod()) {
tokenAuthMethod = ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue();
}
return tokenAuthMethod;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import uk.gov.di.orchestration.shared.entity.ClientRegistry;
import uk.gov.di.orchestration.shared.exceptions.TokenAuthInvalidException;
import uk.gov.di.orchestration.shared.helpers.Argon2MatcherHelper;
import uk.gov.di.orchestration.shared.services.ConfigurationService;
import uk.gov.di.orchestration.shared.services.DynamoClientService;

import java.util.Map;
Expand All @@ -17,11 +18,16 @@
import static uk.gov.di.orchestration.shared.helpers.InstrumentationHelper.addAnnotation;
import static uk.gov.di.orchestration.shared.helpers.LogLineHelper.LogFieldName.CLIENT_ID;
import static uk.gov.di.orchestration.shared.helpers.LogLineHelper.attachLogFieldToLogs;
import static uk.gov.di.orchestration.shared.utils.ClientUtils.getTokenAuthMethodOrDefault;

public class ClientSecretPostClientAuthValidator extends TokenClientAuthValidator {

public ClientSecretPostClientAuthValidator(DynamoClientService dynamoClientService) {
private final ConfigurationService configurationService;

public ClientSecretPostClientAuthValidator(
DynamoClientService dynamoClientService, ConfigurationService configurationService) {
super(dynamoClientService);
this.configurationService = configurationService;
}

@Override
Expand Down Expand Up @@ -56,10 +62,10 @@ public ClientRegistry validateTokenAuthAndReturnClientRegistryIfValid(

private void validateTokenAuthMethod(ClientRegistry clientRegistry)
throws TokenAuthInvalidException {
if (Objects.isNull(clientRegistry.getTokenAuthMethod())
|| !clientRegistry
.getTokenAuthMethod()
.equals(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue())) {
var tokenAuthMethod = getTokenAuthMethodOrDefault(clientRegistry, configurationService);
if (Objects.isNull(tokenAuthMethod)
|| !tokenAuthMethod.equals(
ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue())) {
LOG.warn("Client is not registered to use client_secret_post");
throw generateExceptionWithInvalidClientCode(
"Client is not registered to use client_secret_post",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import uk.gov.di.orchestration.shared.exceptions.TokenAuthInvalidException;
import uk.gov.di.orchestration.shared.helpers.NowHelper;
import uk.gov.di.orchestration.shared.services.ClientSignatureValidationService;
import uk.gov.di.orchestration.shared.services.ConfigurationService;
import uk.gov.di.orchestration.shared.services.DynamoClientService;

import java.util.Date;
Expand All @@ -24,17 +25,21 @@
import static uk.gov.di.orchestration.shared.helpers.InstrumentationHelper.addAnnotation;
import static uk.gov.di.orchestration.shared.helpers.LogLineHelper.LogFieldName.CLIENT_ID;
import static uk.gov.di.orchestration.shared.helpers.LogLineHelper.attachLogFieldToLogs;
import static uk.gov.di.orchestration.shared.utils.ClientUtils.getTokenAuthMethodOrDefault;

public class PrivateKeyJwtClientAuthValidator extends TokenClientAuthValidator {

private final ClientSignatureValidationService clientSignatureValidationService;
private final ConfigurationService configurationService;
private static final String UNKNOWN_CLIENT_ID = "unknown";

public PrivateKeyJwtClientAuthValidator(
DynamoClientService dynamoClientService,
ClientSignatureValidationService clientSignatureValidationService) {
ClientSignatureValidationService clientSignatureValidationService,
ConfigurationService configurationService) {
super(dynamoClientService);
this.clientSignatureValidationService = clientSignatureValidationService;
this.configurationService = configurationService;
}

@Override
Expand All @@ -51,10 +56,10 @@ public ClientRegistry validateTokenAuthAndReturnClientRegistryIfValid(
var clientRegistry = getClientRegistryFromTokenAuth(privateKeyJWT.getClientID());
attachLogFieldToLogs(CLIENT_ID, clientRegistry.getClientID());
addAnnotation("client_id", clientRegistry.getClientID());
if (Objects.nonNull(clientRegistry.getTokenAuthMethod())
&& !clientRegistry
.getTokenAuthMethod()
.equals(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue())) {
var tokenAuthMethod = getTokenAuthMethodOrDefault(clientRegistry, configurationService);
if (Objects.nonNull(tokenAuthMethod)
&& !tokenAuthMethod.equals(
ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue())) {
LOG.warn("Client is not registered to use private_key_jwt");
throw new TokenAuthInvalidException(
new ErrorObject(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import uk.gov.di.orchestration.shared.services.ClientSignatureValidationService;
import uk.gov.di.orchestration.shared.services.ConfigurationService;
import uk.gov.di.orchestration.shared.services.DynamoClientService;

import java.util.Map;
Expand All @@ -12,13 +13,16 @@
public class TokenClientAuthValidatorFactory {
private final DynamoClientService dynamoClientService;
private final ClientSignatureValidationService clientSignatureValidationService;
private final ConfigurationService configurationService;
private static final Logger LOG = LogManager.getLogger(TokenClientAuthValidatorFactory.class);

public TokenClientAuthValidatorFactory(
DynamoClientService dynamoClientService,
ClientSignatureValidationService clientSignatureValidationService) {
ClientSignatureValidationService clientSignatureValidationService,
ConfigurationService configurationService) {
this.clientSignatureValidationService = clientSignatureValidationService;
this.dynamoClientService = dynamoClientService;
this.configurationService = configurationService;
}

public Optional<TokenClientAuthValidator> getTokenAuthenticationValidator(
Expand All @@ -31,12 +35,16 @@ public Optional<TokenClientAuthValidator> getTokenAuthenticationValidator(
checkAssertionType(requestBody);
return Optional.of(
new PrivateKeyJwtClientAuthValidator(
dynamoClientService, clientSignatureValidationService));
dynamoClientService,
clientSignatureValidationService,
configurationService));
}

if (requestBody.containsKey("client_secret") && requestBody.containsKey("client_id")) {
LOG.info("Client auth method is: client_secret_post");
return Optional.of(new ClientSecretPostClientAuthValidator(dynamoClientService));
return Optional.of(
new ClientSecretPostClientAuthValidator(
dynamoClientService, configurationService));
}
return Optional.empty();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package uk.gov.di.orchestration.shared.utils;

import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import uk.gov.di.orchestration.shared.entity.ClientRegistry;
import uk.gov.di.orchestration.shared.services.ConfigurationService;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class ClientUtilsTest {

Check warning on line 14 in orchestration-shared/src/test/java/uk/gov/di/orchestration/shared/utils/ClientUtilsTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this 'public' modifier.

See more on https://sonarcloud.io/project/issues?id=govuk-one-login_authentication-api&issues=AZ5A1kdoRutHMp6i_9ce&open=AZ5A1kdoRutHMp6i_9ce&pullRequest=8362
private final ConfigurationService configurationService = mock(ConfigurationService.class);

@BeforeEach
public void setup() {

Check warning on line 18 in orchestration-shared/src/test/java/uk/gov/di/orchestration/shared/utils/ClientUtilsTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this 'public' modifier.

See more on https://sonarcloud.io/project/issues?id=govuk-one-login_authentication-api&issues=AZ5A1kdoRutHMp6i_9cd&open=AZ5A1kdoRutHMp6i_9cd&pullRequest=8362
when(configurationService.isUseDefaultTokenAuthMethod()).thenReturn(false);
}

@Test
void shouldDefaultToPrivateKeyJwtIfFeatureFlagIsEnabledAndTokenAuthMethodIsNull() {
when(configurationService.isUseDefaultTokenAuthMethod()).thenReturn(true);
var client = clientWithTokenAuthMethod(null);

var actualTokenAuthMethod =
ClientUtils.getTokenAuthMethodOrDefault(client, configurationService);
assertEquals(actualTokenAuthMethod, ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue());
}

@Test
void shouldNotDefaultToPrivateKeyJwtIfFeatureFlagDisabled() {
when(configurationService.isUseDefaultTokenAuthMethod()).thenReturn(false);
var client = clientWithTokenAuthMethod(null);

var actualTokenAuthMethod =
ClientUtils.getTokenAuthMethodOrDefault(client, configurationService);
assertNull(actualTokenAuthMethod);
}

@Test
void shouldNotDefaultToPrivateKeyJwtIfFeatureFlagIsEnabledAndTokenAuthMethodIsAlreadySet() {
when(configurationService.isUseDefaultTokenAuthMethod()).thenReturn(true);
var client =
clientWithTokenAuthMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue());

var actualTokenAuthMethod =
ClientUtils.getTokenAuthMethodOrDefault(client, configurationService);
assertEquals(
actualTokenAuthMethod, ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue());
}

private ClientRegistry clientWithTokenAuthMethod(String tokenAuthMethod) {
return new ClientRegistry()
.withClientID("client-id")
.withClientName("client-one")
.withTokenAuthMethod(tokenAuthMethod);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
import com.nimbusds.oauth2.sdk.util.URLUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import uk.gov.di.orchestration.shared.entity.ClientRegistry;
import uk.gov.di.orchestration.shared.exceptions.TokenAuthInvalidException;
import uk.gov.di.orchestration.shared.helpers.Argon2EncoderHelper;
import uk.gov.di.orchestration.shared.services.ConfigurationService;
import uk.gov.di.orchestration.shared.services.DynamoClientService;

import java.util.Objects;
Expand All @@ -30,6 +33,7 @@
class ClientSecretPostClientAuthValidatorTest {

private final DynamoClientService dynamoClientService = mock(DynamoClientService.class);
private final ConfigurationService configurationService = mock(ConfigurationService.class);
private ClientSecretPostClientAuthValidator clientSecretPostClientAuthValidator;

private static final ClientID CLIENT_ID = new ClientID();
Expand All @@ -38,7 +42,7 @@ class ClientSecretPostClientAuthValidatorTest {
@BeforeEach
void setUp() {
clientSecretPostClientAuthValidator =
new ClientSecretPostClientAuthValidator(dynamoClientService);
new ClientSecretPostClientAuthValidator(dynamoClientService, configurationService);
}

@Test
Expand Down Expand Up @@ -80,13 +84,17 @@ requestString, emptyMap()),
assertThat(tokenAuthInvalidException.getErrorObject(), equalTo(OAuth2Error.INVALID_CLIENT));
}

@Test
void shouldThrowIfClientRegistryDoesNotSupportClientSecretPost() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
void shouldThrowIfClientRegistryDoesNotSupportClientSecretPost(
boolean useDefaultTokenAuthMethod) {
var expectedClientRegistry =
generateClientRegistry(
null, Argon2EncoderHelper.argon2Hash(CLIENT_SECRET.getValue()));
when(dynamoClientService.getClient(CLIENT_ID.getValue()))
.thenReturn(Optional.of(expectedClientRegistry));
when(configurationService.isUseDefaultTokenAuthMethod())
.thenReturn(useDefaultTokenAuthMethod);
var clientSecretPost = new ClientSecretPost(CLIENT_ID, CLIENT_SECRET);
var requestString = URLUtils.serializeParameters(clientSecretPost.toParameters());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import uk.gov.di.orchestration.shared.exceptions.TokenAuthInvalidException;
import uk.gov.di.orchestration.shared.helpers.NowHelper;
import uk.gov.di.orchestration.shared.services.ClientSignatureValidationService;
import uk.gov.di.orchestration.shared.services.ConfigurationService;
import uk.gov.di.orchestration.shared.services.DynamoClientService;

import java.net.URI;
Expand All @@ -46,6 +47,7 @@ class PrivateKeyJwtClientAuthValidatorTest {
private final DynamoClientService dynamoClientService = mock(DynamoClientService.class);
private final ClientSignatureValidationService clientSignatureValidationService =
mock(ClientSignatureValidationService.class);
private final ConfigurationService configurationService = mock(ConfigurationService.class);
private OidcAPI oidcAPI = mock(OidcAPI.class);
private static final URI OIDC_TOKEN_URL = URI.create("https://example.com/token");
private static final ClientID CLIENT_ID = new ClientID();
Expand All @@ -57,7 +59,9 @@ void setUp() {
when(oidcAPI.tokenURI()).thenReturn(OIDC_TOKEN_URL);
privateKeyJwtClientAuthValidator =
new PrivateKeyJwtClientAuthValidator(
dynamoClientService, clientSignatureValidationService);
dynamoClientService,
clientSignatureValidationService,
configurationService);
}

private static Stream<JWSAlgorithm> supportedAlgorithms() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.nimbusds.oauth2.sdk.util.URLUtils;
import org.junit.jupiter.api.Test;
import uk.gov.di.orchestration.shared.services.ClientSignatureValidationService;
import uk.gov.di.orchestration.shared.services.ConfigurationService;
import uk.gov.di.orchestration.shared.services.DynamoClientService;
import uk.gov.di.orchestration.sharedtest.utils.KeyPairUtils;

Expand All @@ -23,11 +24,12 @@ class TokenClientAuthValidatorFactoryTest {
private final DynamoClientService dynamoClientService = mock(DynamoClientService.class);
private final ClientSignatureValidationService clientSignatureValidationService =
mock(ClientSignatureValidationService.class);
private final ConfigurationService configurationService = mock(ConfigurationService.class);
private static final ClientID CLIENT_ID = new ClientID();
private static final Secret CLIENT_SECRET = new Secret();
private final TokenClientAuthValidatorFactory tokenClientAuthValidatorFactory =
new TokenClientAuthValidatorFactory(
dynamoClientService, clientSignatureValidationService);
dynamoClientService, clientSignatureValidationService, configurationService);

@Test
void shouldReturnPrivateKeyJwtClientAuthValidator() throws JOSEException {
Expand Down
14 changes: 14 additions & 0 deletions template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ Conditions:
!Equals [integration, !Ref Environment],
!Equals [production, !Ref Environment],
]
UseDefaultTokenAuthMethod:
!Or [
!Equals [dev, !Ref Environment],
!Equals [build, !Ref Environment],
!Equals [staging, !Ref Environment],
]

Mappings:
EnvironmentConfiguration:
Expand Down Expand Up @@ -2402,6 +2408,10 @@ Resources:
!Ref Environment,
authEnvironment,
]
USE_DEFAULT_TOKEN_AUTH_METHOD: !If
- UseDefaultTokenAuthMethod
- true
- false
Policies:
- !Ref ClientRegistryTableReadAccessPolicy
- !Ref TxmaQueueSendPermissionPolicy
Expand Down Expand Up @@ -4036,6 +4046,10 @@ Resources:
Fn::ImportValue: !Sub orchestration-${Environment}-txma-QueueURL
TXMA_AUDIT_ENCODED_ENABLED: true
JWK_CACHE_EXPIRATION_IN_SECONDS: 300
USE_DEFAULT_TOKEN_AUTH_METHOD: !If
- UseDefaultTokenAuthMethod
- true
- false

Policies:
- !Ref ClientRegistryTableReadAccessPolicy
Expand Down
Loading