Device verification flow always requires consent
Force consent for device verification flow when there are no client scopes to approve by adding a default client scope to approve Closes #26100 Signed-off-by: graziang <g.graziano94@gmail.com>
This commit is contained in:
parent
662ab9811b
commit
4fa940a31e
2 changed files with 47 additions and 4 deletions
|
@ -1058,7 +1058,7 @@ public class AuthenticationManager {
|
|||
UserConsentModel grantedConsent = getEffectiveGrantedConsent(session, authSession);
|
||||
|
||||
// See if any clientScopes need to be approved on consent screen
|
||||
List<AuthorizationDetails> clientScopesToApprove = getClientScopesToApproveOnConsentScreen(grantedConsent, session);
|
||||
List<AuthorizationDetails> clientScopesToApprove = getClientScopesToApproveOnConsentScreen(grantedConsent, session, authSession);
|
||||
if (!clientScopesToApprove.isEmpty()) {
|
||||
return CommonClientSessionModel.Action.OAUTH_GRANT.name();
|
||||
}
|
||||
|
@ -1122,7 +1122,7 @@ public class AuthenticationManager {
|
|||
|
||||
UserConsentModel grantedConsent = getEffectiveGrantedConsent(session, authSession);
|
||||
|
||||
List<AuthorizationDetails> clientScopesToApprove = getClientScopesToApproveOnConsentScreen(grantedConsent, session);
|
||||
List<AuthorizationDetails> clientScopesToApprove = getClientScopesToApproveOnConsentScreen(grantedConsent, session, authSession);
|
||||
|
||||
// Skip grant screen if everything was already approved by this user
|
||||
if (clientScopesToApprove.size() > 0) {
|
||||
|
@ -1149,7 +1149,7 @@ public class AuthenticationManager {
|
|||
|
||||
}
|
||||
|
||||
private static List<AuthorizationDetails> getClientScopesToApproveOnConsentScreen(UserConsentModel grantedConsent, KeycloakSession session) {
|
||||
private static List<AuthorizationDetails> getClientScopesToApproveOnConsentScreen(UserConsentModel grantedConsent, KeycloakSession session, AuthenticationSessionModel authSession) {
|
||||
// Client Scopes to be displayed on consent screen
|
||||
List<AuthorizationDetails> clientScopesToDisplay = new LinkedList<>();
|
||||
|
||||
|
@ -1165,6 +1165,10 @@ public class AuthenticationManager {
|
|||
clientScopesToDisplay.add(authDetails);
|
||||
}
|
||||
}
|
||||
//force consent when running a verification flow of OAuth 2.0 Device Authorization Grant
|
||||
if(clientScopesToDisplay.isEmpty() && isOAuth2DeviceVerificationFlow(authSession)) {
|
||||
clientScopesToDisplay.add(new AuthorizationDetails(authSession.getClient()));
|
||||
}
|
||||
|
||||
return clientScopesToDisplay;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.junit.Test;
|
|||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.admin.client.resource.ClientsResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.events.Errors;
|
||||
|
@ -42,6 +43,7 @@ import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentatio
|
|||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.UserInfo;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.ClientScopeRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||
|
@ -83,6 +85,7 @@ public class OAuth2DeviceAuthorizationGrantTest extends AbstractKeycloakTest {
|
|||
private static final String DEVICE_APP = "test-device";
|
||||
private static final String DEVICE_APP_PUBLIC = "test-device-public";
|
||||
private static final String DEVICE_APP_PUBLIC_CUSTOM_CONSENT = "test-device-public-custom-consent";
|
||||
private static final String DEVICE_APP_WITHOUT_SCOPES = "test-device-without-scopes";
|
||||
private static final String SHORT_DEVICE_FLOW_URL = "https://keycloak.org/device";
|
||||
|
||||
@Rule
|
||||
|
@ -105,7 +108,7 @@ public class OAuth2DeviceAuthorizationGrantTest extends AbstractKeycloakTest {
|
|||
|
||||
ClientRepresentation app = ClientBuilder.create()
|
||||
.id(KeycloakModelUtils.generateId())
|
||||
.clientId("test-device")
|
||||
.clientId(DEVICE_APP)
|
||||
.secret("secret")
|
||||
.attribute(OAuth2DeviceConfig.OAUTH2_DEVICE_AUTHORIZATION_GRANT_ENABLED, "true")
|
||||
.build();
|
||||
|
@ -125,6 +128,13 @@ public class OAuth2DeviceAuthorizationGrantTest extends AbstractKeycloakTest {
|
|||
.build();
|
||||
realm.client(appPublicCustomConsent);
|
||||
|
||||
ClientRepresentation appWithoutScopes = ClientBuilder.create().publicClient()
|
||||
.id(KeycloakModelUtils.generateId())
|
||||
.clientId(DEVICE_APP_WITHOUT_SCOPES)
|
||||
.attribute(OAuth2DeviceConfig.OAUTH2_DEVICE_AUTHORIZATION_GRANT_ENABLED, "true")
|
||||
.build();
|
||||
realm.client(appWithoutScopes);
|
||||
|
||||
userId = KeycloakModelUtils.generateId();
|
||||
UserRepresentation user = UserBuilder.create()
|
||||
.id(userId)
|
||||
|
@ -478,6 +488,35 @@ public class OAuth2DeviceAuthorizationGrantTest extends AbstractKeycloakTest {
|
|||
assertNotNull(token);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPublicClientConsentWithoutScopes() throws Exception {
|
||||
|
||||
ClientsResource clients = realmsResouce().realm(REALM_NAME).clients();
|
||||
ClientRepresentation clientRep = clients.findByClientId(DEVICE_APP_WITHOUT_SCOPES).get(0);
|
||||
ClientResource client = clients.get(clientRep.getId());
|
||||
|
||||
List<ClientScopeRepresentation> defaultClientScopes = client.getDefaultClientScopes();
|
||||
defaultClientScopes.forEach(scope -> client.removeDefaultClientScope(scope.getId()));
|
||||
|
||||
List<ClientScopeRepresentation> optionalClientScopes = client.getOptionalClientScopes();
|
||||
optionalClientScopes.forEach(scope -> client.removeOptionalClientScope(scope.getId()));
|
||||
|
||||
// Device Authorization Request from device
|
||||
oauth.realm(REALM_NAME);
|
||||
oauth.clientId(DEVICE_APP_WITHOUT_SCOPES);
|
||||
OAuthClient.DeviceAuthorizationResponse response = oauth.doDeviceAuthorizationRequest(DEVICE_APP_WITHOUT_SCOPES, null);
|
||||
|
||||
Assert.assertEquals(200, response.getStatusCode());
|
||||
assertNotNull(response.getVerificationUriComplete());
|
||||
openVerificationPage(response.getVerificationUriComplete());
|
||||
loginPage.assertCurrent();
|
||||
|
||||
// Do Login
|
||||
oauth.fillLoginForm("device-login", "password");
|
||||
// Consent
|
||||
grantPage.assertCurrent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoRefreshToken() throws Exception {
|
||||
ClientResource client = ApiUtil.findClientByClientId(adminClient.realm(REALM_NAME), DEVICE_APP);
|
||||
|
|
Loading…
Reference in a new issue