diff --git a/integration/js/src/main/resources/keycloak.js b/integration/js/src/main/resources/keycloak.js index 82f92d2410..d412f198c1 100755 --- a/integration/js/src/main/resources/keycloak.js +++ b/integration/js/src/main/resources/keycloak.js @@ -585,7 +585,7 @@ promise.setSuccess(); } - var src = getRealmUrl() + '/login-status-iframe.html?client_id=' + encodeURIComponent(kc.clientId) + '&origin=' + getOrigin(); + var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html?client_id=' + encodeURIComponent(kc.clientId) + '&origin=' + getOrigin(); iframe.setAttribute('src', src ); iframe.style.display = 'none'; document.body.appendChild(iframe); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java index 71d7b199d3..7ee021b9a7 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java @@ -7,8 +7,10 @@ import org.jboss.resteasy.spi.BadRequestException; import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpResponse; import org.jboss.resteasy.spi.NotAcceptableException; +import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.UnauthorizedException; import org.keycloak.ClientConnection; +import org.keycloak.Config; import org.keycloak.OAuth2Constants; import org.keycloak.OAuthErrorException; import org.keycloak.RSATokenVerifier; @@ -33,12 +35,14 @@ import org.keycloak.services.ForbiddenException; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager.AuthenticationStatus; import org.keycloak.services.managers.ClientSessionCode; +import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resources.Cors; import org.keycloak.services.resources.RealmsResource; import org.keycloak.services.resources.flows.Flows; import org.keycloak.services.resources.flows.Urls; import org.keycloak.util.Base64Url; import org.keycloak.util.BasicAuthHelper; +import org.keycloak.util.StreamUtil; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -46,8 +50,10 @@ import javax.ws.rs.HeaderParam; import javax.ws.rs.OPTIONS; import javax.ws.rs.POST; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; +import javax.ws.rs.core.CacheControl; import javax.ws.rs.core.Context; import javax.ws.rs.core.Cookie; import javax.ws.rs.core.HttpHeaders; @@ -58,6 +64,8 @@ import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; import javax.ws.rs.ext.Providers; +import java.io.IOException; +import java.io.InputStream; import java.net.URI; import java.util.HashMap; import java.util.HashSet; @@ -169,6 +177,64 @@ public class OpenIDConnectService { return uriBuilder.path(OpenIDConnectService.class, "refreshAccessToken"); } + /** + * + * + * @param client_id + * @param origin + * @return + */ + @Path("login-status-iframe.html") + @GET + @Produces(MediaType.TEXT_HTML) + public Response getLoginStatusIframe(@QueryParam("client_id") String client_id, + @QueryParam("origin") String origin) { + ClientModel client = realm.findClient(client_id); + if (client == null) { + throw new NotFoundException("could not find client: " + client_id); + } + + InputStream is = getClass().getClassLoader().getResourceAsStream("login-status-iframe.html"); + if (is == null) throw new NotFoundException("Could not find login-status-iframe.html "); + + boolean valid = false; + for (String o : client.getWebOrigins()) { + if (o.equals("*") || o.equals(origin)) { + valid = true; + break; + } + } + + for (String r : OpenIDConnectService.resolveValidRedirects(uriInfo, client.getRedirectUris())) { + int i = r.indexOf('/', 8); + if (i != -1) { + r = r.substring(0, i); + } + + if (r.equals(origin)) { + valid = true; + break; + } + } + + if (!valid) { + throw new BadRequestException("Invalid origin"); + } + + try { + String file = StreamUtil.readString(is); + file = file.replace("ORIGIN", origin); + + CacheControl cacheControl = new CacheControl(); + cacheControl.setNoTransform(false); + cacheControl.setMaxAge(Config.scope("theme").getInt("staticMaxAge", -1)); + + return Response.ok(file).cacheControl(cacheControl).build(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + /** * Direct grant REST invocation. One stop call to obtain an access token. @@ -730,15 +796,8 @@ public class OpenIDConnectService { ClientSessionModel clientSession = pageInitializer.clientSession; - - AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, clientConnection, headers); - if (authResult != null) { - UserModel user = authResult.getUser(); - UserSessionModel userSession = authResult.getSession(); - TokenManager.attachClientSession(userSession, clientSession); - event.user(user).session(userSession).detail(Details.AUTH_METHOD, "sso"); - return authManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event); - } + response = authManager.checkNonFormAuthentication(session, clientSession, realm, uriInfo, request, clientConnection, headers, event); + if (response != null) return response; if (prompt != null && prompt.equals("none")) { OpenIDConnect oauth = new OpenIDConnect(session, realm, request, uriInfo, clientConnection); diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index 2457705fb1..20616876fc 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -23,6 +23,7 @@ import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.LoginProtocol; +import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.representations.AccessToken; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.services.resources.RealmsResource; @@ -193,6 +194,23 @@ public class AuthenticationManager { return authResult; } + public Response checkNonFormAuthentication(KeycloakSession session, ClientSessionModel clientSession, RealmModel realm, UriInfo uriInfo, + HttpRequest request, + ClientConnection clientConnection, HttpHeaders headers, + EventBuilder event) { + AuthResult authResult = authenticateIdentityCookie(session, realm, uriInfo, clientConnection, headers, true); + if (authResult != null) { + UserModel user = authResult.getUser(); + UserSessionModel userSession = authResult.getSession(); + TokenManager.attachClientSession(userSession, clientSession); + event.user(user).session(userSession).detail(Details.AUTH_METHOD, "sso"); + return nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event); + } + return null; + } + + + public static Response redirectAfterSuccessfulFlow(KeycloakSession session, RealmModel realm, UserSessionModel userSession, ClientSessionModel clientSession, HttpRequest request, UriInfo uriInfo, ClientConnection clientConnection) { diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java index dfe3c9ccb0..785b1014ac 100755 --- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java @@ -14,6 +14,7 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.protocol.LoginProtocol; import org.keycloak.protocol.LoginProtocolFactory; +import org.keycloak.protocol.oidc.OpenIDConnect; import org.keycloak.protocol.oidc.OpenIDConnectService; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.BruteForceProtector; @@ -76,65 +77,25 @@ public class RealmsResource { return base.path(RealmsResource.class).path(RealmsResource.class, "getAccountService"); } - /** - * - * - * @param name - * @param client_id - * @return - */ @Path("{realm}/login-status-iframe.html") @GET @Produces(MediaType.TEXT_HTML) + @Deprecated public Response getLoginStatusIframe(final @PathParam("realm") String name, @QueryParam("client_id") String client_id, @QueryParam("origin") String origin) { + // backward compatibility RealmManager realmManager = new RealmManager(session); RealmModel realm = locateRealm(name, realmManager); - ClientModel client = realm.findClient(client_id); - if (client == null) { - throw new NotFoundException("could not find client: " + client_id); - } + EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder(); + AuthenticationManager authManager = new AuthenticationManager(protector); - InputStream is = getClass().getClassLoader().getResourceAsStream("login-status-iframe.html"); - if (is == null) throw new NotFoundException("Could not find login-status-iframe.html "); + LoginProtocolFactory factory = (LoginProtocolFactory)session.getKeycloakSessionFactory().getProviderFactory(LoginProtocol.class, OpenIDConnect.LOGIN_PROTOCOL); + OpenIDConnectService endpoint = (OpenIDConnectService)factory.createProtocolEndpoint(realm, event, authManager); - boolean valid = false; - for (String o : client.getWebOrigins()) { - if (o.equals("*") || o.equals(origin)) { - valid = true; - break; - } - } + ResteasyProviderFactory.getInstance().injectProperties(endpoint); + return endpoint.getLoginStatusIframe(client_id, origin); - for (String r : OpenIDConnectService.resolveValidRedirects(uriInfo, client.getRedirectUris())) { - int i = r.indexOf('/', 8); - if (i != -1) { - r = r.substring(0, i); - } - - if (r.equals(origin)) { - valid = true; - break; - } - } - - if (!valid) { - throw new BadRequestException("Invalid origin"); - } - - try { - String file = StreamUtil.readString(is); - file = file.replace("ORIGIN", origin); - - CacheControl cacheControl = new CacheControl(); - cacheControl.setNoTransform(false); - cacheControl.setMaxAge(Config.scope("theme").getInt("staticMaxAge", -1)); - - return Response.ok(file).cacheControl(cacheControl).build(); - } catch (IOException e) { - throw new RuntimeException(e); - } } @Path("{realm}/protocol/{protocol}")