use of org.keycloak.models.UserSessionModel in project keycloak by keycloak.
the class AuthenticationProcessor method attachSession.
// May create new userSession too (if userSession argument is null)
public static ClientSessionContext attachSession(AuthenticationSessionModel authSession, UserSessionModel userSession, KeycloakSession session, RealmModel realm, ClientConnection connection, EventBuilder event) {
String username = authSession.getAuthenticatedUser().getUsername();
String attemptedUsername = authSession.getAuthNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME);
if (attemptedUsername != null)
username = attemptedUsername;
String rememberMe = authSession.getAuthNote(Details.REMEMBER_ME);
boolean remember = rememberMe != null && rememberMe.equalsIgnoreCase("true");
String brokerSessionId = authSession.getAuthNote(BROKER_SESSION_ID);
String brokerUserId = authSession.getAuthNote(BROKER_USER_ID);
if (userSession == null) {
// if no authenticator attached a usersession
userSession = session.sessions().getUserSession(realm, authSession.getParentSession().getId());
if (userSession == null) {
UserSessionModel.SessionPersistenceState persistenceState = UserSessionModel.SessionPersistenceState.fromString(authSession.getClientNote(AuthenticationManager.USER_SESSION_PERSISTENT_STATE));
userSession = session.sessions().createUserSession(authSession.getParentSession().getId(), realm, authSession.getAuthenticatedUser(), username, connection.getRemoteAddr(), authSession.getProtocol(), remember, brokerSessionId, brokerUserId, persistenceState);
} else if (userSession.getUser() == null || !AuthenticationManager.isSessionValid(realm, userSession)) {
userSession.restartSession(realm, authSession.getAuthenticatedUser(), username, connection.getRemoteAddr(), authSession.getProtocol(), remember, brokerSessionId, brokerUserId);
} else {
// We have existing userSession even if it wasn't attached to authenticator. Could happen if SSO authentication was ignored (eg. prompt=login) and in some other cases.
// We need to handle case when different user was used
logger.debugf("No SSO login, but found existing userSession with ID '%s' after finished authentication.", userSession.getId());
if (!authSession.getAuthenticatedUser().equals(userSession.getUser())) {
event.detail(Details.EXISTING_USER, userSession.getUser().getId());
event.error(Errors.DIFFERENT_USER_AUTHENTICATED);
throw new ErrorPageException(session, authSession, Response.Status.BAD_REQUEST, Messages.DIFFERENT_USER_AUTHENTICATED, userSession.getUser().getUsername());
}
}
userSession.setState(UserSessionModel.State.LOGGED_IN);
}
if (remember) {
event.detail(Details.REMEMBER_ME, "true");
}
ClientSessionContext clientSessionCtx = TokenManager.attachAuthenticationSession(session, userSession, authSession);
event.user(userSession.getUser()).detail(Details.USERNAME, username).session(userSession);
return clientSessionCtx;
}
use of org.keycloak.models.UserSessionModel 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.models.UserSessionModel in project keycloak by keycloak.
the class TokenRevocationEndpoint method revokeClient.
private void revokeClient() {
session.users().revokeConsentForClient(realm, user.getId(), client.getId());
if (TokenUtil.TOKEN_TYPE_OFFLINE.equals(token.getType())) {
new UserSessionManager(session).revokeOfflineToken(user, client);
}
session.sessions().getUserSessionsStream(realm, user).map(userSession -> userSession.getAuthenticatedClientSessionByClient(client.getId())).filter(Objects::nonNull).collect(// collect to avoid concurrent modification as dettachClientSession removes the user sessions.
Collectors.toList()).forEach(clientSession -> {
UserSessionModel userSession = clientSession.getUserSession();
TokenManager.dettachClientSession(clientSession);
if (userSession != null) {
// TODO: Might need optimization to prevent loading client sessions from cache in getAuthenticatedClientSessions()
if (userSession.getAuthenticatedClientSessions().isEmpty()) {
session.sessions().removeUserSession(realm, userSession);
}
}
});
}
use of org.keycloak.models.UserSessionModel 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.models.UserSessionModel 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;
}
Aggregations