Initial html sdk containing login and registration forms

This commit is contained in:
Stian Thorgersen 2013-07-24 12:30:32 +01:00
parent 75dd88cf5d
commit 612b9c2dda
11 changed files with 382 additions and 477 deletions

View file

@ -13,6 +13,12 @@
<description />
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-social</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId>

View file

@ -0,0 +1,9 @@
package org.keycloak.sdk.resources;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath("sdk/api")
public class SdkApplication extends Application {
}

View file

@ -25,24 +25,28 @@ import javax.ws.rs.GET;
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.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.annotation.XmlRootElement;
import org.keycloak.social.util.UriBuilder;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@Path("sdk")
@Path("")
public class SdkResource {
@XmlRootElement
public static class LoginConfig {
private String callbackUrl;
private String id;
private String name;
@ -52,6 +56,10 @@ public class SdkResource {
return callbackUrl;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
@ -64,6 +72,10 @@ public class SdkResource {
this.callbackUrl = callbackUrl;
}
public void setId(String id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
@ -80,38 +92,40 @@ public class SdkResource {
@Context
private UriInfo uriInfo;
/**
* TODO Retrieve configuration for application from IDM
*/
@GET
@Path("config/{application}")
@Path("{application}/login/config")
@Produces(MediaType.APPLICATION_JSON)
public Response getConfig(@PathParam("application") String application) {
String applicationCallbackUrl = null; // TODO Get application callback url
String applicationJavaScriptOrigin = null; // TODO Get application javascript origin
public LoginConfig getLoginConfig(@PathParam("application") String application) {
LoginConfig loginConfig = new LoginConfig();
loginConfig.setId(application);
loginConfig.setName(application);
loginConfig.setCallbackUrl(applicationCallbackUrl);
loginConfig.setProviders(null); // TODO Get configured identity providers for application
loginConfig.setCallbackUrl("http://localhost:8080");
loginConfig.setProviders(new String[] { "google", "twitter" });
return loginConfig;
}
ResponseBuilder response = Response.ok(loginConfig);
if (applicationJavaScriptOrigin != null) {
response.header("Access-Control-Allow-Origin", applicationJavaScriptOrigin);
@GET
@Path("{application}/login")
public Response login(@PathParam("application") String application, @QueryParam("error") String error) {
UriBuilder ub = new UriBuilder(headers, uriInfo, "sdk/login.html").setQueryParam("application", application);
if (error != null) {
ub.setQueryParam("error", error);
}
return response.build();
return Response.seeOther(ub.build()).build();
}
@GET
@Path("login/{application}")
@Produces(MediaType.TEXT_HTML)
public Response login(@PathParam("application") String application) {
return Response.ok(getClass().getResourceAsStream("login.html")).build();
@Path("{application}/register")
public Response register(@PathParam("application") String application, @QueryParam("error") String error) {
UriBuilder ub = new UriBuilder(headers, uriInfo, "sdk/register.html").setQueryParam("application", application);
if (error != null) {
ub.setQueryParam("error", error);
}
return Response.seeOther(ub.build()).build();
}
@GET
@Path("register/{application}")
@Produces(MediaType.TEXT_HTML)
public Response register(@PathParam("application") String application) {
return Response.ok(getClass().getResourceAsStream("register.html")).build();
}
}

View file

@ -1,5 +1,5 @@
/*!
* Bootstrap v2.3.1
* Bootstrap v2.3.2
*
* Copyright 2012 Twitter, Inc
* Licensed under the Apache License v2.0
@ -3009,6 +3009,15 @@ table th[class*="span"],
display: block;
}
.dropdown-backdrop {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 990;
}
.pull-right > .dropdown-menu {
right: 0;
left: auto;

View file

@ -1,5 +0,0 @@
{
"id" : "acme-corp",
"name" : "Acme Corp",
"providers" : [ "google", "twitter" ]
}

View file

@ -1,5 +1,5 @@
/**
* @license AngularJS v1.0.6
* @license AngularJS v1.0.7
* (c) 2010-2012 Google, Inc. http://angularjs.org
* License: MIT
*/

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,25 @@
var keycloakModule = angular.module('keycloak', [ 'ngResource' ]);
keycloakModule.controller('LoginCtrl', function($scope, $resource) {
$scope.config = $resource("example-config.json").get();
keycloakModule.factory('messages', function() {
var messages = {};
messages['user_registered'] = "User registered";
messages['invalid_provider'] = "Social provider not found";
messages['provider_error'] = "Failed to login with social provider";
return messages
});
keycloakModule.factory('queryParams', function($location) {
var queryParams = {};
var locationParameters = window.location.search.substring(1).split("&");
for ( var i = 0; i < locationParameters.length; i++) {
var param = locationParameters[i].split("=");
queryParams[decodeURIComponent(param[0])] = decodeURIComponent(param[1]);
}
return queryParams;
});
keycloakModule.controller('GlobalCtrl', function($scope, $resource, queryParams, messages) {
$scope.config = $resource("/keycloak-server/sdk/api/" + queryParams.application + "/login/config").get();
$scope.info = queryParams.info && (messages[queryParams.info] || queryParams.info);
$scope.error = queryParams.error && (messages[queryParams.error] || queryParams.error);
});

View file

@ -1,248 +0,0 @@
window.Keycloak = (function () {
var queryParameters = function (name) {
var parameters = window.location.search.substring(1).split("&");
for (var i = 0; i < parameters.length; i++) {
var param = parameters[i].split("=");
if (decodeURIComponent(param[0]) == name) {
return decodeURIComponent(param[1]);
}
}
};
var messageError = queryParameters("error");
var messageInfo = queryParameters("info");
var keycloak = {
appKey: queryParameters("app"),
token: queryParameters("token"),
baseUrl: "/ejs-identity",
get loginUrl() {
return this.baseUrl + "/api/login/" + this.appKey;
},
get registerUrl() {
return this.baseUrl + "/api/register/" + this.appKey;
},
get userInfoUrl() {
return this.baseUrl + "/api/auth/userinfo?appKey=" + this.appKey;
}
};
keycloak.getConfig = function (success, error) {
var req = new XMLHttpRequest();
req.open("GET", keycloak.loginUrl);
req.setRequestHeader("Accept", "application/json");
req.onreadystatechange = function () {
if (req.readyState == 4) {
if (req.status == 200) {
var config = JSON.parse(req.responseText);
if (success) {
success(config);
}
} else {
if (error) {
error(req.status);
}
}
}
};
req.send();
};
keycloak.getUser = function (success, error) {
var req = new XMLHttpRequest();
req.open("GET", keycloak.userInfoUrl + "&token=" + keycloak.token);
req.setRequestHeader("Accept", "application/json");
req.onreadystatechange = function () {
if (req.readyState == 4) {
if (req.status == 200) {
var user = JSON.parse(req.responseText);
if (success) {
success(user);
}
} else {
if (error) {
error(req.status);
}
}
}
};
req.send();
};
var createLogin = function(containerId) {
var login = document.createElement("div");
login.setAttribute("class", "keycloak-login");
var container = document.getElementById(containerId);
container.setAttribute("class", "keycloak-login-container");
container.innerHTML = null;
container.appendChild(login);
return login;
};
var createHeader = function(text) {
var div = document.createElement("div");
div.setAttribute("class", "keycloak-login-header");
var h = document.createElement("h1");
h.textContent = text;
div.appendChild(h);
return div;
};
var createInput = function(group, name, labelText, type) {
var div = document.createElement("div");
div.setAttribute("class", "keycloak-login-" + group + "-" + name);
var label = document.createElement("label");
label.setAttribute("for", name);
label.textContent = labelText;
div.appendChild(label);
var input = document.createElement("input");
input.setAttribute("name", name);
if (type) {
input.setAttribute("type", type);
} else {
input.setAttribute("type", "text");
}
div.appendChild(input);
return div;
};
var createMessage = function(message, type) {
var div = document.createElement("div");
div.setAttribute("class", "keycloak-login-message-" + type);
if (message == "login_failed") {
div.textContent = "Failed to login";
} else if (message == "register_failed") {
div.textContent = "Failed to register user";
} else if (message = "register_created") {
div.textContent = "Created user";
} else {
div.textContent = message;
}
return div;
};
keycloak.renderLoginForm = function (containerId) {
var success = function (config) {
var login = createLogin(containerId);
login.appendChild(createHeader("Login to " + config.name));
if (messageError) {
login.appendChild(createMessage(messageError, "warn"));
}
if (messageInfo) {
login.appendChild(createMessage(messageInfo, "info"));
}
var standardLogin = document.createElement("div");
standardLogin.setAttribute("class", "keycloak-login-standard");
login.appendChild(standardLogin);
var form = document.createElement("form");
form.setAttribute("action", keycloak.loginUrl);
form.setAttribute("method", "post");
standardLogin.appendChild(form);
form.appendChild(createInput("standard", "username", "Username"));
form.appendChild(createInput("standard", "password", "Password", "password"));
var buttonsDiv = document.createElement("div");
buttonsDiv.setAttribute("class", "keycloak-login-buttons");
form.appendChild(buttonsDiv);
var submitButton = document.createElement("button");
submitButton.setAttribute("type", "submit");
submitButton.textContent = "Login";
buttonsDiv.appendChild(submitButton);
var registerButton = document.createElement("button");
registerButton.setAttribute("type", "button");
registerButton.setAttribute("onclick", "location.href='" + keycloak.registerUrl + "'");
registerButton.textContent = "Register";
buttonsDiv.appendChild(registerButton);
var cancelButton = document.createElement("button");
cancelButton.setAttribute("type", "button");
cancelButton.setAttribute("onclick", "location.href='" + config.callbackUrl + "'");
cancelButton.textContent = "Cancel";
buttonsDiv.appendChild(cancelButton);
var socialLogin = document.createElement("div");
socialLogin.setAttribute("class", "keycloak-login-social");
login.appendChild(socialLogin);
for (var i = 0; i < config.providerConfigs.length; i++) {
var provider = config.providerConfigs[i];
var providerLink = document.createElement("a");
providerLink.setAttribute("href", provider.loginUri);
socialLogin.appendChild(providerLink);
var providerImage = document.createElement("img");
providerImage.setAttribute("src", provider.icon);
providerLink.appendChild(providerImage);
}
};
var error = function() {
var login = createLogin(containerId);
login.appendChild(createHeader("Invalid"));
login.appendChild(createMessage("Invalid application key", "warn"));
};
keycloak.getConfig(success, error);
};
keycloak.renderRegistrationForm = function (containerId) {
var login = createLogin(containerId);
var success = function (config) {
login.appendChild(createHeader("Register with " + config.name));
if (messageError) {
login.appendChild(createMessage(messageError, "warn"));
}
var form = document.createElement("form");
form.setAttribute("action", keycloak.registerUrl);
form.setAttribute("method", "post");
login.appendChild(form);
form.appendChild(createInput("register", "username", "Username"));
form.appendChild(createInput("register", "email", "Email", "email"));
form.appendChild(createInput("register", "firstName", "First name"));
form.appendChild(createInput("register", "lastName", "Last name"));
form.appendChild(createInput("register", "password", "Password", "password"));
var buttonsDiv = document.createElement("div");
buttonsDiv.setAttribute("class", "keycloak-login-buttons");
form.appendChild(buttonsDiv);
var submitButton = document.createElement("button");
submitButton.setAttribute("type", "submit");
submitButton.textContent = "Register";
buttonsDiv.appendChild(submitButton);
var cancelButton = document.createElement("button");
cancelButton.setAttribute("type", "button");
cancelButton.setAttribute("onclick", "location.href='" + keycloak.loginUrl + "'");
cancelButton.textContent = "Cancel";
buttonsDiv.appendChild(cancelButton);
};
keycloak.getConfig(success);
};
return keycloak;
}());

View file

@ -11,10 +11,13 @@
</head>
<body class=keycloak-login-page data-ng-app=keycloak>
<div id=keycloak-login-container data-ng-controller=LoginCtrl>
<div id=keycloak-login-container data-ng-controller=GlobalCtrl>
<div id=keycloak-login-standard>
<h1>Login to {{config.name}}</h1>
<div class="alert alert-info" data-ng-show="info">{{info}}</div>
<div class="alert alert-error" data-ng-show="error">{{error}}</div>
<form action="#">
<label for=username>Username</label>
<input id=username type=text data-ng-model=username required />
@ -22,7 +25,11 @@
<label for=password>Password</label>
<input id=password type=text data-ng-model=password required />
<div><button class="btn btn-primary" id=keycloak-login-submit type=submit>Login</button></div>
<div>
<button class="btn btn-primary" id=keycloak-login-submit type=submit>Login</button>
<a class="btn" href="register.html?application={{config.id}}">Register</a>
<a class="btn" href="{{config.callbackUrl}}">Cancel</a>
</div>
</form>
</div>
@ -30,7 +37,7 @@
<h3>Login with</h3>
<div data-ng-repeat="p in config.providers">
<a href="/social/{{config.id}}/provider/{{p}}"><img data-ng-src="icons/{{p}}.png" /></a>
<a href="/keycloak-server/social/api/{{config.id}}/auth/{{p}}"><img data-ng-src="icons/{{p}}.png" /></a>
</div>
</div>
</div>

View file

@ -11,9 +11,12 @@
</head>
<body class=keycloak-login-page data-ng-app=keycloak>
<div id=keycloak-login-container data-ng-controller=LoginCtrl>
<div id=keycloak-login-container data-ng-controller=GlobalCtrl>
<div id=keycloak-login-standard>
<h1>Register with {{config.name}}</h1>
<div class="alert alert-info" data-ng-show="info">{{info}}</div>
<div class="alert alert-error" data-ng-show="error">{{error}}</div>
<form action="#">
<label for=firstname>Firstname</label>
@ -34,7 +37,10 @@
<label for=password-confirm>Password confirmation</label>
<input id=password-confirm type=text data-ng-model=passwordConfirm required pattern="{{password}}" title="Passwords don't match" />
<div><button class="btn btn-primary" id=keycloak-login-submit type=submit>Register</button></div>
<div>
<button class="btn btn-primary" id=keycloak-login-submit type=submit>Register</button>
<a class="btn" href="{{config.callbackUrl}}">Cancel</a>
</div>
</form>
</div>