Initial html sdk containing login and registration forms
This commit is contained in:
parent
75dd88cf5d
commit
612b9c2dda
11 changed files with 382 additions and 477 deletions
|
@ -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>
|
||||
|
|
|
@ -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 {
|
||||
|
||||
}
|
|
@ -25,18 +25,20 @@ 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
|
||||
|
@ -44,6 +46,8 @@ public class SdkResource {
|
|||
|
||||
private String callbackUrl;
|
||||
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String[] providers;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"id" : "acme-corp",
|
||||
"name" : "Acme Corp",
|
||||
"providers" : [ "google", "twitter" ]
|
||||
}
|
|
@ -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
|
@ -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);
|
||||
});
|
|
@ -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;
|
||||
}());
|
|
@ -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>
|
||||
|
|
|
@ -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>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>
|
||||
<input id=firstname type=text data-ng-model=firstname />
|
||||
|
@ -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>
|
||||
|
||||
|
|
Loading…
Reference in a new issue