use of org.keycloak.protocol.oidc.grants.device.clientpolicy.context.DeviceTokenRequestContext in project keycloak by keycloak.
the class DeviceGrantType method oauth2DeviceFlow.
public Response oauth2DeviceFlow() {
if (!realm.getOAuth2DeviceConfig().isOAuth2DeviceAuthorizationGrantEnabled(client)) {
event.error(Errors.NOT_ALLOWED);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Client not allowed OAuth 2.0 Device Authorization Grant", Response.Status.BAD_REQUEST);
}
String deviceCode = formParams.getFirst(OAuth2Constants.DEVICE_CODE);
if (deviceCode == null) {
event.error(Errors.INVALID_OAUTH2_DEVICE_CODE);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Missing parameter: " + OAuth2Constants.DEVICE_CODE, Response.Status.BAD_REQUEST);
}
OAuth2DeviceTokenStoreProvider store = session.getProvider(OAuth2DeviceTokenStoreProvider.class);
OAuth2DeviceCodeModel deviceCodeModel = store.getByDeviceCode(realm, deviceCode);
if (deviceCodeModel == null) {
event.error(Errors.INVALID_OAUTH2_DEVICE_CODE);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Device code not valid", Response.Status.BAD_REQUEST);
}
if (deviceCodeModel.isExpired()) {
event.error(Errors.EXPIRED_OAUTH2_DEVICE_CODE);
throw new CorsErrorResponseException(cors, OAuthErrorException.EXPIRED_TOKEN, "Device code is expired", Response.Status.BAD_REQUEST);
}
if (!store.isPollingAllowed(deviceCodeModel)) {
event.error(Errors.SLOW_DOWN);
throw new CorsErrorResponseException(cors, OAuthErrorException.SLOW_DOWN, "Slow down", Response.Status.BAD_REQUEST);
}
if (deviceCodeModel.isDenied()) {
event.error(Errors.ACCESS_DENIED);
throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "The end user denied the authorization request", Response.Status.BAD_REQUEST);
}
if (deviceCodeModel.isPending()) {
throw new CorsErrorResponseException(cors, OAuthErrorException.AUTHORIZATION_PENDING, "The authorization request is still pending", Response.Status.BAD_REQUEST);
}
// https://tools.ietf.org/html/rfc7636#section-4.6
String codeVerifier = formParams.getFirst(OAuth2Constants.CODE_VERIFIER);
String codeChallenge = deviceCodeModel.getCodeChallenge();
String codeChallengeMethod = deviceCodeModel.getCodeChallengeMethod();
if (codeChallengeMethod != null && !codeChallengeMethod.isEmpty()) {
PkceUtils.checkParamsForPkceEnforcedClient(codeVerifier, codeChallenge, codeChallengeMethod, null, null, event, cors);
} else {
// PKCE Activation is OFF, execute the codes implemented in KEYCLOAK-2604
PkceUtils.checkParamsForPkceNotEnforcedClient(codeVerifier, codeChallenge, codeChallengeMethod, null, null, event, cors);
}
// Approved
String userSessionId = deviceCodeModel.getUserSessionId();
event.detail(Details.CODE_ID, userSessionId);
event.session(userSessionId);
// Retrieve UserSession
UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, userSessionId, client.getId());
if (userSession == null) {
userSession = session.sessions().getUserSession(realm, userSessionId);
if (userSession == null) {
throw new CorsErrorResponseException(cors, OAuthErrorException.AUTHORIZATION_PENDING, "The authorization request is verified but can not lookup the user session yet", Response.Status.BAD_REQUEST);
}
}
// Now, remove the device code
store.removeDeviceCode(realm, deviceCode);
UserModel user = userSession.getUser();
if (user == null) {
event.error(Errors.USER_NOT_FOUND);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "User not found", Response.Status.BAD_REQUEST);
}
event.user(userSession.getUser());
if (!user.isEnabled()) {
event.error(Errors.USER_DISABLED);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "User disabled", Response.Status.BAD_REQUEST);
}
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
if (!client.getClientId().equals(clientSession.getClient().getClientId())) {
event.error(Errors.INVALID_CODE);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Auth error", Response.Status.BAD_REQUEST);
}
if (!AuthenticationManager.isSessionValid(realm, userSession)) {
event.error(Errors.USER_SESSION_NOT_FOUND);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Session not active", Response.Status.BAD_REQUEST);
}
try {
session.clientPolicy().triggerOnEvent(new DeviceTokenRequestContext(deviceCodeModel, formParams));
} catch (ClientPolicyException cpe) {
event.error(cpe.getError());
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, cpe.getErrorDetail(), Response.Status.BAD_REQUEST);
}
// 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)
String scopeParam = deviceCodeModel.getScope();
if (!TokenManager.verifyConsentStillAvailable(session, user, client, TokenManager.getRequestedClientScopes(scopeParam, client))) {
event.error(Errors.NOT_ALLOWED);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_SCOPE, "Client no longer has requested consent from user", Response.Status.BAD_REQUEST);
}
ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndScopeParameter(clientSession, scopeParam, session);
// Set nonce as an attribute in the ClientSessionContext. Will be used for the token generation
clientSessionCtx.setAttribute(OIDCLoginProtocol.NONCE_PARAM, deviceCodeModel.getNonce());
return tokenEndpoint.createTokenResponse(user, userSession, clientSessionCtx, scopeParam, false);
}
Aggregations