Search in sources :

Example 6 with OAuth2DeviceCodeModel

use of org.keycloak.models.OAuth2DeviceCodeModel in project keycloak by keycloak.

the class BackchannelAuthenticationCallbackEndpoint method verifyAuthenticationRequest.

private BackchannelAuthCallbackContext verifyAuthenticationRequest(HttpHeaders headers) {
    String rawBearerToken = AppAuthManager.extractAuthorizationHeaderTokenOrReturnNull(headers);
    if (rawBearerToken == null) {
        throw new ErrorResponseException(OAuthErrorException.INVALID_TOKEN, "Invalid token", Response.Status.UNAUTHORIZED);
    }
    AccessToken bearerToken;
    try {
        bearerToken = TokenVerifier.createWithoutSignature(session.tokens().decode(rawBearerToken, AccessToken.class)).withDefaultChecks().realmUrl(Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName())).checkActive(true).audience(Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName())).verify().getToken();
    } catch (Exception e) {
        event.error(Errors.INVALID_TOKEN);
        // authentication channel id format is invalid or it has already been used
        throw new ErrorResponseException(OAuthErrorException.INVALID_TOKEN, "Invalid token", Response.Status.FORBIDDEN);
    }
    OAuth2DeviceTokenStoreProvider store = session.getProvider(OAuth2DeviceTokenStoreProvider.class);
    OAuth2DeviceCodeModel deviceCode = store.getByUserCode(realm, bearerToken.getId());
    if (deviceCode == null) {
        throw new ErrorResponseException(OAuthErrorException.INVALID_TOKEN, "Invalid token", Response.Status.FORBIDDEN);
    }
    if (!deviceCode.isPending()) {
        cancelRequest(bearerToken.getId());
        throw new ErrorResponseException(OAuthErrorException.INVALID_TOKEN, "Invalid token", Response.Status.FORBIDDEN);
    }
    ClientModel issuedFor = realm.getClientByClientId(bearerToken.getIssuedFor());
    if (issuedFor == null || !issuedFor.isEnabled()) {
        throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Invalid token recipient", Response.Status.BAD_REQUEST);
    }
    if (!deviceCode.getClientId().equals(issuedFor.getClientId())) {
        throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Token recipient mismatch", Response.Status.BAD_REQUEST);
    }
    session.getContext().setClient(issuedFor);
    event.client(issuedFor);
    return new BackchannelAuthCallbackContext(bearerToken, deviceCode);
}
Also used : OAuth2DeviceTokenStoreProvider(org.keycloak.models.OAuth2DeviceTokenStoreProvider) ClientModel(org.keycloak.models.ClientModel) OAuth2DeviceCodeModel(org.keycloak.models.OAuth2DeviceCodeModel) AccessToken(org.keycloak.representations.AccessToken) ErrorResponseException(org.keycloak.services.ErrorResponseException) OAuthErrorException(org.keycloak.OAuthErrorException) ErrorResponseException(org.keycloak.services.ErrorResponseException) IOException(java.io.IOException)

Example 7 with OAuth2DeviceCodeModel

use of org.keycloak.models.OAuth2DeviceCodeModel in project keycloak by keycloak.

the class BackchannelAuthenticationCallbackEndpoint method processAuthenticationChannelResult.

@Path("/")
@POST
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response processAuthenticationChannelResult(AuthenticationChannelResponse response) {
    event.event(EventType.LOGIN);
    BackchannelAuthCallbackContext ctx = verifyAuthenticationRequest(httpRequest.getHttpHeaders());
    AccessToken bearerToken = ctx.bearerToken;
    OAuth2DeviceCodeModel deviceModel = ctx.deviceModel;
    Status status = response.getStatus();
    if (status == null) {
        event.error(Errors.INVALID_REQUEST);
        throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Invalid authentication status", Response.Status.BAD_REQUEST);
    }
    switch(status) {
        case SUCCEED:
            approveRequest(bearerToken, response.getAdditionalParams());
            break;
        case CANCELLED:
        case UNAUTHORIZED:
            denyRequest(bearerToken, status);
            break;
    }
    // Call the notification endpoint
    ClientModel client = session.getContext().getClient();
    CibaConfig cibaConfig = realm.getCibaPolicy();
    if (cibaConfig.getBackchannelTokenDeliveryMode(client).equals(CibaConfig.CIBA_PING_MODE)) {
        sendClientNotificationRequest(client, cibaConfig, deviceModel);
    }
    return Response.ok(MediaType.APPLICATION_JSON_TYPE).build();
}
Also used : Status(org.keycloak.protocol.oidc.grants.ciba.channel.AuthenticationChannelResponse.Status) ClientModel(org.keycloak.models.ClientModel) OAuth2DeviceCodeModel(org.keycloak.models.OAuth2DeviceCodeModel) AccessToken(org.keycloak.representations.AccessToken) CibaConfig(org.keycloak.models.CibaConfig) ErrorResponseException(org.keycloak.services.ErrorResponseException) Path(javax.ws.rs.Path) POST(javax.ws.rs.POST) Consumes(javax.ws.rs.Consumes) Produces(javax.ws.rs.Produces) NoCache(org.jboss.resteasy.annotations.cache.NoCache)

Example 8 with OAuth2DeviceCodeModel

use of org.keycloak.models.OAuth2DeviceCodeModel in project keycloak by keycloak.

the class InfinispanOAuth2DeviceTokenStoreProvider method deny.

@Override
public boolean deny(RealmModel realm, String userCode) {
    try {
        OAuth2DeviceCodeModel deviceCode = findDeviceCodeByUserCode(realm, userCode);
        if (deviceCode == null) {
            return false;
        }
        OAuth2DeviceCodeModel denied = deviceCode.deny();
        BasicCache<String, ActionTokenValueEntity> cache = codeCache.get();
        cache.replace(denied.serializeKey(), new ActionTokenValueEntity(denied.toMap()));
        return true;
    } catch (HotRodClientException re) {
        // In case of lock conflict, we don't want to retry anyway as there was likely an attempt to remove the code from different place.
        if (logger.isDebugEnabled()) {
            logger.debugf(re, "Failed when denying device user code %s", userCode);
        }
        return false;
    }
}
Also used : ActionTokenValueEntity(org.keycloak.models.sessions.infinispan.entities.ActionTokenValueEntity) OAuth2DeviceCodeModel(org.keycloak.models.OAuth2DeviceCodeModel) HotRodClientException(org.infinispan.client.hotrod.exceptions.HotRodClientException)

Example 9 with OAuth2DeviceCodeModel

use of org.keycloak.models.OAuth2DeviceCodeModel in project keycloak by keycloak.

the class InfinispanOAuth2DeviceTokenStoreProvider method approve.

@Override
public boolean approve(RealmModel realm, String userCode, String userSessionId, Map<String, String> additionalParams) {
    try {
        OAuth2DeviceCodeModel deviceCode = findDeviceCodeByUserCode(realm, userCode);
        if (deviceCode == null) {
            return false;
        }
        OAuth2DeviceCodeModel approved = deviceCode.approve(userSessionId, additionalParams);
        // Update the device code with approved status
        BasicCache<String, ActionTokenValueEntity> cache = codeCache.get();
        cache.replace(approved.serializeKey(), new ActionTokenValueEntity(approved.toMap()));
        return true;
    } catch (HotRodClientException re) {
        // In case of lock conflict, we don't want to retry anyway as there was likely an attempt to remove the code from different place.
        if (logger.isDebugEnabled()) {
            logger.debugf(re, "Failed when verifying device user code %s", userCode);
        }
        return false;
    }
}
Also used : ActionTokenValueEntity(org.keycloak.models.sessions.infinispan.entities.ActionTokenValueEntity) OAuth2DeviceCodeModel(org.keycloak.models.OAuth2DeviceCodeModel) HotRodClientException(org.infinispan.client.hotrod.exceptions.HotRodClientException)

Example 10 with OAuth2DeviceCodeModel

use of org.keycloak.models.OAuth2DeviceCodeModel in project keycloak by keycloak.

the class BackchannelAuthenticationEndpoint method storeAuthenticationRequest.

/**
 * TODO: Leverage the device code storage for tracking authentication requests. Not sure if we need a specific storage,
 * but probably make the {@link OAuth2DeviceTokenStoreProvider} more generic for ciba, device, or any other use case
 * that relies on cross-references for unsolicited user authentication requests from devices.
 */
private void storeAuthenticationRequest(CIBAAuthenticationRequest request, CibaConfig cibaConfig, String authReqId) {
    ClientModel client = request.getClient();
    int expiresIn = cibaConfig.getExpiresIn();
    int poolingInterval = cibaConfig.getPoolingInterval();
    String cibaMode = cibaConfig.getBackchannelTokenDeliveryMode(client);
    // Set authReqId just for the ping mode as it is relatively big and not necessarily needed in the infinispan cache for the "poll" mode
    if (!CibaConfig.CIBA_PING_MODE.equals(cibaMode)) {
        authReqId = null;
    }
    OAuth2DeviceCodeModel deviceCode = OAuth2DeviceCodeModel.create(realm, client, request.getId(), request.getScope(), null, expiresIn, poolingInterval, request.getClientNotificationToken(), authReqId, Collections.emptyMap(), null, null);
    String authResultId = request.getAuthResultId();
    OAuth2DeviceUserCodeModel userCode = new OAuth2DeviceUserCodeModel(realm, deviceCode.getDeviceCode(), authResultId);
    // To inform "expired_token" to the client, the lifespan of the cache provider is longer than device code
    int lifespanSeconds = expiresIn + poolingInterval + 10;
    OAuth2DeviceTokenStoreProvider store = session.getProvider(OAuth2DeviceTokenStoreProvider.class);
    store.put(deviceCode, userCode, lifespanSeconds);
}
Also used : OAuth2DeviceTokenStoreProvider(org.keycloak.models.OAuth2DeviceTokenStoreProvider) ClientModel(org.keycloak.models.ClientModel) OAuth2DeviceCodeModel(org.keycloak.models.OAuth2DeviceCodeModel) OAuth2DeviceUserCodeModel(org.keycloak.models.OAuth2DeviceUserCodeModel)

Aggregations

OAuth2DeviceCodeModel (org.keycloak.models.OAuth2DeviceCodeModel)10 OAuth2DeviceTokenStoreProvider (org.keycloak.models.OAuth2DeviceTokenStoreProvider)7 ClientModel (org.keycloak.models.ClientModel)4 ErrorResponseException (org.keycloak.services.ErrorResponseException)4 OAuthErrorException (org.keycloak.OAuthErrorException)3 ClientPolicyException (org.keycloak.services.clientpolicy.ClientPolicyException)3 Consumes (javax.ws.rs.Consumes)2 POST (javax.ws.rs.POST)2 Path (javax.ws.rs.Path)2 Produces (javax.ws.rs.Produces)2 HotRodClientException (org.infinispan.client.hotrod.exceptions.HotRodClientException)2 ClientSessionContext (org.keycloak.models.ClientSessionContext)2 OAuth2DeviceUserCodeModel (org.keycloak.models.OAuth2DeviceUserCodeModel)2 OAuth2DeviceUserCodeProvider (org.keycloak.models.OAuth2DeviceUserCodeProvider)2 UserModel (org.keycloak.models.UserModel)2 UserSessionModel (org.keycloak.models.UserSessionModel)2 ActionTokenValueEntity (org.keycloak.models.sessions.infinispan.entities.ActionTokenValueEntity)2 AccessToken (org.keycloak.representations.AccessToken)2 CorsErrorResponseException (org.keycloak.services.CorsErrorResponseException)2 DefaultClientSessionContext (org.keycloak.services.util.DefaultClientSessionContext)2