Skip to content

Commit

Permalink
Allow asking for additional scopes when querying the account console …
Browse files Browse the repository at this point in the history
…root URL

Closes keycloak#35243

Signed-off-by: Pedro Igor <[email protected]>
  • Loading branch information
pedroigor committed Nov 27, 2024
1 parent bcb1930 commit 4dfa224
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 13 deletions.
4 changes: 4 additions & 0 deletions docs/documentation/server_admin/topics/account.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
.Account Console
image:images/account-console-intro.png[Account Console]

You can also ask for additional scopes when calling the account console URL by setting the `scope` parameter in this format: _server-root_{kc_realms_path}/{realm-name}/account?scope=phone.
The scopes can only be requested (and eventually granted) when the user is authenticating to the account console. Once authenticated, the granted scopes won't change
until re-authenticating.

=== Configuring ways to sign in

You can sign in to this console using basic authentication (a login name and password) or two-factor authentication. For two-factor authentication, use one of the following procedures.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,10 @@ private Response redirectToLogin(String path) {
throw new ServerErrorException(Status.INTERNAL_SERVER_ERROR);
}
}
String scope = queryParameters.getFirst(OIDCLoginProtocol.SCOPE_PARAM);
if (StringUtil.isNotBlank(scope)) {
uriBuilder.queryParam(OIDCLoginProtocol.SCOPE_PARAM, scope);
}
}

URI url = uriBuilder.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
package org.keycloak.testsuite.account;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URLDecoder;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.HttpClientBuilder;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Test;
import org.keycloak.models.Constants;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.util.OAuthClient;

public class AccountConsoleTest extends AbstractTestRealmKeycloakTest {

Expand All @@ -22,23 +28,53 @@ public void configureTestRealm(RealmRepresentation testRealm) {
}

@Test
public void redirectToLoginIfNotAuthenticated() throws Exception {
public void redirectToLoginIfNotAuthenticated() {
assertRedirectLocation(getAccount());
}

@Test
public void testScopesPresentInAuthorizationRequest() {
String expectedScopes = "phone address";
String redirectLocation = URLDecoder.decode(assertRedirectLocation(getAccount(expectedScopes)));
Assert.assertTrue(redirectLocation.contains(expectedScopes));
expectedScopes = "phone";
redirectLocation = URLDecoder.decode(assertRedirectLocation(getAccount(expectedScopes)));
Assert.assertTrue(redirectLocation.contains(expectedScopes));
Assert.assertTrue(!redirectLocation.contains("address"));
}

String accountUrl = oauth.getCurrentUri().toString().replace("/admin/master/console", "/realms/" + oauth.getRealm() + "/account");
private CloseableHttpResponse getAccount() {
return getAccount(null);
}

HttpGet getAccount = new HttpGet(accountUrl);
private CloseableHttpResponse getAccount(String scope) {
try {
var uriBuilder = new URIBuilder(suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth/realms/test/account");

int statusCode;
String redirectLocation;
try (var client = HttpClientBuilder.create().disableRedirectHandling().build()) {
try (var response = client.execute(getAccount)) {
statusCode = response.getStatusLine().getStatusCode();
redirectLocation = response.getFirstHeader("Location").getValue();
if (scope != null) {
uriBuilder.setParameter(OIDCLoginProtocol.SCOPE_PARAM, scope);
}

var request = new HttpGet(uriBuilder.build());

try (var client = HttpClientBuilder.create().disableRedirectHandling().build()) {
return client.execute(request);
}
} catch (URISyntaxException | IOException e) {
throw new RuntimeException(e);
}
}

Assert.assertEquals(302, statusCode);
String expectedLoginUrlPart = "/realms/" + oauth.getRealm() + "/protocol/openid-connect/auth?client_id=account";
Assert.assertTrue(redirectLocation.contains(expectedLoginUrlPart));
private String assertRedirectLocation(CloseableHttpResponse Account) {
try (var response = Account) {
int statusCode = response.getStatusLine().getStatusCode();
Assert.assertEquals(302, statusCode);
String expectedLoginUrlPart = "/realms/" + oauth.getRealm() + "/protocol/openid-connect/auth?client_id=" + Constants.ACCOUNT_CONSOLE_CLIENT_ID;
String redirectLocation = response.getFirstHeader("Location").getValue();
Assert.assertTrue(redirectLocation.contains(expectedLoginUrlPart));
return redirectLocation;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

0 comments on commit 4dfa224

Please sign in to comment.