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 @@ -197,7 +197,7 @@ public void executeOnEvent(ClientPolicyContext context) throws ClientPolicyExcep
if (client == null) {
throw invalidRedirectUri("Invalid parameter: clientId");
}
if (isAuthFlowWithRedirectEnabled(client)) {
if (redirectUriParam != null && isAuthFlowWithRedirectEnabled(client)) {
verifyRedirectUri(redirectUriParam, true);
}
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import org.keycloak.testsuite.util.ServerURLs;
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse;
import org.keycloak.testsuite.util.oauth.ParResponse;

import org.junit.Test;
import org.junit.jupiter.api.Assertions;
Expand Down Expand Up @@ -726,6 +727,173 @@ public void testSecureRedirectUrisEnforcerExecutor_authorizationRequestWithClien
testSecureRedirectUrisEnforcerExecutor_failAuthorizationRequestWithRedirectUriToClient(alphaClientId, "http://oauth.redirect/some");
}

@Test
public void testSecureRedirectUrisEnforcerExecutor_parAuthorizationRequest() throws Exception {
Comment on lines +730 to +731
// Register profile
String json = (new ClientPoliciesUtil.ClientProfilesBuilder()).addProfile(
(new ClientPoliciesUtil.ClientProfileBuilder())
.createProfile(PROFILE_NAME, "Secure Redirect URIs Profile")
.addExecutor(
SecureRedirectUrisEnforcerExecutorFactory.PROVIDER_ID,
createSecureRedirectUrisEnforcerExecutorConfig(it -> {
it.setAllowIPv4LoopbackAddress(true);
it.setAllowIPv6LoopbackAddress(true);
it.setAllowHttpScheme(true);
})
)
.toRepresentation()
).toString();
updateProfiles(json);

// Register policy
json = (new ClientPoliciesUtil.ClientPoliciesBuilder()).addPolicy(
(new ClientPoliciesUtil.ClientPolicyBuilder())
.createPolicy(
POLICY_NAME,
"Secure Redirect URIs Policy",
Boolean.TRUE
)
.addCondition(
AnyClientConditionFactory.PROVIDER_ID,
createAnyClientConditionConfig()
)
.addProfile(PROFILE_NAME)
.toRepresentation()
).toString();
updatePolicies(json);

String redirectUri =
ServerURLs.getAuthServerContextRoot()
+ "/auth/realms/master/app/auth";

// Register client
List<String> registerResult =
testSecureRedirectUrisEnforcerExecutor_successRegisterByAdmin(
List.of(redirectUri)
);

String clientId = registerResult.get(0);

// Pushed Authorization Request
oauth.client(clientId, "secret");
oauth.redirectUri(redirectUri);

ParResponse parResponse = oauth.doPushedAuthorizationRequest();

assertEquals(201, parResponse.getStatusCode());
Assertions.assertNotNull(parResponse.getRequestUri());

// Authorization Request containing client_id and request_uri,
// but not redirect_uri, scope, or response_type
oauth.redirectUri(null);
oauth.scope(null);
oauth.responseType(null);

String state =
"testSecureRedirectUrisEnforcerExecutor_parAuthorizationRequest";

AuthorizationEndpointResponse authorizationResponse =
oauth.loginForm()
.requestUri(parResponse.getRequestUri())
.state(state)
.doLogin("test-user@localhost", "password");

assertEquals(state, authorizationResponse.getState());
Assertions.assertNotNull(authorizationResponse.getCode());

// Token Request
oauth.redirectUri(redirectUri);

AccessTokenResponse tokenResponse =
oauth.doAccessTokenRequest(authorizationResponse.getCode());

assertEquals(200, tokenResponse.getStatusCode());

oauth.doLogout(tokenResponse.getRefreshToken());
}
@Test
public void testSecureRedirectUrisEnforcerExecutor_parAuthorizationRequestWithInvalidRedirectUri() throws Exception {
String redirectUri = "http://oauth.redirect/some";

// Create the client before enabling the policy.
// The redirect URI is registered, so Keycloak's standard redirect_uri
// validation should pass.
List<String> registerResult =
testSecureRedirectUrisEnforcerExecutor_successRegisterByAdmin(
List.of(redirectUri)
);

String clientId = registerResult.get(0);

// Register profile.
// HTTP redirect URIs are not allowed by the executor policy.
String json = (new ClientPoliciesUtil.ClientProfilesBuilder()).addProfile(
(new ClientPoliciesUtil.ClientProfileBuilder())
.createProfile(PROFILE_NAME, "Secure Redirect URIs Profile")
.addExecutor(
SecureRedirectUrisEnforcerExecutorFactory.PROVIDER_ID,
createSecureRedirectUrisEnforcerExecutorConfig(it -> {
it.setAllowHttpScheme(false);
})
)
.toRepresentation()
).toString();
updateProfiles(json);

// Register policy.
json = (new ClientPoliciesUtil.ClientPoliciesBuilder()).addPolicy(
(new ClientPoliciesUtil.ClientPolicyBuilder())
.createPolicy(
POLICY_NAME,
"Secure Redirect URIs Policy",
Boolean.TRUE
)
.addCondition(
AnyClientConditionFactory.PROVIDER_ID,
createAnyClientConditionConfig()
)
.addProfile(PROFILE_NAME)
.toRepresentation()
).toString();
updatePolicies(json);

// Pushed Authorization Request.
//
// The PAR request should succeed because the executor validates the
// resolved redirect URI later during AUTHORIZATION_REQUEST.
oauth.client(clientId, "secret");
oauth.redirectUri(redirectUri);

ParResponse parResponse = oauth.doPushedAuthorizationRequest();

assertEquals(201, parResponse.getStatusCode());
Assertions.assertNotNull(parResponse.getRequestUri());

// Authorization Request containing client_id and request_uri,
// but not redirect_uri, scope, or response_type.
oauth.redirectUri(null);
oauth.scope(null);
oauth.responseType(null);

oauth.loginForm()
.requestUri(parResponse.getRequestUri())
.open();

// The redirect URI stored in the PAR must be resolved and rejected by
// secure-redirect-uris-enforcer during AUTHORIZATION_REQUEST.
EventAssertion.assertError(events.poll())
.type(EventType.LOGIN_ERROR)
.error(Errors.INVALID_REQUEST)
.details(Details.REASON, Details.CLIENT_POLICY_ERROR)
.details(Details.RESPONSE_TYPE, "code")
.details(Details.REDIRECT_URI, redirectUri)
.details(
Details.CLIENT_POLICY_ERROR,
OAuthErrorException.INVALID_REQUEST
);
}


private void testSecureRedirectUrisEnforcerExecutor_failRegisterByAdmin(List<String> redirectUrisList) {
try {
createClientByAdmin(generateSuffixedName(CLIENT_NAME), (ClientRepresentation clientRep) -> {
Expand Down
Loading