KEYCLOAK-19237 Avoid using stream that has been operated
This commit is contained in:
parent
10c3e149d3
commit
884471c729
2 changed files with 48 additions and 3 deletions
|
@ -273,15 +273,14 @@ public class DeviceGrantType {
|
||||||
// Compute client scopes again from scope parameter. Check if user still has them granted
|
// Compute client scopes again from scope parameter. Check if user still has them granted
|
||||||
// (but in device_code-to-token request, it could just theoretically happen that they are not available)
|
// (but in device_code-to-token request, it could just theoretically happen that they are not available)
|
||||||
String scopeParam = deviceCodeModel.getScope();
|
String scopeParam = deviceCodeModel.getScope();
|
||||||
Stream<ClientScopeModel> clientScopes = TokenManager.getRequestedClientScopes(scopeParam, client);
|
if (!TokenManager.verifyConsentStillAvailable(session, user, client, TokenManager.getRequestedClientScopes(scopeParam, client))) {
|
||||||
if (!TokenManager.verifyConsentStillAvailable(session, user, client, clientScopes)) {
|
|
||||||
event.error(Errors.NOT_ALLOWED);
|
event.error(Errors.NOT_ALLOWED);
|
||||||
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_SCOPE,
|
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_SCOPE,
|
||||||
"Client no longer has requested consent from user", Response.Status.BAD_REQUEST);
|
"Client no longer has requested consent from user", Response.Status.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndClientScopes(clientSession,
|
ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndClientScopes(clientSession,
|
||||||
clientScopes, session);
|
TokenManager.getRequestedClientScopes(scopeParam, client), session);
|
||||||
|
|
||||||
// Set nonce as an attribute in the ClientSessionContext. Will be used for the token generation
|
// Set nonce as an attribute in the ClientSessionContext. Will be used for the token generation
|
||||||
clientSessionCtx.setAttribute(OIDCLoginProtocol.NONCE_PARAM, deviceCodeModel.getNonce());
|
clientSessionCtx.setAttribute(OIDCLoginProtocol.NONCE_PARAM, deviceCodeModel.getNonce());
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.admin.client.resource.ClientResource;
|
import org.keycloak.admin.client.resource.ClientResource;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
|
import org.keycloak.models.ClientScopeModel;
|
||||||
import org.keycloak.models.OAuth2DeviceConfig;
|
import org.keycloak.models.OAuth2DeviceConfig;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
|
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
|
||||||
|
@ -59,6 +60,7 @@ public class OAuth2DeviceAuthorizationGrantTest extends AbstractKeycloakTest {
|
||||||
public static final String REALM_NAME = "test";
|
public static final String REALM_NAME = "test";
|
||||||
public static final String DEVICE_APP = "test-device";
|
public static final String DEVICE_APP = "test-device";
|
||||||
public static final String DEVICE_APP_PUBLIC = "test-device-public";
|
public static final String DEVICE_APP_PUBLIC = "test-device-public";
|
||||||
|
public static final String DEVICE_APP_PUBLIC_CUSTOM_CONSENT = "test-device-public-custom-consent";
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public AssertEvents events = new AssertEvents(this);
|
public AssertEvents events = new AssertEvents(this);
|
||||||
|
@ -93,6 +95,14 @@ public class OAuth2DeviceAuthorizationGrantTest extends AbstractKeycloakTest {
|
||||||
.build();
|
.build();
|
||||||
realm.client(appPublic);
|
realm.client(appPublic);
|
||||||
|
|
||||||
|
ClientRepresentation appPublicCustomConsent = ClientBuilder.create().id(KeycloakModelUtils.generateId()).publicClient()
|
||||||
|
.clientId(DEVICE_APP_PUBLIC_CUSTOM_CONSENT).attribute(OAuth2DeviceConfig.OAUTH2_DEVICE_AUTHORIZATION_GRANT_ENABLED, "true")
|
||||||
|
.consentRequired(true)
|
||||||
|
.attribute(ClientScopeModel.DISPLAY_ON_CONSENT_SCREEN, "true")
|
||||||
|
.attribute(ClientScopeModel.CONSENT_SCREEN_TEXT, "This is the custom consent screen text.")
|
||||||
|
.build();
|
||||||
|
realm.client(appPublicCustomConsent);
|
||||||
|
|
||||||
userId = KeycloakModelUtils.generateId();
|
userId = KeycloakModelUtils.generateId();
|
||||||
UserRepresentation user = UserBuilder.create()
|
UserRepresentation user = UserBuilder.create()
|
||||||
.id(userId)
|
.id(userId)
|
||||||
|
@ -192,6 +202,42 @@ public class OAuth2DeviceAuthorizationGrantTest extends AbstractKeycloakTest {
|
||||||
assertNotNull(token);
|
assertNotNull(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPublicClientCustomConsent() throws Exception {
|
||||||
|
// Device Authorization Request from device
|
||||||
|
oauth.realm(REALM_NAME);
|
||||||
|
oauth.clientId(DEVICE_APP_PUBLIC_CUSTOM_CONSENT);
|
||||||
|
OAuthClient.DeviceAuthorizationResponse response = oauth.doDeviceAuthorizationRequest(DEVICE_APP_PUBLIC_CUSTOM_CONSENT, null);
|
||||||
|
|
||||||
|
Assert.assertEquals(200, response.getStatusCode());
|
||||||
|
assertNotNull(response.getDeviceCode());
|
||||||
|
assertNotNull(response.getUserCode());
|
||||||
|
assertNotNull(response.getVerificationUri());
|
||||||
|
assertNotNull(response.getVerificationUriComplete());
|
||||||
|
Assert.assertEquals(60, response.getExpiresIn());
|
||||||
|
Assert.assertEquals(5, response.getInterval());
|
||||||
|
|
||||||
|
openVerificationPage(response.getVerificationUriComplete());
|
||||||
|
|
||||||
|
// Do Login
|
||||||
|
oauth.fillLoginForm("device-login", "password");
|
||||||
|
|
||||||
|
// Consent
|
||||||
|
Assert.assertTrue(grantPage.getDisplayedGrants().contains("This is the custom consent screen text."));
|
||||||
|
grantPage.accept();
|
||||||
|
|
||||||
|
// Token request from device
|
||||||
|
OAuthClient.AccessTokenResponse tokenResponse = oauth.doDeviceTokenRequest(DEVICE_APP_PUBLIC_CUSTOM_CONSENT, null, response.getDeviceCode());
|
||||||
|
|
||||||
|
Assert.assertEquals(200, tokenResponse.getStatusCode());
|
||||||
|
|
||||||
|
String tokenString = tokenResponse.getAccessToken();
|
||||||
|
assertNotNull(tokenString);
|
||||||
|
AccessToken token = oauth.verifyToken(tokenString);
|
||||||
|
|
||||||
|
assertNotNull(token);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoRefreshToken() throws Exception {
|
public void testNoRefreshToken() throws Exception {
|
||||||
ClientResource client = ApiUtil.findClientByClientId(adminClient.realm(REALM_NAME), DEVICE_APP);
|
ClientResource client = ApiUtil.findClientByClientId(adminClient.realm(REALM_NAME), DEVICE_APP);
|
||||||
|
|
Loading…
Reference in a new issue