use of org.keycloak.protocol.oidc.grants.ciba.channel.CIBAAuthenticationRequest in project keycloak by keycloak.
the class CibaGrantType method cibaGrant.
public Response cibaGrant() {
ProfileHelper.requireFeature(Profile.Feature.CIBA);
if (!realm.getCibaPolicy().isOIDCCIBAGrantEnabled(client)) {
event.error(Errors.NOT_ALLOWED);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Client not allowed OIDC CIBA Grant", Response.Status.BAD_REQUEST);
}
String jwe = formParams.getFirst(AUTH_REQ_ID);
if (jwe == null) {
event.error(Errors.INVALID_CODE);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Missing parameter: " + AUTH_REQ_ID, Response.Status.BAD_REQUEST);
}
logger.tracev("CIBA Grant :: authReqId = {0}", jwe);
CIBAAuthenticationRequest request;
try {
request = CIBAAuthenticationRequest.deserialize(session, jwe);
} catch (Exception e) {
logger.warnf("illegal format of auth_req_id : e.getMessage() = %s", e.getMessage());
// Auth Req ID has not put onto cache, no need to remove Auth Req ID.
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Invalid Auth Req ID", Response.Status.BAD_REQUEST);
}
request.setClient(client);
try {
session.clientPolicy().triggerOnEvent(new BackchannelTokenRequestContext(request, formParams));
} catch (ClientPolicyException cpe) {
event.error(cpe.getError());
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, cpe.getErrorDetail(), Response.Status.BAD_REQUEST);
}
OAuth2DeviceTokenStoreProvider store = session.getProvider(OAuth2DeviceTokenStoreProvider.class);
OAuth2DeviceCodeModel deviceCode = store.getByDeviceCode(realm, request.getId());
if (deviceCode == null) {
// Auth Req ID has not put onto cache, no need to remove Auth Req ID.
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Invalid " + AUTH_REQ_ID, Response.Status.BAD_REQUEST);
}
if (!request.getIssuedFor().equals(client.getClientId())) {
logDebug("invalid client.", request);
// the client sending this Auth Req ID does not match the client to which keycloak had issued Auth Req ID.
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "unauthorized client", Response.Status.BAD_REQUEST);
}
if (deviceCode.isExpired()) {
logDebug("expired.", request);
throw new CorsErrorResponseException(cors, OAuthErrorException.EXPIRED_TOKEN, "authentication timed out", Response.Status.BAD_REQUEST);
}
if (!store.isPollingAllowed(deviceCode)) {
logDebug("pooling.", request);
throw new CorsErrorResponseException(cors, OAuthErrorException.SLOW_DOWN, "too early to access", Response.Status.BAD_REQUEST);
}
if (deviceCode.isDenied()) {
logDebug("denied.", request);
throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "not authorized", Response.Status.BAD_REQUEST);
}
// get corresponding Authentication Channel Result entry
if (deviceCode.isPending()) {
logDebug("not yet authenticated by Authentication Device or auth_req_id has already been used to get tokens.", request);
throw new CorsErrorResponseException(cors, OAuthErrorException.AUTHORIZATION_PENDING, "The authorization request is still pending as the end-user hasn't yet been authenticated.", Response.Status.BAD_REQUEST);
}
UserSessionModel userSession = createUserSession(request, deviceCode.getAdditionalParams());
UserModel user = userSession.getUser();
store.removeDeviceCode(realm, request.getId());
// Compute client scopes again from scope parameter. Check if user still has them granted
// (but in code-to-token request, it could just theoretically happen that they are not available)
String scopeParam = request.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(userSession.getAuthenticatedClientSessionByClient(client.getId()), scopeParam, session);
int authTime = Time.currentTime();
userSession.setNote(AuthenticationManager.AUTH_TIME, String.valueOf(authTime));
return tokenEndpoint.createTokenResponse(user, userSession, clientSessionCtx, scopeParam, true);
}
use of org.keycloak.protocol.oidc.grants.ciba.channel.CIBAAuthenticationRequest in project keycloak by keycloak.
the class BackchannelAuthenticationEndpoint method authorizeClient.
private CIBAAuthenticationRequest authorizeClient(MultivaluedMap<String, String> params) {
ClientModel client = null;
try {
client = authenticateClient();
} catch (WebApplicationException wae) {
OAuth2ErrorRepresentation errorRep = (OAuth2ErrorRepresentation) wae.getResponse().getEntity();
throw new ErrorResponseException(errorRep.getError(), errorRep.getErrorDescription(), Response.Status.UNAUTHORIZED);
}
BackchannelAuthenticationEndpointRequest endpointRequest = BackchannelAuthenticationEndpointRequestParserProcessor.parseRequest(event, session, client, params, realm.getCibaPolicy());
UserModel user = resolveUser(endpointRequest, realm.getCibaPolicy().getAuthRequestedUserHint());
CIBAAuthenticationRequest request = new CIBAAuthenticationRequest(session, user, client);
request.setClient(client);
String scope = endpointRequest.getScope();
if (scope == null) {
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "missing parameter : scope", Response.Status.BAD_REQUEST);
}
request.setScope(scope);
// optional parameters
if (endpointRequest.getBindingMessage() != null) {
validateBindingMessage(endpointRequest.getBindingMessage());
request.setBindingMessage(endpointRequest.getBindingMessage());
}
if (endpointRequest.getAcr() != null)
request.setAcrValues(endpointRequest.getAcr());
CibaConfig policy = realm.getCibaPolicy();
// create JWE encoded auth_req_id from Auth Req ID.
Integer expiresIn = Optional.ofNullable(endpointRequest.getRequestedExpiry()).orElse(policy.getExpiresIn());
request.exp(request.getIat() + expiresIn.longValue());
StringBuilder scopes = new StringBuilder(Optional.ofNullable(request.getScope()).orElse(""));
client.getClientScopes(true).forEach((key, value) -> {
if (value.isDisplayOnConsentScreen())
scopes.append(" ").append(value.getName());
});
request.setScope(scopes.toString());
if (endpointRequest.getClientNotificationToken() != null) {
if (!policy.getBackchannelTokenDeliveryMode(client).equals(CibaConfig.CIBA_PING_MODE)) {
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Client Notification token supported only for the ping mode", Response.Status.BAD_REQUEST);
}
if (endpointRequest.getClientNotificationToken().length() > 1024) {
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Client Notification token length is limited to 1024 characters", Response.Status.BAD_REQUEST);
}
request.setClientNotificationToken(endpointRequest.getClientNotificationToken());
}
if (endpointRequest.getClientNotificationToken() == null && policy.getBackchannelTokenDeliveryMode(client).equals(CibaConfig.CIBA_PING_MODE)) {
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Client Notification token needs to be provided with the ping mode", Response.Status.BAD_REQUEST);
}
if (endpointRequest.getUserCode() != null) {
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "User code not supported", Response.Status.BAD_REQUEST);
}
extractAdditionalParams(endpointRequest, request);
try {
session.clientPolicy().triggerOnEvent(new BackchannelAuthenticationRequestContext(endpointRequest, request, params));
} catch (ClientPolicyException cpe) {
throw new ErrorResponseException(cpe.getError(), cpe.getErrorDetail(), Response.Status.BAD_REQUEST);
}
return request;
}
use of org.keycloak.protocol.oidc.grants.ciba.channel.CIBAAuthenticationRequest in project keycloak by keycloak.
the class BackchannelAuthenticationEndpoint method processGrantRequest.
@POST
@NoCache
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON)
public Response processGrantRequest(@Context HttpRequest httpRequest) {
CIBAAuthenticationRequest request = authorizeClient(httpRequest.getDecodedFormParameters());
try {
String authReqId = request.serialize(session);
AuthenticationChannelProvider provider = session.getProvider(AuthenticationChannelProvider.class);
if (provider == null) {
throw new RuntimeException("Authentication Channel Provider not found.");
}
CIBALoginUserResolver resolver = session.getProvider(CIBALoginUserResolver.class);
if (resolver == null) {
throw new RuntimeException("CIBA Login User Resolver not setup properly.");
}
UserModel user = request.getUser();
String infoUsedByAuthentication = resolver.getInfoUsedByAuthentication(user);
if (provider.requestAuthentication(request, infoUsedByAuthentication)) {
CibaConfig cibaPolicy = realm.getCibaPolicy();
int poolingInterval = cibaPolicy.getPoolingInterval();
storeAuthenticationRequest(request, cibaPolicy, authReqId);
ObjectNode response = JsonSerialization.createObjectNode();
response.put(CibaGrantType.AUTH_REQ_ID, authReqId).put(OAuth2Constants.EXPIRES_IN, cibaPolicy.getExpiresIn());
if (poolingInterval > 0) {
response.put(OAuth2Constants.INTERVAL, poolingInterval);
}
return Response.ok(JsonSerialization.writeValueAsBytes(response)).build();
}
} catch (Exception e) {
throw new ErrorResponseException(OAuthErrorException.SERVER_ERROR, "Failed to send authentication request", Response.Status.SERVICE_UNAVAILABLE);
}
throw new ErrorResponseException(OAuthErrorException.SERVER_ERROR, "Unexpected response from authentication device", Response.Status.SERVICE_UNAVAILABLE);
}
Aggregations