use of org.keycloak.services.CorsErrorResponseException 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);
}
use of org.keycloak.services.CorsErrorResponseException in project keycloak by keycloak.
the class UserInfoEndpoint method issueUserInfo.
private Response issueUserInfo(String tokenString) {
cors = Cors.add(request).auth().allowedMethods(request.getHttpMethod()).auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
try {
session.clientPolicy().triggerOnEvent(new UserInfoRequestContext(tokenString));
} catch (ClientPolicyException cpe) {
throw new CorsErrorResponseException(cors.allowAllOrigins(), cpe.getError(), cpe.getErrorDetail(), cpe.getErrorStatus());
}
EventBuilder event = new EventBuilder(realm, session, clientConnection).event(EventType.USER_INFO_REQUEST).detail(Details.AUTH_METHOD, Details.VALIDATE_ACCESS_TOKEN);
if (tokenString == null) {
event.error(Errors.INVALID_TOKEN);
throw new CorsErrorResponseException(cors.allowAllOrigins(), OAuthErrorException.INVALID_REQUEST, "Token not provided", Response.Status.BAD_REQUEST);
}
AccessToken token;
ClientModel clientModel = null;
try {
TokenVerifier<AccessToken> verifier = TokenVerifier.create(tokenString, AccessToken.class).withDefaultChecks().realmUrl(Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
SignatureVerifierContext verifierContext = session.getProvider(SignatureProvider.class, verifier.getHeader().getAlgorithm().name()).verifier(verifier.getHeader().getKeyId());
verifier.verifierContext(verifierContext);
token = verifier.verify().getToken();
clientModel = realm.getClientByClientId(token.getIssuedFor());
if (clientModel == null) {
event.error(Errors.CLIENT_NOT_FOUND);
throw new CorsErrorResponseException(cors.allowAllOrigins(), OAuthErrorException.INVALID_REQUEST, "Client not found", Response.Status.BAD_REQUEST);
}
cors.allowedOrigins(session, clientModel);
TokenVerifier.createWithoutSignature(token).withChecks(NotBeforeCheck.forModel(clientModel), new TokenManager.TokenRevocationCheck(session)).verify();
} catch (VerificationException e) {
if (clientModel == null) {
cors.allowAllOrigins();
}
event.error(Errors.INVALID_TOKEN);
throw newUnauthorizedErrorResponseException(OAuthErrorException.INVALID_TOKEN, "Token verification failed");
}
if (!clientModel.getProtocol().equals(OIDCLoginProtocol.LOGIN_PROTOCOL)) {
event.error(Errors.INVALID_CLIENT);
throw new CorsErrorResponseException(cors, Errors.INVALID_CLIENT, "Wrong client protocol.", Response.Status.BAD_REQUEST);
}
session.getContext().setClient(clientModel);
event.client(clientModel);
if (!clientModel.isEnabled()) {
event.error(Errors.CLIENT_DISABLED);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Client disabled", Response.Status.BAD_REQUEST);
}
UserSessionModel userSession = findValidSession(token, event, clientModel);
UserModel userModel = userSession.getUser();
if (userModel == null) {
event.error(Errors.USER_NOT_FOUND);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "User not found", Response.Status.BAD_REQUEST);
}
event.user(userModel).detail(Details.USERNAME, userModel.getUsername());
// https://tools.ietf.org/html/draft-ietf-oauth-mtls-08#section-3
if (OIDCAdvancedConfigWrapper.fromClientModel(clientModel).isUseMtlsHokToken()) {
if (!MtlsHoKTokenUtil.verifyTokenBindingWithClientCertificate(token, request, session)) {
event.error(Errors.NOT_ALLOWED);
throw newUnauthorizedErrorResponseException(OAuthErrorException.UNAUTHORIZED_CLIENT, "Client certificate missing, or its thumbprint and one in the refresh token did NOT match");
}
}
// Existence of authenticatedClientSession for our client already handled before
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(clientModel.getId());
// Retrieve by latest scope parameter
ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionScopeParameter(clientSession, session);
AccessToken userInfo = new AccessToken();
tokenManager.transformUserInfoAccessToken(session, userInfo, userSession, clientSessionCtx);
Map<String, Object> claims = tokenManager.generateUserInfoClaims(userInfo, userModel);
Response.ResponseBuilder responseBuilder;
OIDCAdvancedConfigWrapper cfg = OIDCAdvancedConfigWrapper.fromClientModel(clientModel);
if (cfg.isUserInfoSignatureRequired()) {
String issuerUrl = Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName());
String audience = clientModel.getClientId();
claims.put("iss", issuerUrl);
claims.put("aud", audience);
String signatureAlgorithm = session.tokens().signatureAlgorithm(TokenCategory.USERINFO);
SignatureProvider signatureProvider = session.getProvider(SignatureProvider.class, signatureAlgorithm);
SignatureSignerContext signer = signatureProvider.signer();
String signedUserInfo = new JWSBuilder().type("JWT").jsonContent(claims).sign(signer);
responseBuilder = Response.ok(signedUserInfo).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JWT);
event.detail(Details.SIGNATURE_REQUIRED, "true");
event.detail(Details.SIGNATURE_ALGORITHM, cfg.getUserInfoSignedResponseAlg().toString());
} else {
responseBuilder = Response.ok(claims).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
event.detail(Details.SIGNATURE_REQUIRED, "false");
}
event.success();
return cors.builder(responseBuilder).build();
}
use of org.keycloak.services.CorsErrorResponseException in project keycloak by keycloak.
the class CibaGrantType method createUserSession.
private UserSessionModel createUserSession(CIBAAuthenticationRequest request, Map<String, String> additionalParams) {
RootAuthenticationSessionModel rootAuthSession = session.authenticationSessions().createRootAuthenticationSession(realm);
// here Client Model of CD(Consumption Device) needs to be used to bind its Client Session with User Session.
AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(client);
authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
authSession.setAction(AuthenticatedClientSessionModel.Action.AUTHENTICATE.name());
authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
authSession.setClientNote(OIDCLoginProtocol.SCOPE_PARAM, request.getScope());
if (additionalParams != null) {
for (String paramName : additionalParams.keySet()) {
authSession.setClientNote(ADDITIONAL_CALLBACK_PARAMS_PREFIX + paramName, additionalParams.get(paramName));
}
}
if (request.getOtherClaims() != null) {
for (String paramName : request.getOtherClaims().keySet()) {
authSession.setClientNote(ADDITIONAL_BACKCHANNEL_REQ_PARAMS_PREFIX + paramName, request.getOtherClaims().get(paramName).toString());
}
}
UserModel user = session.users().getUserById(realm, request.getSubject());
if (user == null) {
event.error(Errors.USERNAME_MISSING);
throw new ErrorResponseException(OAuthErrorException.INVALID_GRANT, "Could not identify user", Response.Status.BAD_REQUEST);
}
if (!user.isEnabled()) {
event.error(Errors.USER_DISABLED);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "User disabled", Response.Status.BAD_REQUEST);
}
logger.debugf("CIBA Grant :: user model found. user.getId() = %s, user.getEmail() = %s, user.getUsername() = %s.", user.getId(), user.getEmail(), user.getUsername());
authSession.setAuthenticatedUser(user);
if (user.getRequiredActionsStream().count() > 0) {
event.error(Errors.RESOLVE_REQUIRED_ACTIONS);
throw new ErrorResponseException(OAuthErrorException.INVALID_GRANT, "Account is not fully set up", Response.Status.BAD_REQUEST);
}
AuthenticationManager.setClientScopesInSession(authSession);
ClientSessionContext context = AuthenticationProcessor.attachSession(authSession, null, session, realm, session.getContext().getConnection(), event);
UserSessionModel userSession = context.getClientSession().getUserSession();
if (userSession == null) {
event.error(Errors.USER_SESSION_NOT_FOUND);
throw new ErrorResponseException(OAuthErrorException.INVALID_GRANT, "User session is not found", Response.Status.BAD_REQUEST);
}
// authorization (consent)
UserConsentModel grantedConsent = session.users().getConsentByClient(realm, user.getId(), client.getId());
if (grantedConsent == null) {
grantedConsent = new UserConsentModel(client);
session.users().addConsent(realm, user.getId(), grantedConsent);
if (logger.isTraceEnabled()) {
grantedConsent.getGrantedClientScopes().forEach(i -> logger.tracef("CIBA Grant :: Consent granted. %s", i.getName()));
}
}
boolean updateConsentRequired = false;
for (String clientScopeId : authSession.getClientScopes()) {
ClientScopeModel clientScope = KeycloakModelUtils.findClientScopeById(realm, client, clientScopeId);
if (clientScope != null && !grantedConsent.isClientScopeGranted(clientScope) && clientScope.isDisplayOnConsentScreen()) {
grantedConsent.addGrantedClientScope(clientScope);
updateConsentRequired = true;
}
}
if (updateConsentRequired) {
session.users().updateConsent(realm, user.getId(), grantedConsent);
if (logger.isTraceEnabled()) {
grantedConsent.getGrantedClientScopes().forEach(i -> logger.tracef("CIBA Grant :: Consent updated. %s", i.getName()));
}
}
event.detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED);
event.detail(Details.CODE_ID, userSession.getId());
event.session(userSession.getId());
event.user(user);
logger.debugf("Successfully verified Authe Req Id '%s'. User session: '%s', client: '%s'", request, userSession.getId(), client.getId());
return userSession;
}
use of org.keycloak.services.CorsErrorResponseException 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.services.CorsErrorResponseException in project keycloak by keycloak.
the class PkceUtils method verifyCodeVerifier.
public static void verifyCodeVerifier(String codeVerifier, String codeChallenge, String codeChallengeMethod, String authUserId, String authUsername, EventBuilder event, Cors cors) {
if (!isValidPkceCodeVerifier(codeVerifier)) {
logger.infof("PKCE invalid code verifier");
event.error(Errors.INVALID_CODE_VERIFIER);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "PKCE invalid code verifier", Response.Status.BAD_REQUEST);
}
logger.debugf("PKCE supporting Client, codeVerifier = %s", codeVerifier);
String codeVerifierEncoded = codeVerifier;
try {
// plain or S256
if (codeChallengeMethod != null && codeChallengeMethod.equals(OAuth2Constants.PKCE_METHOD_S256)) {
logger.debugf("PKCE codeChallengeMethod = %s", codeChallengeMethod);
codeVerifierEncoded = PkceUtils.generateS256CodeChallenge(codeVerifier);
} else {
logger.debug("PKCE codeChallengeMethod is plain");
codeVerifierEncoded = codeVerifier;
}
} catch (Exception nae) {
logger.infof("PKCE code verification failed, not supported algorithm specified");
event.error(Errors.PKCE_VERIFICATION_FAILED);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "PKCE code verification failed, not supported algorithm specified", Response.Status.BAD_REQUEST);
}
if (!codeChallenge.equals(codeVerifierEncoded)) {
logger.warnf("PKCE verification failed. authUserId = %s, authUsername = %s", authUserId, authUsername);
event.error(Errors.PKCE_VERIFICATION_FAILED);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "PKCE verification failed", Response.Status.BAD_REQUEST);
} else {
logger.debugf("PKCE verification success. codeVerifierEncoded = %s, codeChallenge = %s", codeVerifierEncoded, codeChallenge);
}
}
Aggregations