finish protocol refactoring

This commit is contained in:
Bill Burke 2014-10-01 14:19:59 -04:00
parent 546d45b009
commit cbc383d494
5 changed files with 119 additions and 96 deletions

View file

@ -631,7 +631,6 @@ public class OpenIDConnectService {
* *
*/ */
private class FrontPageInitializer { private class FrontPageInitializer {
protected String code;
protected String clientId; protected String clientId;
protected String redirect; protected String redirect;
protected String state; protected String state;
@ -642,11 +641,7 @@ public class OpenIDConnectService {
protected ClientSessionModel clientSession; protected ClientSessionModel clientSession;
public Response processInput() { public Response processInput() {
if (code != null) { event.client(clientId).detail(Details.REDIRECT_URI, redirect).detail(Details.RESPONSE_TYPE, "code");
event.detail(Details.CODE_ID, code);
} else {
event.client(clientId).detail(Details.REDIRECT_URI, redirect).detail(Details.RESPONSE_TYPE, "code");
}
if (!checkSsl()) { if (!checkSsl()) {
event.error(Errors.SSL_REQUIRED); event.error(Errors.SSL_REQUIRED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required"); return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required");
@ -657,65 +652,43 @@ public class OpenIDConnectService {
} }
clientSession = null; clientSession = null;
if (code != null) { if (state == null) {
ClientSessionCode clientCode = ClientSessionCode.parse(code, session, realm); event.error(Errors.STATE_PARAM_NOT_FOUND);
if (clientCode == null) { return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid state param.");
event.error(Errors.INVALID_CODE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.");
}
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.");
}
clientSession = clientCode.getClientSession();
if (!clientSession.getAuthMethod().equals(OpenIDConnect.LOGIN_PROTOCOL)) {
event.error(Errors.INVALID_CODE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid protocol, please login again through your application.");
}
state = clientSession.getNote(OpenIDConnect.STATE_PARAM);
scopeParam = clientSession.getNote(OpenIDConnect.SCOPE_PARAM);
responseType = clientSession.getNote(OpenIDConnect.RESPONSE_TYPE_PARAM);
loginHint = clientSession.getNote(OpenIDConnect.LOGIN_HINT_PARAM);
prompt = clientSession.getNote(OpenIDConnect.PROMPT_PARAM);
} else {
if (state == null) {
event.error(Errors.STATE_PARAM_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid state param.");
}
ClientModel client = realm.findClient(clientId);
if (client == null) {
event.error(Errors.CLIENT_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.");
}
if (!client.isEnabled()) {
event.error(Errors.CLIENT_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.");
}
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");
}
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");
}
redirect = verifyRedirectUri(uriInfo, redirect, realm, client);
if (redirect == null) {
event.error(Errors.INVALID_REDIRECT_URI);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect_uri.");
}
clientSession = session.sessions().createClientSession(realm, client);
clientSession.setAuthMethod(OpenIDConnect.LOGIN_PROTOCOL);
clientSession.setRedirectUri(redirect);
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
clientSession.setNote(OpenIDConnect.STATE_PARAM, state);
if (scopeParam != null) clientSession.setNote(OpenIDConnect.SCOPE_PARAM, scopeParam);
if (responseType != null) clientSession.setNote(OpenIDConnect.RESPONSE_TYPE_PARAM, responseType);
if (loginHint != null) clientSession.setNote(OpenIDConnect.LOGIN_HINT_PARAM, loginHint);
if (prompt != null) clientSession.setNote(OpenIDConnect.PROMPT_PARAM, prompt);
} }
ClientModel client = realm.findClient(clientId);
if (client == null) {
event.error(Errors.CLIENT_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.");
}
if (!client.isEnabled()) {
event.error(Errors.CLIENT_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.");
}
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");
}
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");
}
redirect = verifyRedirectUri(uriInfo, redirect, realm, client);
if (redirect == null) {
event.error(Errors.INVALID_REDIRECT_URI);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect_uri.");
}
clientSession = session.sessions().createClientSession(realm, client);
clientSession.setAuthMethod(OpenIDConnect.LOGIN_PROTOCOL);
clientSession.setRedirectUri(redirect);
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
clientSession.setNote(OpenIDConnect.STATE_PARAM, state);
if (scopeParam != null) clientSession.setNote(OpenIDConnect.SCOPE_PARAM, scopeParam);
if (responseType != null) clientSession.setNote(OpenIDConnect.RESPONSE_TYPE_PARAM, responseType);
if (loginHint != null) clientSession.setNote(OpenIDConnect.LOGIN_HINT_PARAM, loginHint);
if (prompt != null) clientSession.setNote(OpenIDConnect.PROMPT_PARAM, prompt);
return null; return null;
} }
} }
@ -726,7 +699,6 @@ public class OpenIDConnectService {
* @See <a href="http://tools.ietf.org/html/rfc6749#section-4.1">http://tools.ietf.org/html/rfc6749#section-4.1</a> * @See <a href="http://tools.ietf.org/html/rfc6749#section-4.1">http://tools.ietf.org/html/rfc6749#section-4.1</a>
* *
* *
* @param code
* @param responseType * @param responseType
* @param redirect * @param redirect
* @param clientId * @param clientId
@ -737,8 +709,7 @@ public class OpenIDConnectService {
*/ */
@Path("login") @Path("login")
@GET @GET
public Response loginPage(@QueryParam("code") String code, public Response loginPage(@QueryParam(OpenIDConnect.RESPONSE_TYPE_PARAM) String responseType,
@QueryParam(OpenIDConnect.RESPONSE_TYPE_PARAM) String responseType,
@QueryParam(OpenIDConnect.REDIRECT_URI_PARAM) String redirect, @QueryParam(OpenIDConnect.REDIRECT_URI_PARAM) String redirect,
@QueryParam(OpenIDConnect.CLIENT_ID_PARAM) String clientId, @QueryParam(OpenIDConnect.CLIENT_ID_PARAM) String clientId,
@QueryParam(OpenIDConnect.SCOPE_PARAM) String scopeParam, @QueryParam(OpenIDConnect.SCOPE_PARAM) String scopeParam,
@ -747,7 +718,6 @@ public class OpenIDConnectService {
@QueryParam(OpenIDConnect.LOGIN_HINT_PARAM) String loginHint) { @QueryParam(OpenIDConnect.LOGIN_HINT_PARAM) String loginHint) {
event.event(EventType.LOGIN); event.event(EventType.LOGIN);
FrontPageInitializer pageInitializer = new FrontPageInitializer(); FrontPageInitializer pageInitializer = new FrontPageInitializer();
pageInitializer.code = code;
pageInitializer.responseType = responseType; pageInitializer.responseType = responseType;
pageInitializer.redirect = redirect; pageInitializer.redirect = redirect;
pageInitializer.clientId = clientId; pageInitializer.clientId = clientId;
@ -758,14 +728,6 @@ public class OpenIDConnectService {
Response response = pageInitializer.processInput(); Response response = pageInitializer.processInput();
if (response != null) return response; if (response != null) return response;
ClientSessionModel clientSession = pageInitializer.clientSession; ClientSessionModel clientSession = pageInitializer.clientSession;
code = pageInitializer.code;
responseType = pageInitializer.responseType;
redirect = pageInitializer.redirect;
clientId = pageInitializer.clientId ;
scopeParam = pageInitializer.scopeParam;
state = pageInitializer.state;
prompt = pageInitializer.prompt;
loginHint = pageInitializer.loginHint;
@ -822,8 +784,7 @@ public class OpenIDConnectService {
*/ */
@Path("registrations") @Path("registrations")
@GET @GET
public Response registerPage(@QueryParam("code") String code, public Response registerPage(@QueryParam(OpenIDConnect.RESPONSE_TYPE_PARAM) String responseType,
@QueryParam(OpenIDConnect.RESPONSE_TYPE_PARAM) String responseType,
@QueryParam(OpenIDConnect.REDIRECT_URI_PARAM) String redirect, @QueryParam(OpenIDConnect.REDIRECT_URI_PARAM) String redirect,
@QueryParam(OpenIDConnect.CLIENT_ID_PARAM) String clientId, @QueryParam(OpenIDConnect.CLIENT_ID_PARAM) String clientId,
@QueryParam(OpenIDConnect.SCOPE_PARAM) String scopeParam, @QueryParam(OpenIDConnect.SCOPE_PARAM) String scopeParam,
@ -835,7 +796,6 @@ public class OpenIDConnectService {
} }
FrontPageInitializer pageInitializer = new FrontPageInitializer(); FrontPageInitializer pageInitializer = new FrontPageInitializer();
pageInitializer.code = code;
pageInitializer.responseType = responseType; pageInitializer.responseType = responseType;
pageInitializer.redirect = redirect; pageInitializer.redirect = redirect;
pageInitializer.clientId = clientId; pageInitializer.clientId = clientId;

View file

@ -742,7 +742,7 @@ public class AccountService {
private Response login(String path) { private Response login(String path) {
OAuthRedirect oauth = new OAuthRedirect(); OAuthRedirect oauth = new OAuthRedirect();
String authUrl = Urls.realmLoginPage(uriInfo.getBaseUri(), realm.getName()).toString(); String authUrl = OpenIDConnectService.loginPageUrl(uriInfo).build(realm.getName()).toString();
oauth.setAuthUrl(authUrl); oauth.setAuthUrl(authUrl);
oauth.setClientId(Constants.ACCOUNT_MANAGEMENT_APP); oauth.setClientId(Constants.ACCOUNT_MANAGEMENT_APP);

View file

@ -22,6 +22,7 @@
package org.keycloak.services.resources; package org.keycloak.services.resources;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.ClientConnection; import org.keycloak.ClientConnection;
import org.keycloak.email.EmailException; import org.keycloak.email.EmailException;
@ -44,6 +45,7 @@ import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.TimeBasedOTP; import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.protocol.LoginProtocol; import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.oidc.OpenIDConnect;
import org.keycloak.protocol.oidc.OpenIDConnectService; import org.keycloak.protocol.oidc.OpenIDConnectService;
import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.PasswordToken; import org.keycloak.representations.PasswordToken;
@ -61,6 +63,7 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.QueryParam; import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
@ -153,6 +156,15 @@ public class LoginActionsService {
Response response; Response response;
boolean check(String code, ClientSessionModel.Action requiredAction) { boolean check(String code, ClientSessionModel.Action requiredAction) {
if (!check(code)) return false;
if (!clientCode.isValid(requiredAction)) {
event.error(Errors.INVALID_CODE);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid code, please login again through your application.");
}
return true;
}
public boolean check(String code) {
if (!checkSsl()) { if (!checkSsl()) {
event.error(Errors.SSL_REQUIRED); event.error(Errors.SSL_REQUIRED);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required"); response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required");
@ -169,14 +181,68 @@ public class LoginActionsService {
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.");
return false; return false;
} }
if (!clientCode.isValid(requiredAction)) {
event.error(Errors.INVALID_CODE);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid code, please login again through your application.");
}
return true; return true;
} }
} }
/**
* protocol independent login page entry point
*
*
* @param code
* @return
*/
@Path("login")
@GET
public Response loginPage(@QueryParam("code") String code) {
event.event(EventType.LOGIN);
Checks checks = new Checks();
if (!checks.check(code)) {
return checks.response;
}
event.detail(Details.CODE_ID, code);
ClientSessionCode clientSessionCode = checks.clientCode;
ClientSessionModel clientSession = clientSessionCode.getClientSession();
LoginFormsProvider forms = Flows.forms(session, realm, clientSession.getClient(), uriInfo)
.setClientSessionCode(clientSessionCode.getCode());
return forms.createLogin();
}
/**
* protocol independent registration page entry point
*
* @param code
* @return
*/
@Path("registration")
@GET
public Response registerPage(@QueryParam("code") String code) {
event.event(EventType.REGISTER);
if (!realm.isRegistrationAllowed()) {
event.error(Errors.REGISTRATION_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Registration not allowed");
}
Checks checks = new Checks();
if (!checks.check(code)) {
return checks.response;
}
event.detail(Details.CODE_ID, code);
ClientSessionCode clientSessionCode = checks.clientCode;
ClientSessionModel clientSession = clientSessionCode.getClientSession();
authManager.expireIdentityCookie(realm, uriInfo, clientConnection);
return Flows.forms(session, realm, clientSession.getClient(), uriInfo)
.setClientSessionCode(clientSessionCode.getCode())
.createRegistration();
}
/** /**
* URL called after login page. YOU SHOULD NEVER INVOKE THIS DIRECTLY! * URL called after login page. YOU SHOULD NEVER INVOKE THIS DIRECTLY!
* *

View file

@ -138,10 +138,10 @@ public class Urls {
} }
public static URI realmLoginPage(URI baseUri, String realmId) { public static URI realmLoginPage(URI baseUri, String realmId) {
return tokenBase(baseUri).path(OpenIDConnectService.class, "loginPage").build(realmId); return requiredActionsBase(baseUri).path(LoginActionsService.class, "loginPage").build(realmId);
} }
public static UriBuilder realmLogout(URI baseUri) { private static UriBuilder realmLogout(URI baseUri) {
return tokenBase(baseUri).path(OpenIDConnectService.class, "logout"); return tokenBase(baseUri).path(OpenIDConnectService.class, "logout");
} }
@ -150,7 +150,7 @@ public class Urls {
} }
public static URI realmRegisterPage(URI baseUri, String realmId) { public static URI realmRegisterPage(URI baseUri, String realmId) {
return tokenBase(baseUri).path(OpenIDConnectService.class, "registerPage").build(realmId); return requiredActionsBase(baseUri).path(LoginActionsService.class, "registerPage").build(realmId);
} }
public static URI realmInstalledAppUrnCallback(URI baseUri, String realmId) { public static URI realmInstalledAppUrnCallback(URI baseUri, String realmId) {
@ -161,10 +161,6 @@ public class Urls {
return requiredActionsBase(baseUri).path(LoginActionsService.class, "processConsent").build(realmId); return requiredActionsBase(baseUri).path(LoginActionsService.class, "processConsent").build(realmId);
} }
public static URI realmCode(URI baseUri, String realmId) {
return tokenBase(baseUri).path(OpenIDConnectService.class, "accessCodeToToken").build(realmId);
}
public static UriBuilder socialBase(URI baseUri) { public static UriBuilder socialBase(URI baseUri) {
return UriBuilder.fromUri(baseUri).path(SocialResource.class); return UriBuilder.fromUri(baseUri).path(SocialResource.class);
} }

View file

@ -25,6 +25,7 @@ import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.ClassRule; import org.junit.ClassRule;
import org.junit.Ignore;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.events.Details; import org.keycloak.events.Details;
@ -156,12 +157,12 @@ public class AccountTest {
}); });
} }
// @Test @Test
// @Ignore @Ignore
// public void runit() throws Exception { public void runit() throws Exception {
// Thread.sleep(10000000); Thread.sleep(10000000);
//
// } }