add HttpHeaders

This commit is contained in:
Michael Gerber
2015-02-23 20:08:05 +01:00
parent 0b1a2dd27d
commit be921c8544
19 changed files with 256 additions and 164 deletions

View File

@@ -0,0 +1,76 @@
package org.keycloak.freemarker;
import org.jboss.logging.Logger;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.UriInfo;
import java.util.*;
/**
* Created by michigerber on 23.02.15.
*/
public class LocaleHelper {
private final static String LOCALE_COOKIE = "KEYCLOAK_LOCALE";
private final static Logger LOGGER = Logger.getLogger(LocaleHelper.class);
private static final String LOCALE_PARAM = "ui_locale";
public static Locale getLocale(RealmModel realm, UserModel user, UriInfo uriInfo, HttpHeaders httpHeaders) {
//1. Locale cookie
if(httpHeaders.getCookies().containsKey(LOCALE_COOKIE)){
String localeString = httpHeaders.getCookies().get(LOCALE_COOKIE).getValue();
Locale locale = findLocale(localeString, realm.getSupportedLocales());
if(locale != null){
return locale;
}else{
LOGGER.infof("Locale %s is not supported.", localeString);
}
}
//2. User profile
if(user.getAttributes().containsKey(UserModel.LOCALE)){
String localeString = user.getAttribute(UserModel.LOCALE);
Locale locale = findLocale(localeString, realm.getSupportedLocales());
if(locale != null){
return locale;
}else{
LOGGER.infof("Locale %s is not supported.", localeString);
}
}
//3. ui_locales query parameter
if(uriInfo.getQueryParameters().containsKey(LOCALE_PARAM)){
String localeString = uriInfo.getQueryParameters().getFirst(LOCALE_PARAM);
Locale locale = findLocale(localeString, realm.getSupportedLocales());
if(locale != null){
return locale;
}else{
LOGGER.infof("Locale %s is not supported.", localeString);
}
}
//4. Accept-Language http header
if(httpHeaders.getLanguage() != null){
String localeString =httpHeaders.getLanguage().toLanguageTag();
Locale locale = findLocale(localeString, realm.getSupportedLocales());
if(locale != null){
return locale;
}else{
LOGGER.infof("Locale %s is not supported.", localeString);
}
}
//5. Default realm locale
return Locale.forLanguageTag(realm.getDefaultLocale());
}
private static Locale findLocale(String localeString, Set<String> supportedLocales) {
List<Locale> locales = new ArrayList<Locale>();
for(String l : supportedLocales) {
locales.add(Locale.forLanguageTag(l));
}
return Locale.lookup(Locale.LanguageRange.parse(localeString),locales);
}
}

View File

@@ -1,6 +1,5 @@
package org.keycloak.theme;
import org.jboss.logging.Logger;
import org.keycloak.freemarker.Theme;
import java.io.File;
@@ -9,14 +8,13 @@ import java.io.InputStream;
import java.net.URL;
import java.util.Locale;
import java.util.Properties;
import java.util.ResourceBundle;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ClassLoaderTheme implements Theme {
private static final Logger LOGGER = Logger.getLogger(ClassLoaderTheme.class);
private String name;
private String parentName;
@@ -110,20 +108,14 @@ public class ClassLoaderTheme implements Theme {
public Properties getMessages(Locale locale) throws IOException {
Properties m = new Properties();
URL url = null;
if(locale != null) {
URL messageFile = classLoader.getResource(this.messageRoot + "messages_" + locale.toString() + ".properties");
if(messageFile != null){
url = messageFile;
}else{
LOGGER.warnf("Can not find message file %s", messageFile);
}
}
if(url == null){
url = classLoader.getResource(this.messageRoot + "messages.properties");
String message = null;
if(locale != null){
message = this.messageRoot + "messages_" + locale.toString() + ".properties";
}else{
message = this.messageRoot + "messages.properties";
}
URL url = classLoader.getResource(message);
if (url != null) {
m.load(url.openStream());
}

View File

@@ -1,6 +1,5 @@
package org.keycloak.theme;
import org.jboss.logging.Logger;
import org.keycloak.freemarker.Theme;
import java.io.File;
@@ -16,7 +15,6 @@ import java.util.Properties;
*/
public class FolderTheme implements Theme {
private static final Logger LOGGER = Logger.getLogger(FolderTheme.class);
private String parentName;
private String importName;
private File themeDir;
@@ -87,20 +85,14 @@ public class FolderTheme implements Theme {
public Properties getMessages(Locale locale) throws IOException {
Properties m = new Properties();
File file = null;
if(locale != null) {
File messageFile = new File(themeDir, "messages" + File.separator + "messages_" + locale.toString() + ".properties");
if (messageFile.isFile()) {
file = messageFile;
} else {
LOGGER.warnf("Can not find message file %s", messageFile);
}
}
if(file == null){
file = new File(themeDir, "messages" + File.separator + "messages.properties");
String message = null;
if(locale != null){
message = "messages_" + locale.toString() + ".properties";
}else{
message = "messages.properties";
}
File file = new File(themeDir, "messages" + File.separator + message);
if (file.isFile()) {
m.load(new FileInputStream(file));
}

View File

@@ -6,6 +6,7 @@ import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.Provider;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
@@ -21,6 +22,8 @@ public interface LoginFormsProvider extends Provider {
public LoginFormsProvider setUriInfo(UriInfo uriInfo);
public LoginFormsProvider setHttpHeaders(HttpHeaders httpHeaders);
public Response createResponse(UserModel.RequiredAction action);
public Response createLogin();

View File

@@ -5,11 +5,7 @@ import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.keycloak.OAuth2Constants;
import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider;
import org.keycloak.freemarker.BrowserSecurityHeaderSetup;
import org.keycloak.freemarker.FreeMarkerException;
import org.keycloak.freemarker.FreeMarkerUtil;
import org.keycloak.freemarker.Theme;
import org.keycloak.freemarker.ThemeProvider;
import org.keycloak.freemarker.*;
import org.keycloak.login.LoginFormsPages;
import org.keycloak.login.LoginFormsProvider;
import org.keycloak.login.freemarker.model.ClientBean;
@@ -31,11 +27,7 @@ import org.keycloak.models.UserModel;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.flows.Urls;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.core.*;
import java.io.IOException;
import java.net.URI;
import java.util.*;
@@ -74,6 +66,8 @@ import java.util.concurrent.TimeUnit;
private UriInfo uriInfo;
private HttpHeaders httpHeaders;
public FreeMarkerLoginFormsProvider(KeycloakSession session, FreeMarkerUtil freeMarker) {
this.session = session;
this.freeMarker = freeMarker;
@@ -89,6 +83,12 @@ import java.util.concurrent.TimeUnit;
return this;
}
@Override
public LoginFormsProvider setHttpHeaders(HttpHeaders httpHeaders) {
this.httpHeaders = httpHeaders;
return this;
}
public Response createResponse(UserModel.RequiredAction action) {
String actionMessage;
LoginFormsPages page;
@@ -170,7 +170,7 @@ import java.util.concurrent.TimeUnit;
Properties messages;
try {
messages = theme.getMessages(Locale.GERMAN);
messages = theme.getMessages(LocaleHelper.getLocale(realm, user, uriInfo, httpHeaders));
attributes.put("rb", messages);
} catch (IOException e) {
logger.warn("Failed to load messages", e);

View File

@@ -13,6 +13,7 @@ public interface UserModel {
public static final String LAST_NAME = "lastName";
public static final String FIRST_NAME = "firstName";
public static final String EMAIL = "email";
public static final String LOCALE = "locale";
String getId();

View File

@@ -82,6 +82,7 @@ public class SamlProtocol implements LoginProtocol {
protected UriInfo uriInfo;
protected HttpHeaders headers;
@Override
@@ -102,6 +103,12 @@ public class SamlProtocol implements LoginProtocol {
return this;
}
@Override
public SamlProtocol setHttpHeaders(HttpHeaders headers){
this.headers = headers;
return this;
}
@Override
public Response cancelLogin(ClientSessionModel clientSession) {
return getErrorResponse(clientSession, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.get());
@@ -129,7 +136,7 @@ public class SamlProtocol implements LoginProtocol {
return builder.redirectBinding().response();
}
} catch (Exception e) {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response", headers);
}
}
@@ -279,7 +286,7 @@ public class SamlProtocol implements LoginProtocol {
publicKey = SamlProtocolUtils.getEncryptionValidationKey(client);
} catch (Exception e) {
logger.error("failed", e);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response", headers);
}
builder.encrypt(publicKey);
}
@@ -291,7 +298,7 @@ public class SamlProtocol implements LoginProtocol {
}
} catch (Exception e) {
logger.error("failed", e);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response", headers);
}
}

View File

@@ -101,18 +101,18 @@ public class SamlService {
if (!checkSsl()) {
event.event(EventType.LOGIN);
event.error(Errors.SSL_REQUIRED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required", headers);
}
if (!realm.isEnabled()) {
event.event(EventType.LOGIN_ERROR);
event.error(Errors.REALM_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled", headers);
}
if (samlRequest == null && samlResponse == null) {
event.event(EventType.LOGIN);
event.error(Errors.INVALID_TOKEN);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request", headers);
}
return null;
@@ -124,7 +124,7 @@ public class SamlService {
logger.warn("Unknown saml response.");
event.event(EventType.LOGIN);
event.error(Errors.INVALID_TOKEN);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request", headers);
}
// assume this is a logout response
UserSessionModel userSession = authResult.getSession();
@@ -133,10 +133,10 @@ public class SamlService {
logger.warn("UserSession is not tagged as logging out.");
event.event(EventType.LOGIN);
event.error(Errors.INVALID_TOKEN);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request", headers);
}
logger.debug("logout response");
return authManager.browserLogout(session, realm, userSession, uriInfo, clientConnection);
return authManager.browserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
}
protected Response handleSamlRequest(String samlRequest, String relayState) {
@@ -144,7 +144,7 @@ public class SamlService {
if (documentHolder == null) {
event.event(EventType.LOGIN);
event.error(Errors.INVALID_TOKEN);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request", headers);
}
SAML2Object samlObject = documentHolder.getSamlObject();
@@ -156,23 +156,23 @@ public class SamlService {
if (client == null) {
event.event(EventType.LOGIN);
event.error(Errors.CLIENT_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.", headers);
}
if (!client.isEnabled()) {
event.event(EventType.LOGIN);
event.error(Errors.CLIENT_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.", headers);
}
if ((client instanceof ApplicationModel) && ((ApplicationModel)client).isBearerOnly()) {
event.event(EventType.LOGIN);
event.error(Errors.NOT_ALLOWED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Bearer-only applications are not allowed to initiate browser login");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Bearer-only applications are not allowed to initiate browser login", headers);
}
if (client.isDirectGrantsOnly()) {
event.event(EventType.LOGIN);
event.error(Errors.NOT_ALLOWED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "direct-grants-only clients are not allowed to initiate browser login");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "direct-grants-only clients are not allowed to initiate browser login", headers);
}
try {
@@ -181,7 +181,7 @@ public class SamlService {
SamlService.logger.error("request validation failed", e);
event.event(EventType.LOGIN);
event.error(Errors.INVALID_SIGNATURE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid requester.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid requester.", headers);
}
logger.debug("verified request");
if (samlObject instanceof AuthnRequestType) {
@@ -199,7 +199,7 @@ public class SamlService {
} else {
event.event(EventType.LOGIN);
event.error(Errors.INVALID_TOKEN);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request", headers);
}
}
@@ -229,7 +229,7 @@ public class SamlService {
if (redirect == null) {
event.error(Errors.INVALID_REDIRECT_URI);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect_uri.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect_uri.", headers);
}
@@ -251,14 +251,14 @@ public class SamlService {
clientSession.setNote(GeneralConstants.NAMEID_FORMAT, nameIdFormat);
} else {
event.error(Errors.INVALID_TOKEN);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unsupported NameIDFormat.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unsupported NameIDFormat.", headers);
}
}
Response response = authManager.checkNonFormAuthentication(session, clientSession, realm, uriInfo, request, clientConnection, headers, event);
if (response != null) return response;
LoginFormsProvider forms = Flows.forms(session, realm, clientSession.getClient(), uriInfo)
LoginFormsProvider forms = Flows.forms(session, realm, clientSession.getClient(), uriInfo, headers)
.setClientSessionCode(new ClientSessionCode(realm, clientSession).getCode());
String rememberMeUsername = AuthenticationManager.getRememberMeUsername(realm, headers);
@@ -327,7 +327,7 @@ public class SamlService {
}
}
logger.debug("browser Logout");
return authManager.browserLogout(session, realm, userSession, uriInfo, clientConnection);
return authManager.browserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
}
@@ -340,7 +340,7 @@ public class SamlService {
if (redirectUri != null) {
redirectUri = OIDCLoginProtocolService.verifyRedirectUri(uriInfo, redirectUri, realm, client);
if (redirectUri == null) {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect uri.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect uri.", headers);
}
}
if (redirectUri != null) {
@@ -352,7 +352,7 @@ public class SamlService {
}
private Response logout(UserSessionModel userSession) {
Response response = authManager.browserLogout(session, realm, userSession, uriInfo, clientConnection);
Response response = authManager.browserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
if (response == null) event.user(userSession.getUser()).session(userSession).success();
return response;
}

View File

@@ -7,6 +7,7 @@ import org.keycloak.models.UserSessionModel;
import org.keycloak.provider.Provider;
import org.keycloak.services.managers.ClientSessionCode;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
@@ -21,6 +22,8 @@ public interface LoginProtocol extends Provider {
LoginProtocol setUriInfo(UriInfo uriInfo);
LoginProtocol setHttpHeaders(HttpHeaders headers);
Response cancelLogin(ClientSessionModel clientSession);
Response invalidSessionError(ClientSessionModel clientSession);
Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode);

View File

@@ -33,6 +33,7 @@ import org.keycloak.protocol.LoginProtocol;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.ResourceAdminManager;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
@@ -59,13 +60,17 @@ public class OIDCLoginProtocol implements LoginProtocol {
protected UriInfo uriInfo;
public OIDCLoginProtocol(KeycloakSession session, RealmModel realm, UriInfo uriInfo) {
protected HttpHeaders headers;
public OIDCLoginProtocol(KeycloakSession session, RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
this.session = session;
this.realm = realm;
this.uriInfo = uriInfo;
this.headers = headers;
}
public OIDCLoginProtocol() {
public OIDCLoginProtocol(){
}
@Override
@@ -86,6 +91,12 @@ public class OIDCLoginProtocol implements LoginProtocol {
return this;
}
@Override
public OIDCLoginProtocol setHttpHeaders(HttpHeaders headers){
this.headers = headers;
return this;
}
@Override
public Response cancelLogin(ClientSessionModel clientSession) {
String redirect = clientSession.getRedirectUri();

View File

@@ -508,7 +508,7 @@ public class OIDCLoginProtocolService {
}
AccessToken accessToken;
try {
accessToken = tokenManager.refreshAccessToken(session, uriInfo, clientConnection, realm, client, refreshToken, event);
accessToken = tokenManager.refreshAccessToken(session, uriInfo, clientConnection, realm, client, refreshToken, event, headers);
} catch (OAuthErrorException e) {
Map<String, String> error = new HashMap<String, String>();
error.put(OAuth2Constants.ERROR, e.getError());
@@ -648,7 +648,7 @@ public class OIDCLoginProtocolService {
}
if (!AuthenticationManager.isSessionValid(realm, userSession)) {
AuthenticationManager.logout(session, realm, userSession, uriInfo, clientConnection);
AuthenticationManager.logout(session, realm, userSession, uriInfo, clientConnection, headers);
Map<String, String> res = new HashMap<String, String>();
res.put(OAuth2Constants.ERROR, "invalid_grant");
res.put(OAuth2Constants.ERROR_DESCRIPTION, "Session not active");
@@ -785,37 +785,37 @@ public class OIDCLoginProtocolService {
event.client(clientId).detail(Details.REDIRECT_URI, redirect).detail(Details.RESPONSE_TYPE, "code");
if (!checkSsl()) {
event.error(Errors.SSL_REQUIRED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required", headers);
}
if (!realm.isEnabled()) {
event.error(Errors.REALM_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled", headers);
}
clientSession = null;
ClientModel client = realm.findClient(clientId);
if (client == null) {
event.error(Errors.CLIENT_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.", headers);
}
if (!client.isEnabled()) {
event.error(Errors.CLIENT_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.", headers);
}
if ((client instanceof ApplicationModel) && ((ApplicationModel)client).isBearerOnly()) {
event.error(Errors.NOT_ALLOWED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Bearer-only applications are not allowed to initiate browser login");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Bearer-only applications are not allowed to initiate browser login", headers);
}
if (client.isDirectGrantsOnly()) {
event.error(Errors.NOT_ALLOWED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "direct-grants-only clients are not allowed to initiate browser login");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "direct-grants-only clients are not allowed to initiate browser login", headers);
}
String redirectUriParam = redirect;
redirect = verifyRedirectUri(uriInfo, redirect, realm, client);
if (redirect == null) {
event.error(Errors.INVALID_REDIRECT_URI);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect_uri.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect_uri.", headers);
}
clientSession = session.sessions().createClientSession(realm, client);
clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL);
@@ -874,7 +874,7 @@ public class OIDCLoginProtocolService {
IdentityProviderModel identityProviderModel = realm.getIdentityProviderById(idpHint);
if (identityProviderModel == null) {
return Flows.forms(session, realm, null, uriInfo)
return Flows.forms(session, realm, null, uriInfo, headers)
.setError("Could not find an identity provider with the identifier [" + idpHint + "].")
.createErrorPage();
}
@@ -886,11 +886,11 @@ public class OIDCLoginProtocolService {
// SPNEGO/Kerberos authentication TODO: This should be somehow pluggable instead of hardcoded this way (Authentication interceptors?)
HttpAuthenticationManager httpAuthManager = new HttpAuthenticationManager(session, clientSession, realm, uriInfo, request, clientConnection, event);
HttpAuthenticationManager.HttpAuthOutput httpAuthOutput = httpAuthManager.spnegoAuthenticate();
HttpAuthenticationManager.HttpAuthOutput httpAuthOutput = httpAuthManager.spnegoAuthenticate(headers);
if (httpAuthOutput.getResponse() != null) return httpAuthOutput.getResponse();
if (prompt != null && prompt.equals("none")) {
OIDCLoginProtocol oauth = new OIDCLoginProtocol(session, realm, uriInfo);
OIDCLoginProtocol oauth = new OIDCLoginProtocol(session, realm, uriInfo, headers);
return oauth.cancelLogin(clientSession);
}
@@ -908,13 +908,13 @@ public class OIDCLoginProtocolService {
return redirectToIdentityProvider(identityProviders.get(0).getId(), accessCode);
}
return Flows.forms(session, realm, null, uriInfo).setError("Realm [" + this.realm.getName() + "] supports multiple identity providers. Could not determine which identity provider should be used to authenticate with.").createErrorPage();
return Flows.forms(session, realm, null, uriInfo, headers).setError("Realm [" + this.realm.getName() + "] supports multiple identity providers. Could not determine which identity provider should be used to authenticate with.").createErrorPage();
}
return Flows.forms(session, realm, null, uriInfo).setError("Realm [" + this.realm.getName() + "] does not support any credential type.").createErrorPage();
return Flows.forms(session, realm, null, uriInfo, headers).setError("Realm [" + this.realm.getName() + "] does not support any credential type.").createErrorPage();
}
LoginFormsProvider forms = Flows.forms(session, realm, clientSession.getClient(), uriInfo)
LoginFormsProvider forms = Flows.forms(session, realm, clientSession.getClient(), uriInfo, headers)
.setClientSessionCode(accessCode);
// Attach state from SPNEGO authentication
@@ -960,7 +960,7 @@ public class OIDCLoginProtocolService {
event.event(EventType.REGISTER);
if (!realm.isRegistrationAllowed()) {
event.error(Errors.REGISTRATION_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Registration not allowed");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Registration not allowed", headers);
}
FrontPageInitializer pageInitializer = new FrontPageInitializer();
@@ -976,7 +976,7 @@ public class OIDCLoginProtocolService {
authManager.expireIdentityCookie(realm, uriInfo, clientConnection);
return Flows.forms(session, realm, clientSession.getClient(), uriInfo)
return Flows.forms(session, realm, clientSession.getClient(), uriInfo, headers)
.setClientSessionCode(new ClientSessionCode(realm, clientSession).getCode())
.createRegistration();
}
@@ -1004,7 +1004,7 @@ public class OIDCLoginProtocolService {
if (redirectUri != null) {
String validatedRedirect = verifyRealmRedirectUri(uriInfo, redirectUri, realm);
if (validatedRedirect == null) {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect uri.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect uri.", headers);
}
return Response.status(302).location(UriBuilder.fromUri(validatedRedirect).build()).build();
} else {
@@ -1065,14 +1065,14 @@ public class OIDCLoginProtocolService {
}
private void logout(UserSessionModel userSession) {
authManager.logout(session, realm, userSession, uriInfo, clientConnection);
authManager.logout(session, realm, userSession, uriInfo, clientConnection, headers);
event.user(userSession.getUser()).session(userSession).success();
}
@Path("oauth/oob")
@GET
public Response installedAppUrnCallback(final @QueryParam("code") String code, final @QueryParam("error") String error, final @QueryParam("error_description") String errorDescription) {
LoginFormsProvider forms = Flows.forms(session, realm, null, uriInfo);
LoginFormsProvider forms = Flows.forms(session, realm, null, uriInfo, headers);
if (code != null) {
return forms.setClientSessionCode(code).createCode();
} else {

View File

@@ -28,6 +28,7 @@ import org.keycloak.representations.RefreshToken;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.util.Time;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
import java.util.HashSet;
@@ -57,7 +58,7 @@ public class TokenManager {
}
}
public AccessToken refreshAccessToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm, ClientModel client, String encodedRefreshToken, EventBuilder event) throws OAuthErrorException {
public AccessToken refreshAccessToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm, ClientModel client, String encodedRefreshToken, EventBuilder event, HttpHeaders headers) throws OAuthErrorException {
RefreshToken refreshToken = verifyRefreshToken(realm, encodedRefreshToken);
event.user(refreshToken.getSubject()).session(refreshToken.getSessionState()).detail(Details.REFRESH_TOKEN_ID, refreshToken.getId());
@@ -74,7 +75,7 @@ public class TokenManager {
UserSessionModel userSession = session.sessions().getUserSession(realm, refreshToken.getSessionState());
int currentTime = Time.currentTime();
if (!AuthenticationManager.isSessionValid(realm, userSession)) {
AuthenticationManager.logout(session, realm, userSession, uriInfo, connection);
AuthenticationManager.logout(session, realm, userSession, uriInfo, connection, headers);
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Session not active", "Session not active");
}

View File

@@ -42,7 +42,7 @@ public class AppAuthManager extends AuthenticationManager {
public AuthResult authenticateBearerToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
String tokenString = extractAuthorizationHeaderToken(headers);
if (tokenString == null) return null;
AuthResult authResult = verifyIdentityToken(session, realm, uriInfo, connection, true, tokenString);
AuthResult authResult = verifyIdentityToken(session, realm, uriInfo, connection, true, tokenString, headers);
return authResult;
}

View File

@@ -78,7 +78,7 @@ public class AuthenticationManager {
return userSession != null && userSession.getLastSessionRefresh() + realm.getSsoSessionIdleTimeout() > currentTime && max > currentTime;
}
public static void logout(KeycloakSession session, RealmModel realm, UserSessionModel userSession, UriInfo uriInfo, ClientConnection connection) {
public static void logout(KeycloakSession session, RealmModel realm, UserSessionModel userSession, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
if (userSession == null) return;
UserModel user = userSession.getUser();
userSession.setState(UserSessionModel.State.LOGGING_OUT);
@@ -94,6 +94,7 @@ public class AuthenticationManager {
if (authMethod == null) continue; // must be a keycloak service like account
LoginProtocol protocol = session.getProvider(LoginProtocol.class, authMethod);
protocol.setRealm(realm)
.setHttpHeaders(headers)
.setUriInfo(uriInfo);
protocol.backchannelLogout(userSession, clientSession);
clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT);
@@ -104,7 +105,7 @@ public class AuthenticationManager {
}
public static Response browserLogout(KeycloakSession session, RealmModel realm, UserSessionModel userSession, UriInfo uriInfo, ClientConnection connection) {
public static Response browserLogout(KeycloakSession session, RealmModel realm, UserSessionModel userSession, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
if (userSession == null) return null;
UserModel user = userSession.getUser();
@@ -127,6 +128,7 @@ public class AuthenticationManager {
if (authMethod == null) continue; // must be a keycloak service like account
LoginProtocol protocol = session.getProvider(LoginProtocol.class, authMethod);
protocol.setRealm(realm)
.setHttpHeaders(headers)
.setUriInfo(uriInfo);
try {
logger.debugv("backchannel logout to: {0}", client.getClientId());
@@ -139,12 +141,13 @@ public class AuthenticationManager {
}
if (redirectClients.size() == 0) {
return finishBrowserLogout(session, realm, userSession, uriInfo, connection);
return finishBrowserLogout(session, realm, userSession, uriInfo, connection, headers);
}
for (ClientSessionModel nextRedirectClient : redirectClients) {
String authMethod = nextRedirectClient.getAuthMethod();
LoginProtocol protocol = session.getProvider(LoginProtocol.class, authMethod);
protocol.setRealm(realm)
.setHttpHeaders(headers)
.setUriInfo(uriInfo);
// setting this to logged out cuz I"m not sure protocols can always verify that the client was logged out or not
nextRedirectClient.setAction(ClientSessionModel.Action.LOGGED_OUT);
@@ -160,16 +163,17 @@ public class AuthenticationManager {
}
}
return finishBrowserLogout(session, realm, userSession, uriInfo, connection);
return finishBrowserLogout(session, realm, userSession, uriInfo, connection, headers);
}
protected static Response finishBrowserLogout(KeycloakSession session, RealmModel realm, UserSessionModel userSession, UriInfo uriInfo, ClientConnection connection) {
protected static Response finishBrowserLogout(KeycloakSession session, RealmModel realm, UserSessionModel userSession, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
expireIdentityCookie(realm, uriInfo, connection);
expireRememberMeCookie(realm, uriInfo, connection);
userSession.setState(UserSessionModel.State.LOGGED_OUT);
String method = userSession.getNote(KEYCLOAK_LOGOUT_PROTOCOL);
LoginProtocol protocol = session.getProvider(LoginProtocol.class, method);
protocol.setRealm(realm)
.setHttpHeaders(headers)
.setUriInfo(uriInfo);
Response response = protocol.finishLogout(userSession);
session.sessions().removeUserSession(realm, userSession);
@@ -284,7 +288,7 @@ public class AuthenticationManager {
}
String tokenString = cookie.getValue();
AuthResult authResult = verifyIdentityToken(session, realm, uriInfo, connection, checkActive, tokenString);
AuthResult authResult = verifyIdentityToken(session, realm, uriInfo, connection, checkActive, tokenString, headers);
if (authResult == null) {
expireIdentityCookie(realm, uriInfo, connection);
return null;
@@ -334,6 +338,7 @@ public class AuthenticationManager {
if (userSession.isRememberMe()) createRememberMeCookie(realm, userSession.getUser().getUsername(), uriInfo, clientConnection);
LoginProtocol protocol = session.getProvider(LoginProtocol.class, clientSession.getAuthMethod());
protocol.setRealm(realm)
.setHttpHeaders(request.getHttpHeaders())
.setUriInfo(uriInfo);
return protocol.authenticated(userSession, new ClientSessionCode(realm, clientSession));
@@ -363,7 +368,7 @@ public class AuthenticationManager {
UserModel.RequiredAction action = user.getRequiredActions().iterator().next();
accessCode.setRequiredAction(action);
LoginFormsProvider loginFormsProvider = Flows.forms(session, realm, client, uriInfo).setClientSessionCode(accessCode.getCode()).setUser(user);
LoginFormsProvider loginFormsProvider = Flows.forms(session, realm, client, uriInfo, request.getHttpHeaders()).setClientSessionCode(accessCode.getCode()).setUser(user);
if (action.equals(UserModel.RequiredAction.VERIFY_EMAIL)) {
event.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, user.getEmail()).success();
}
@@ -385,7 +390,7 @@ public class AuthenticationManager {
}
}
return Flows.forms(session, realm, client, uriInfo)
return Flows.forms(session, realm, client, uriInfo, request.getHttpHeaders())
.setClientSessionCode(accessCode.getCode())
.setAccessRequest(realmRoles, resourceRoles)
.setClient(client)
@@ -413,7 +418,7 @@ public class AuthenticationManager {
}
}
protected AuthResult verifyIdentityToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, boolean checkActive, String tokenString) {
protected AuthResult verifyIdentityToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, boolean checkActive, String tokenString, HttpHeaders headers) {
try {
AccessToken token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), realm.getName(), checkActive);
if (checkActive) {
@@ -433,7 +438,7 @@ public class AuthenticationManager {
UserSessionModel userSession = session.sessions().getUserSession(realm, token.getSessionState());
if (!isSessionValid(realm, userSession)) {
if (userSession != null) logout(session, realm, userSession, uriInfo, connection);
if (userSession != null) logout(session, realm, userSession, uriInfo, connection, headers);
logger.debug("User session not active");
return null;
}

View File

@@ -56,7 +56,7 @@ public class HttpAuthenticationManager {
}
public HttpAuthOutput spnegoAuthenticate() {
public HttpAuthOutput spnegoAuthenticate(HttpHeaders headers) {
boolean kerberosSupported = false;
for (RequiredCredentialModel c : realm.getRequiredCredentials()) {
if (c.getType().equals(CredentialRepresentation.KERBEROS)) {
@@ -94,7 +94,7 @@ public class HttpAuthenticationManager {
CredentialValidationOutput output = session.users().validCredentials(realm, spnegoCredential);
if (output.getAuthStatus() == CredentialValidationOutput.Status.AUTHENTICATED) {
return sendResponse(output.getAuthenticatedUser(), "spnego");
return sendResponse(output.getAuthenticatedUser(), "spnego", headers);
} else {
String spnegoResponseToken = (String) output.getState().get(KerberosConstants.RESPONSE_TOKEN);
return challengeNegotiation(spnegoResponseToken);
@@ -104,7 +104,7 @@ public class HttpAuthenticationManager {
// Send response after successful authentication
private HttpAuthOutput sendResponse(UserModel user, String authMethod) {
private HttpAuthOutput sendResponse(UserModel user, String authMethod, HttpHeaders headers) {
if (logger.isTraceEnabled()) {
logger.trace("User " + user.getUsername() + " authenticated with " + authMethod);
}
@@ -112,7 +112,7 @@ public class HttpAuthenticationManager {
Response response;
if (!user.isEnabled()) {
event.error(Errors.USER_DISABLED);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, Messages.ACCOUNT_DISABLED);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, Messages.ACCOUNT_DISABLED, headers);
} else {
UserSessionModel userSession = session.sessions().createUserSession(realm, user, user.getUsername(), clientConnection.getRemoteAddr(), authMethod, false);
TokenManager.attachClientSession(userSession, clientSession);

View File

@@ -241,7 +241,7 @@ public class AccountService {
try {
require(AccountRoles.MANAGE_ACCOUNT);
} catch (ForbiddenException e) {
return Flows.forms(session, realm, null, uriInfo).setError("No access").createErrorPage();
return Flows.forms(session, realm, null, uriInfo, headers).setError("No access").createErrorPage();
}
setReferrerOnPage();

View File

@@ -57,13 +57,8 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.*;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -97,6 +92,10 @@ public class IdentityBrokerService {
@Context
private HttpRequest request;
@Context
private HttpHeaders headers;
private EventBuilder event;
public IdentityBrokerService(RealmModel realmModel) {
@@ -187,11 +186,10 @@ public class IdentityBrokerService {
}
if (OAuthClientModel.class.isInstance(clientModel) && !forceRetrieval) {
return corsResponse(Flows.forms(this.session, this.realmModel, clientModel, this.uriInfo)
return corsResponse(Flows.forms(this.session, this.realmModel, clientModel, this.uriInfo, headers)
.setClientSessionCode(authManager.extractAuthorizationHeaderToken(this.request.getHttpHeaders()))
.setAccessRequest("Your information from " + providerId + " identity provider.")
.setClient(clientModel)
.setUriInfo(this.uriInfo)
.setActionUri(this.uriInfo.getRequestUri())
.createOAuthGrant(), clientModel);
}
@@ -439,7 +437,7 @@ public class IdentityBrokerService {
private Response redirectToErrorPage(String message, Throwable throwable) {
fireErrorEvent(message, throwable);
return Flows.forwardToSecurityFailurePage(this.session, this.realmModel, this.uriInfo, message);
return Flows.forwardToSecurityFailurePage(this.session, this.realmModel, this.uriInfo, message, headers);
}
private Response badRequest(String message) {
@@ -449,7 +447,7 @@ public class IdentityBrokerService {
private Response redirectToLoginPage(String message, ClientSessionCode clientCode) {
fireErrorEvent(message);
return Flows.forms(this.session, this.realmModel, clientCode.getClientSession().getClient(), this.uriInfo)
return Flows.forms(this.session, this.realmModel, clientCode.getClientSession().getClient(), this.uriInfo, headers)
.setClientSessionCode(clientCode.getCode())
.setError(message)
.createLogin();

View File

@@ -156,7 +156,7 @@ public class LoginActionsService {
return false;
} else if (!clientCode.isValid(requiredAction)) {
event.error(Errors.INVALID_CODE);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid code, please login again through your application.");
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid code, please login again through your application.", headers);
return false;
} else {
return true;
@@ -166,18 +166,18 @@ public class LoginActionsService {
public boolean check(String code) {
if (!checkSsl()) {
event.error(Errors.SSL_REQUIRED);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required");
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required", headers);
return false;
}
if (!realm.isEnabled()) {
event.error(Errors.REALM_DISABLED);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.");
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.", headers);
return false;
}
clientCode = ClientSessionCode.parse(code, session, realm);
if (clientCode == null) {
event.error(Errors.INVALID_CODE);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.");
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.", headers);
return false;
}
return true;
@@ -208,7 +208,7 @@ public class LoginActionsService {
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
}
LoginFormsProvider forms = Flows.forms(session, realm, clientSession.getClient(), uriInfo)
LoginFormsProvider forms = Flows.forms(session, realm, clientSession.getClient(), uriInfo, headers)
.setClientSessionCode(clientSessionCode.getCode());
return forms.createLogin();
@@ -226,7 +226,7 @@ public class LoginActionsService {
event.event(EventType.REGISTER);
if (!realm.isRegistrationAllowed()) {
event.error(Errors.REGISTRATION_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Registration not allowed");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Registration not allowed", headers);
}
Checks checks = new Checks();
@@ -240,7 +240,7 @@ public class LoginActionsService {
authManager.expireIdentityCookie(realm, uriInfo, clientConnection);
return Flows.forms(session, realm, clientSession.getClient(), uriInfo)
return Flows.forms(session, realm, clientSession.getClient(), uriInfo, headers)
.setClientSessionCode(clientSessionCode.getCode())
.createRegistration();
}
@@ -260,23 +260,23 @@ public class LoginActionsService {
event.event(EventType.LOGIN);
if (!checkSsl()) {
event.error(Errors.SSL_REQUIRED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required", headers);
}
if (!realm.isEnabled()) {
event.error(Errors.REALM_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.", headers);
}
ClientSessionCode clientCode = ClientSessionCode.parse(code, session, realm);
if (clientCode == null) {
event.error(Errors.INVALID_CODE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.", headers);
}
ClientSessionModel clientSession = clientCode.getClientSession();
if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE) || clientSession.getUserSession() != null) {
clientCode.setAction(ClientSessionModel.Action.AUTHENTICATE);
event.client(clientSession.getClient()).error(Errors.EXPIRED_CODE);
return Flows.forms(this.session, realm, clientSession.getClient(), uriInfo).setError(Messages.EXPIRED_CODE)
return Flows.forms(this.session, realm, clientSession.getClient(), uriInfo, headers).setError(Messages.EXPIRED_CODE)
.setClientSessionCode(clientCode.getCode())
.createLogin();
}
@@ -300,17 +300,18 @@ public class LoginActionsService {
ClientModel client = clientSession.getClient();
if (client == null) {
event.error(Errors.CLIENT_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.", headers);
}
if (!client.isEnabled()) {
event.error(Errors.CLIENT_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.", headers);
}
if (formData.containsKey("cancel")) {
event.error(Errors.REJECTED_BY_USER);
LoginProtocol protocol = session.getProvider(LoginProtocol.class, clientSession.getAuthMethod());
protocol.setRealm(realm)
.setHttpHeaders(headers)
.setUriInfo(uriInfo);
return protocol.cancelLogin(clientSession);
}
@@ -337,14 +338,14 @@ public class LoginActionsService {
return authManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
case ACCOUNT_TEMPORARILY_DISABLED:
event.error(Errors.USER_TEMPORARILY_DISABLED);
return Flows.forms(this.session, realm, client, uriInfo)
return Flows.forms(this.session, realm, client, uriInfo, headers)
.setError(Messages.ACCOUNT_TEMPORARILY_DISABLED)
.setFormData(formData)
.setClientSessionCode(clientCode.getCode())
.createLogin();
case ACCOUNT_DISABLED:
event.error(Errors.USER_DISABLED);
return Flows.forms(this.session, realm, client, uriInfo)
return Flows.forms(this.session, realm, client, uriInfo, headers)
.setError(Messages.ACCOUNT_DISABLED)
.setClientSessionCode(clientCode.getCode())
.setFormData(formData).createLogin();
@@ -354,19 +355,19 @@ public class LoginActionsService {
String passwordToken = new JWSBuilder().jsonContent(new PasswordToken(realm.getName(), user.getId())).rsa256(realm.getPrivateKey());
formData.add(CredentialRepresentation.PASSWORD_TOKEN, passwordToken);
return Flows.forms(this.session, realm, client, uriInfo)
return Flows.forms(this.session, realm, client, uriInfo, headers)
.setFormData(formData)
.setClientSessionCode(clientCode.getCode())
.createLoginTotp();
case INVALID_USER:
event.error(Errors.USER_NOT_FOUND);
return Flows.forms(this.session, realm, client, uriInfo).setError(Messages.INVALID_USER)
return Flows.forms(this.session, realm, client, uriInfo, headers).setError(Messages.INVALID_USER)
.setFormData(formData)
.setClientSessionCode(clientCode.getCode())
.createLogin();
default:
event.error(Errors.INVALID_USER_CREDENTIALS);
return Flows.forms(this.session, realm, client, uriInfo).setError(Messages.INVALID_USER)
return Flows.forms(this.session, realm, client, uriInfo, headers).setError(Messages.INVALID_USER)
.setFormData(formData)
.setClientSessionCode(clientCode.getCode())
.createLogin();
@@ -388,25 +389,25 @@ public class LoginActionsService {
event.event(EventType.REGISTER);
if (!checkSsl()) {
event.error(Errors.SSL_REQUIRED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required", headers);
}
if (!realm.isEnabled()) {
event.error(Errors.REALM_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.", headers);
}
if (!realm.isRegistrationAllowed()) {
event.error(Errors.REGISTRATION_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Registration not allowed");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Registration not allowed", headers);
}
ClientSessionCode clientCode = ClientSessionCode.parse(code, session, realm);
if (clientCode == null) {
event.error(Errors.INVALID_CODE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.", headers);
}
if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE)) {
event.error(Errors.INVALID_CODE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid code, please login again through your application.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid code, please login again through your application.", headers);
}
String username = formData.getFirst("username");
@@ -421,17 +422,17 @@ public class LoginActionsService {
if (!realm.isEnabled()) {
event.error(Errors.REALM_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled", headers);
}
ClientModel client = clientSession.getClient();
if (client == null) {
event.error(Errors.CLIENT_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.", headers);
}
if (!client.isEnabled()) {
event.error(Errors.CLIENT_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.", headers);
}
@@ -448,7 +449,7 @@ public class LoginActionsService {
if (error != null) {
event.error(Errors.INVALID_REGISTRATION);
return Flows.forms(session, realm, client, uriInfo)
return Flows.forms(session, realm, client, uriInfo, headers)
.setError(error)
.setFormData(formData)
.setClientSessionCode(clientCode.getCode())
@@ -458,7 +459,7 @@ public class LoginActionsService {
// Validate that user with this username doesn't exist in realm or any federation provider
if (session.users().getUserByUsername(username, realm) != null) {
event.error(Errors.USERNAME_IN_USE);
return Flows.forms(session, realm, client, uriInfo)
return Flows.forms(session, realm, client, uriInfo, headers)
.setError(Messages.USERNAME_EXISTS)
.setFormData(formData)
.setClientSessionCode(clientCode.getCode())
@@ -468,7 +469,7 @@ public class LoginActionsService {
// Validate that user with this email doesn't exist in realm or any federation provider
if (session.users().getUserByEmail(email, realm) != null) {
event.error(Errors.EMAIL_IN_USE);
return Flows.forms(session, realm, client, uriInfo)
return Flows.forms(session, realm, client, uriInfo, headers)
.setError(Messages.EMAIL_EXISTS)
.setFormData(formData)
.setClientSessionCode(clientCode.getCode())
@@ -500,7 +501,7 @@ public class LoginActionsService {
// User already registered, but force him to update password
if (!passwordUpdateSuccessful) {
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
return Flows.forms(session, realm, client, uriInfo)
return Flows.forms(session, realm, client, uriInfo, headers)
.setError(passwordUpdateError)
.setClientSessionCode(clientCode.getCode())
.createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
@@ -527,7 +528,7 @@ public class LoginActionsService {
if (!checkSsl()) {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required", headers);
}
String code = formData.getFirst("code");
@@ -535,7 +536,7 @@ public class LoginActionsService {
ClientSessionCode accessCode = ClientSessionCode.parse(code, session, realm);
if (accessCode == null || !accessCode.isValid(ClientSessionModel.Action.OAUTH_GRANT)) {
event.error(Errors.INVALID_CODE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid access code.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid access code.", headers);
}
ClientSessionModel clientSession = accessCode.getClientSession();
event.detail(Details.CODE_ID, clientSession.getId());
@@ -557,14 +558,15 @@ public class LoginActionsService {
}
if (!AuthenticationManager.isSessionValid(realm, userSession)) {
AuthenticationManager.logout(session, realm, userSession, uriInfo, clientConnection);
AuthenticationManager.logout(session, realm, userSession, uriInfo, clientConnection, headers);
event.error(Errors.INVALID_CODE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Session not active");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Session not active", headers);
}
event.session(userSession);
LoginProtocol protocol = session.getProvider(LoginProtocol.class, clientSession.getAuthMethod());
protocol.setRealm(realm)
.setHttpHeaders(headers)
.setUriInfo(uriInfo);
if (formData.containsKey("cancel")) {
event.error(Errors.REJECTED_BY_USER);
@@ -598,7 +600,7 @@ public class LoginActionsService {
String error = Validation.validateUpdateProfileForm(formData);
if (error != null) {
return Flows.forms(session, realm, null, uriInfo).setUser(user).setError(error)
return Flows.forms(session, realm, null, uriInfo, headers).setUser(user).setError(error)
.setClientSessionCode(accessCode.getCode())
.createResponse(RequiredAction.UPDATE_PROFILE);
}
@@ -616,7 +618,7 @@ public class LoginActionsService {
// check for duplicated email
if (userByEmail != null && !userByEmail.getId().equals(user.getId())) {
return Flows.forms(session, realm, null, uriInfo).setUser(user).setError(Messages.EMAIL_EXISTS)
return Flows.forms(session, realm, null, uriInfo, headers).setUser(user).setError(Messages.EMAIL_EXISTS)
.setClientSessionCode(accessCode.getCode())
.createResponse(RequiredAction.UPDATE_PROFILE);
}
@@ -655,7 +657,7 @@ public class LoginActionsService {
String totp = formData.getFirst("totp");
String totpSecret = formData.getFirst("totpSecret");
LoginFormsProvider loginForms = Flows.forms(session, realm, null, uriInfo).setUser(user);
LoginFormsProvider loginForms = Flows.forms(session, realm, null, uriInfo, headers).setUser(user);
if (Validation.isEmpty(totp)) {
return loginForms.setError(Messages.MISSING_TOTP)
.setClientSessionCode(accessCode.getCode())
@@ -700,7 +702,7 @@ public class LoginActionsService {
String passwordNew = formData.getFirst("password-new");
String passwordConfirm = formData.getFirst("password-confirm");
LoginFormsProvider loginForms = Flows.forms(session, realm, null, uriInfo).setUser(user);
LoginFormsProvider loginForms = Flows.forms(session, realm, null, uriInfo, headers).setUser(user);
if (Validation.isEmpty(passwordNew)) {
return loginForms.setError(Messages.MISSING_PASSWORD)
.setClientSessionCode(accessCode.getCode())
@@ -757,7 +759,7 @@ public class LoginActionsService {
UserSessionModel userSession = clientSession.getUserSession();
initEvent(clientSession);
return Flows.forms(session, realm, null, uriInfo)
return Flows.forms(session, realm, null, uriInfo, headers)
.setClientSessionCode(accessCode.getCode())
.setUser(userSession.getUser())
.createResponse(RequiredAction.VERIFY_EMAIL);
@@ -775,11 +777,11 @@ public class LoginActionsService {
}
ClientSessionCode accessCode = checks.clientCode;
accessCode.setRequiredAction(RequiredAction.UPDATE_PASSWORD);
return Flows.forms(session, realm, null, uriInfo)
return Flows.forms(session, realm, null, uriInfo, headers)
.setClientSessionCode(accessCode.getCode())
.createResponse(RequiredAction.UPDATE_PASSWORD);
} else {
return Flows.forms(session, realm, null, uriInfo)
return Flows.forms(session, realm, null, uriInfo, headers)
.setClientSessionCode(code)
.createPasswordReset();
}
@@ -792,16 +794,16 @@ public class LoginActionsService {
final MultivaluedMap<String, String> formData) {
event.event(EventType.SEND_RESET_PASSWORD);
if (!checkSsl()) {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required", headers);
}
if (!realm.isEnabled()) {
event.error(Errors.REALM_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.", headers);
}
ClientSessionCode accessCode = ClientSessionCode.parse(code, session, realm);
if (accessCode == null) {
event.error(Errors.INVALID_CODE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.", headers);
}
ClientSessionModel clientSession = accessCode.getClientSession();
@@ -810,11 +812,11 @@ public class LoginActionsService {
ClientModel client = clientSession.getClient();
if (client == null) {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo,
"Unknown login requester.");
"Unknown login requester.", headers);
}
if (!client.isEnabled()) {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo,
"Login requester not enabled.");
"Login requester not enabled.", headers);
}
event.client(client.getClientId())
@@ -857,13 +859,13 @@ public class LoginActionsService {
} catch (EmailException e) {
event.error(Errors.EMAIL_SEND_FAILED);
logger.error("Failed to send password reset email", e);
return Flows.forms(this.session, realm, client, uriInfo).setError("emailSendError")
return Flows.forms(this.session, realm, client, uriInfo, headers).setError("emailSendError")
.setClientSessionCode(accessCode.getCode())
.createErrorPage();
}
}
return Flows.forms(session, realm, client, uriInfo).setSuccess("emailSent").setClientSessionCode(accessCode.getCode()).createPasswordReset();
return Flows.forms(session, realm, client, uriInfo, headers).setSuccess("emailSent").setClientSessionCode(accessCode.getCode()).createPasswordReset();
}
private Response redirectOauth(UserModel user, ClientSessionCode accessCode, ClientSessionModel clientSession, UserSessionModel userSession) {

View File

@@ -26,6 +26,7 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
@@ -37,16 +38,16 @@ public class Flows {
private Flows() {
}
public static LoginFormsProvider forms(KeycloakSession session, RealmModel realm, ClientModel client, UriInfo uriInfo) {
return session.getProvider(LoginFormsProvider.class).setRealm(realm).setUriInfo(uriInfo).setClient(client);
public static LoginFormsProvider forms(KeycloakSession session, RealmModel realm, ClientModel client, UriInfo uriInfo, HttpHeaders headers) {
return session.getProvider(LoginFormsProvider.class).setRealm(realm).setUriInfo(uriInfo).setClient(client).setHttpHeaders(headers);
}
public static ErrorFlows errors() {
return new ErrorFlows();
}
public static Response forwardToSecurityFailurePage(KeycloakSession session, RealmModel realm, UriInfo uriInfo, String message) {
return Flows.forms(session, realm, null, uriInfo).setError(message).createErrorPage();
public static Response forwardToSecurityFailurePage(KeycloakSession session, RealmModel realm, UriInfo uriInfo, String message, HttpHeaders headers) {
return Flows.forms(session, realm, null, uriInfo, headers).setError(message).createErrorPage();
}