keycloak-aplcache
Changes
examples/cordova/.gitignore 4(+4 -0)
examples/cordova/example-realm.json 72(+72 -0)
examples/cordova/README.md 31(+31 -0)
examples/cordova/www/config.xml 14(+14 -0)
examples/cordova/www/index.html 78(+78 -0)
examples/js-console/README.md 17(+17 -0)
forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java 19(+4 -15)
forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/ReferrerBean.java 12(+5 -7)
Details
examples/cordova/.gitignore 4(+4 -0)
diff --git a/examples/cordova/.gitignore b/examples/cordova/.gitignore
new file mode 100644
index 0000000..758f6ed
--- /dev/null
+++ b/examples/cordova/.gitignore
@@ -0,0 +1,4 @@
+platforms
+plugins
+www/keycloak.js
+www/keycloak.json
examples/cordova/example-realm.json 72(+72 -0)
diff --git a/examples/cordova/example-realm.json b/examples/cordova/example-realm.json
new file mode 100755
index 0000000..37e899e
--- /dev/null
+++ b/examples/cordova/example-realm.json
@@ -0,0 +1,72 @@
+{
+ "realm": "example",
+ "enabled": true,
+ "sslNotRequired": true,
+ "registrationAllowed": true,
+ "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+ "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "requiredCredentials": [ "password" ],
+ "users" : [
+ {
+ "username" : "user",
+ "enabled": true,
+ "email" : "sample-user@example",
+ "firstName": "Sample",
+ "lastName": "User",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ]
+ }
+ ],
+ "roles" : {
+ "realm" : [
+ {
+ "name": "user",
+ "description": "User privileges"
+ },
+ {
+ "name": "admin",
+ "description": "Administrator privileges"
+ }
+ ]
+ },
+ "roleMappings": [
+ {
+ "username": "user",
+ "roles": ["user"]
+ }
+ ],
+ "scopeMappings": [
+ {
+ "client": "cordova",
+ "roles": ["user"]
+ }
+ ],
+ "applications": [
+ {
+ "name": "cordova",
+ "enabled": true,
+ "publicClient": true,
+ "redirectUris": [
+ "http://localhost"
+ ]
+ }
+ ],
+ "applicationRoleMappings": {
+ "account": [
+ {
+ "username": "user",
+ "roles": ["view-profile", "manage-account"]
+ }
+ ]
+ },
+ "applicationScopeMappings": {
+ "account": [
+ {
+ "client": "cordova",
+ "roles": ["view-profile"]
+ }
+ ]
+ }
+}
examples/cordova/README.md 31(+31 -0)
diff --git a/examples/cordova/README.md b/examples/cordova/README.md
new file mode 100644
index 0000000..9173783
--- /dev/null
+++ b/examples/cordova/README.md
@@ -0,0 +1,31 @@
+Basic Cordova Example
+=====================
+
+Before running this example you need to have Cordova installed with a phone or emulator available.
+
+Start and configure Keycloak
+----------------------------
+
+Start Keycloak bound to an IP address available to the phone or emulator. For example:
+
+ bin/standalone.sh -b 192.168.0.10
+
+Open the Keycloak admin console, click on Add Realm, click on 'Choose a JSON file', selct example-realm.json and click Upload.
+
+Navigate to applications, click on 'Cordova', select 'Installation' and in the 'Format option' drop-down select 'keycloak.json'. Download this file to the www folder.
+
+Download '/js/keycloak.js' from the server to the www folder as well. For example:
+
+ wget http://192.168.0.10:8080/auth/js/keycloak.js
+
+
+Install to Android phone or emulator
+------------------------------------
+
+ mkdir platforms plugins
+ cordova plugin add org.apache.cordova.inappbrowser
+ cordova platform add android
+ cordova run android
+
+
+Once the application is opened you can login with username: 'user', and password: 'password'.
examples/cordova/www/config.xml 14(+14 -0)
diff --git a/examples/cordova/www/config.xml b/examples/cordova/www/config.xml
new file mode 100644
index 0000000..c568b5a
--- /dev/null
+++ b/examples/cordova/www/config.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<widget xmlns="http://www.w3.org/ns/widgets" xmlns:gap="http://phonegap.com/ns/1.0" id="org.keycloak.examples.cordova" version="1.0.0">
+ <name>Keycloak Auth</name>
+ <description>Keycloak Cordova Example</description>
+ <author href="http://www.keycloak.org">Keycloak Team</author>
+
+ <feature name="http://api.phonegap.com/1.0/device" />
+
+ <preference name="permissions" value="none"/>
+
+ <gap:plugin name="org.apache.cordova.inappbrowser" />
+
+ <access origin="http://*"/>
+</widget>
examples/cordova/www/index.html 78(+78 -0)
diff --git a/examples/cordova/www/index.html b/examples/cordova/www/index.html
new file mode 100644
index 0000000..27d7db1
--- /dev/null
+++ b/examples/cordova/www/index.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Authentication Example</title>
+
+ <script type="text/javascript" charset="utf-8" src="cordova.js"></script>
+ <script type="text/javascript" charset="utf-8" src="keycloak.js"></script>
+ <script type="text/javascript" charset="utf-8">
+ var keycloak = new Keycloak();
+
+ keycloak.onReady = updateState;
+ keycloak.onAuthSuccess = updateState;
+ keycloak.onAuthRefreshSuccess = updateState;
+ keycloak.onAuthLogout = updateState;
+
+ function updateState() {
+ console.debug('Updating state');
+
+ if (keycloak.authenticated) {
+ document.getElementById('authenticated').style.display = 'block';
+ document.getElementById('not-authenticated').style.display = 'none';
+
+ document.getElementById('subject').innerText = keycloak.subject;
+ document.getElementById('username').innerText = keycloak.idToken.preferred_username;
+ document.getElementById('tokenExpires').innerText = new Date(keycloak.tokenParsed.exp * 1000).toLocaleString();
+ document.getElementById('tokenRefreshExpires').innerText = new Date(keycloak.refreshTokenParsed.exp * 1000).toLocaleString();
+ } else {
+ document.getElementById('authenticated').style.display = 'none';
+ document.getElementById('not-authenticated').style.display = 'block';
+ }
+ }
+
+ document.addEventListener("deviceready", function() {
+ console.debug('Device ready');
+
+ keycloak.init('check-sso');
+ }, false);
+ </script>
+</head>
+<body>
+<div id="authenticated" style="display: none;">
+ <div>
+ <button onclick="keycloak.logout()">Log out</button>
+ <button onclick="keycloak.updateToken()">Refresh token</button>
+ <button onclick="keycloak.accountManagement()">Manage account</button>
+ </div>
+ <div>
+ <table>
+ <tr>
+ <td>Subject</td>
+ <td id="subject"></td>
+ </tr>
+ <tr>
+ <td>Username</td>
+ <td id="username"></td>
+ </tr>
+ <tr>
+ <td>Token expires</td>
+ <td id="tokenExpires"></td>
+ </tr>
+ <tr>
+ <td>Refresh token expires</td>
+ <td id="tokenRefreshExpires"></td>
+ </tr>
+ </table>
+ </div>
+</div>
+<div id="not-authenticated" style="display: none;">
+ <div>
+ <button onclick="keycloak.login()">Log in</button>
+ </div>
+ <div>
+ <p>Not authenticated</p>
+ </div>
+</div>
+</body>
+</html>
+
diff --git a/examples/demo-template/customer-app-cli/pom.xml b/examples/demo-template/customer-app-cli/pom.xml
index 2abf670..363bc3e 100755
--- a/examples/demo-template/customer-app-cli/pom.xml
+++ b/examples/demo-template/customer-app-cli/pom.xml
@@ -14,14 +14,6 @@
<name>Customer Portal CLI</name>
<description/>
- <repositories>
- <repository>
- <id>jboss</id>
- <name>jboss repo</name>
- <url>http://repository.jboss.org/nexus/content/groups/public/</url>
- </repository>
- </repositories>
-
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
@@ -33,6 +25,14 @@
<build>
<plugins>
<plugin>
+ <groupId>org.jboss.as.plugins</groupId>
+ <artifactId>jboss-as-maven-plugin</artifactId>
+ <version>7.4.Final</version>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
diff --git a/examples/demo-template/customer-app-js/src/main/webapp/customers/view.html b/examples/demo-template/customer-app-js/src/main/webapp/customers/view.html
index 845a1ff..445876f 100755
--- a/examples/demo-template/customer-app-js/src/main/webapp/customers/view.html
+++ b/examples/demo-template/customer-app-js/src/main/webapp/customers/view.html
@@ -5,7 +5,8 @@
</head>
<body bgcolor="#E3F6CE">
-<p>Goto: <a href="#" onclick="keycloak.logout()">logout</a></p>
+<p>Goto: <a href="http://localhost:8080/product-portal">products</a> | <a href="#" onclick="keycloak.logout()">logout</a> | <a href="#" onclick="keycloak.accountManagement()">manage acct</a></p>
+
User <b id="subject"></b> made this request.
<p><b>User details (from <span id="profileType"></span>)</b></p>
<p>Username: <span id="username"></span></p>
@@ -18,16 +19,11 @@ User <b id="subject"></b> made this request.
<div id="customers"></div>
<script>
- var keycloak = Keycloak({
- clientId: 'customer-portal-js',
- realm: 'demo',
- onload: 'login-required'
- });
+ var keycloak = Keycloak('../keycloak.json');
var loadData = function () {
document.getElementById('subject').innerText = keycloak.subject;
- console.debug(keycloak.idToken);
if (keycloak.idToken) {
document.getElementById('profileType').innerText = 'IDToken';
document.getElementById('username').innerText = keycloak.idToken.preferred_username;
@@ -74,14 +70,14 @@ User <b id="subject"></b> made this request.
var loadFailure = function () {
document.getElementById('customers').innerHTML = '<b>Failed to load data. Check console log</b>';
-
};
var reloadData = function () {
- keycloak.onValidAccessToken(loadData, loadFailure);
+ keycloak.updateToken(10).success(loadData).error(loadFailure);
}
- keycloak.init(loadData, loadFailure);
+ keycloak.onAuthSuccess = loadData;
+ keycloak.init('login-required');
</script>
diff --git a/examples/demo-template/customer-app-js/src/main/webapp/keycloak.json b/examples/demo-template/customer-app-js/src/main/webapp/keycloak.json
new file mode 100644
index 0000000..d73332e
--- /dev/null
+++ b/examples/demo-template/customer-app-js/src/main/webapp/keycloak.json
@@ -0,0 +1,8 @@
+{
+ "realm" : "demo",
+ "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "auth-server-url" : "http://localhost:8080/auth",
+ "ssl-not-required" : true,
+ "resource" : "customer-portal-js",
+ "public-client" : true
+}
\ No newline at end of file
diff --git a/examples/demo-template/testrealm.json b/examples/demo-template/testrealm.json
index a82e9e8..81af756 100755
--- a/examples/demo-template/testrealm.json
+++ b/examples/demo-template/testrealm.json
@@ -52,6 +52,10 @@
"roles": ["user"]
},
{
+ "client": "customer-portal-js",
+ "roles": ["user"]
+ },
+ {
"client": "product-portal",
"roles": ["user"]
}
examples/js-console/README.md 17(+17 -0)
diff --git a/examples/js-console/README.md b/examples/js-console/README.md
new file mode 100644
index 0000000..749c0f3
--- /dev/null
+++ b/examples/js-console/README.md
@@ -0,0 +1,17 @@
+Basic JavaScript Example
+========================
+
+Start and configure Keycloak
+----------------------------
+
+Start Keycloak:
+
+ bin/standalone.sh
+
+Open the Keycloak admin console, click on Add Realm, click on 'Choose a JSON file', selct example-realm.json and click Upload.
+
+Deploy the JS Console to Keycloak by running:
+
+ mvn install jboss-as:deploy
+
+Open the console at http://localhost:8080/js-console and login with username: 'user', and password: 'password'.
diff --git a/examples/js-console/src/main/webapp/index.html b/examples/js-console/src/main/webapp/index.html
index 58513ad..3b9e4a0 100644
--- a/examples/js-console/src/main/webapp/index.html
+++ b/examples/js-console/src/main/webapp/index.html
@@ -1,6 +1,6 @@
<html>
<head>
- <script src="http://localhost:8080/auth/js/keycloak.js"></script>
+ <script src="/auth/js/keycloak.js"></script>
</head>
<body>
@@ -36,7 +36,7 @@
}
function refreshToken(minValidity) {
- keycloak.refreshAccessToken(minValidity).success(function(refreshed) {
+ keycloak.updateToken(minValidity).success(function(refreshed) {
if (refreshed) {
output(keycloak.tokenParsed);
} else {
diff --git a/forms/account-api/src/main/java/org/keycloak/account/Account.java b/forms/account-api/src/main/java/org/keycloak/account/Account.java
index 61e92e2..060b8a5 100644
--- a/forms/account-api/src/main/java/org/keycloak/account/Account.java
+++ b/forms/account-api/src/main/java/org/keycloak/account/Account.java
@@ -25,6 +25,6 @@ public interface Account {
public Account setRealm(RealmModel realm);
- public Account setReferrer(String referrer);
+ public Account setReferrer(String[] referrer);
}
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java
index 1a5cc67..143aa1c 100644
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java
@@ -36,7 +36,7 @@ public class FreeMarkerAccount implements Account {
private UserModel user;
private Response.Status status = Response.Status.OK;
private RealmModel realm;
- private String referrer;
+ private String[] referrer;
public static enum MessageType {SUCCESS, WARNING, ERROR}
@@ -82,9 +82,8 @@ public class FreeMarkerAccount implements Account {
attributes.put("message", new MessageBean(messages.containsKey(message) ? messages.getProperty(message) : message, messageType));
}
- ApplicationModel referrerApp = getReferrer();
- if (referrerApp != null) {
- attributes.put("referrer", new ReferrerBean(referrerApp));
+ if (referrer != null) {
+ attributes.put("referrer", new ReferrerBean(referrer));
}
attributes.put("url", new UrlBean(realm, theme, baseUri));
@@ -114,16 +113,6 @@ public class FreeMarkerAccount implements Account {
}
}
- private ApplicationModel getReferrer() {
- if (referrer != null) {
- ApplicationModel app = realm.getApplicationByName(referrer);
- if (app != null) {
- return app;
- }
- }
- return null;
- }
-
@Override
public Account setError(String message) {
this.message = message;
@@ -164,7 +153,7 @@ public class FreeMarkerAccount implements Account {
}
@Override
- public Account setReferrer(String referrer) {
+ public Account setReferrer(String[] referrer) {
this.referrer = referrer;
return this;
}
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/ReferrerBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/ReferrerBean.java
index 8904bf6..0734a7b 100644
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/ReferrerBean.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/ReferrerBean.java
@@ -1,24 +1,22 @@
package org.keycloak.account.freemarker.model;
-import org.keycloak.models.ApplicationModel;
-
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ReferrerBean {
- private ApplicationModel referrer;
+ private String[] referrer;
- public ReferrerBean(ApplicationModel referrer) {
+ public ReferrerBean(String[] referrer) {
this.referrer = referrer;
}
public String getName() {
- return referrer.getName();
+ return referrer[0];
}
- public String getBaseUrl() {
- return referrer.getBaseUrl();
+ public String getUrl() {
+ return referrer[1];
}
}
diff --git a/forms/common-themes/src/main/resources/theme/account/base/template.ftl b/forms/common-themes/src/main/resources/theme/account/base/template.ftl
index 376a421..d6a2f5e 100644
--- a/forms/common-themes/src/main/resources/theme/account/base/template.ftl
+++ b/forms/common-themes/src/main/resources/theme/account/base/template.ftl
@@ -28,7 +28,7 @@
<div class="navbar-collapse navbar-collapse-1">
<div class="container">
<ul class="nav navbar-nav navbar-utility">
- <#if referrer?has_content && referrer.baseUrl?has_content><li><a href="${referrer.baseUrl}">Back to ${referrer.name}</a></li></#if>
+ <#if referrer?has_content && referrer.url?has_content><li><a href="${referrer.url}" id="referrer">Back to ${referrer.name}</a></li></#if>
<li><a href="${url.logoutUrl}">Sign Out</a></li>
</ul>
</div>
diff --git a/integration/js/src/main/resources/META-INF/resources/js/keycloak.js b/integration/js/src/main/resources/META-INF/resources/js/keycloak.js
index 6860b78..11da6c8 100755
--- a/integration/js/src/main/resources/META-INF/resources/js/keycloak.js
+++ b/integration/js/src/main/resources/META-INF/resources/js/keycloak.js
@@ -4,107 +4,118 @@ var Keycloak = function (config) {
}
var kc = this;
- kc.authenticated = false;
-
- var configPromise = createPromise();
- configPromise.name = 'config';
-
- if (!config) {
- loadConfig('keycloak.json', configPromise);
- } else if (typeof config === 'string') {
- loadConfig(config, configPromise);
- } else {
- if (!config['url']) {
- var scripts = document.getElementsByTagName('script');
- for (var i = 0; i < scripts.length; i++) {
- if (scripts[i].src.match(/.*keycloak\.js/)) {
- config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'));
- break;
- }
- }
- }
+ var adapter;
- if (!config.realm) {
- throw 'realm missing';
- }
+ kc.init = function (init) {
+ kc.authenticated = false;
- if (!config.clientId) {
- throw 'clientId missing';
+ if (window.Cordova) {
+ adapter = loadAdapter('cordova');
+ } else {
+ adapter = loadAdapter();
}
- kc.authServerUrl = config.url;
- kc.realm = config.realm;
- kc.clientId = config.clientId;
+ var promise = createPromise();
- configPromise.setSuccess();
- }
+ var initPromise = createPromise();
+ initPromise.promise.success(function() {
+ kc.onReady && kc.onReady(kc.authenticated);
+ promise.setSuccess(kc.authenticated);
+ }).error(promise.error);
- kc.init = function (init) {
- var promise = createPromise();
- var callback = parseCallback(window.location.href);
+ var configPromise = loadConfig(config);
function processInit() {
+ var callback = parseCallback(window.location.href);
if (callback) {
window.history.replaceState({}, null, location.protocol + '//' + location.host + location.pathname + (callback.fragment ? '#' + callback.fragment : ''));
- processCallback(callback, promise);
+ processCallback(callback, initPromise);
return;
} else if (init) {
if (init.code || init.error) {
- processCallback(init, promise);
+ processCallback(init, initPromise);
return;
} else if (init.token || init.refreshToken) {
setToken(init.token, init.refreshToken);
+ initPromise.setSuccess();
} else if (init == 'login-required') {
- kc.login();
- return;
+ var p = kc.login();
+ if (p) {
+ p.success(function() {
+ initPromise.setSuccess();
+ }).error(function() {
+ initPromise.setError();
+ });
+ };
} else if (init == 'check-sso') {
- window.location = kc.createLoginUrl() + '&prompt=none';
- return;
+ var p = kc.login({ prompt: 'none' });
+ if (p) {
+ p.success(function() {
+ initPromise.setSuccess();
+ }).error(function() {
+ initPromise.setSuccess();
+ });
+ };
+ } else {
+ throw 'invalid init: ' + init;
}
+ } else {
+ initPromise.setSuccess();
}
-
- promise.setSuccess(false);
}
- configPromise.promise.success(processInit);
+ configPromise.success(processInit);
return promise.promise;
}
- kc.login = function (redirectUri) {
- window.location.href = kc.createLoginUrl(redirectUri);
+ kc.login = function (options) {
+ return adapter.login(options);
}
- kc.createLoginUrl = function(redirectUri) {
+ kc.createLoginUrl = function(options) {
var state = createUUID();
sessionStorage.oauthState = state;
var url = getRealmUrl()
+ '/tokens/login'
+ '?client_id=' + encodeURIComponent(kc.clientId)
- + '&redirect_uri=' + getEncodedRedirectUri(redirectUri)
+ + '&redirect_uri=' + encodeURIComponent(adapter.redirectUri(options))
+ '&state=' + encodeURIComponent(state)
+ '&response_type=code';
+ if (options && options.prompt) {
+ url += '&prompt=' + options.prompt;
+ }
+
return url;
}
- kc.logout = function(redirectUri) {
- setToken(null, null);
- window.location.href = kc.createLogoutUrl(redirectUri);
+ kc.logout = function(options) {
+ return adapter.logout(options);
}
- kc.clearToken = function() {
- setToken(null, null);
+ kc.createLogoutUrl = function(options) {
+ var url = getRealmUrl()
+ + '/tokens/logout'
+ + '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options));
+
+ return url;
}
- kc.createLogoutUrl = function(redirectUri) {
+ kc.createAccountUrl = function(options) {
var url = getRealmUrl()
- + '/tokens/logout'
- + '?redirect_uri=' + getEncodedRedirectUri(redirectUri);
+ + '/account'
+ + '?referrer=' + kc.clientId
+ + '&referrer_uri=' + encodeURIComponent(adapter.redirectUri(options));
+
return url;
}
+ kc.accountManagement = function() {
+ return adapter.accountManagement();
+ }
+
kc.hasRealmRole = function (role) {
var access = kc.realmAccess;
return access && access.roles.indexOf(role) >= 0 || false;
@@ -144,7 +155,20 @@ var Keycloak = function (config) {
return promise.promise;
}
- kc.refreshAccessToken = function(minValidity) {
+ kc.isTokenExpired = function(minValidity) {
+ if (!kc.tokenParsed || !kc.refreshToken) {
+ throw 'Not authenticated';
+ }
+
+ var expiresIn = kc.tokenParsed['exp'] - (new Date().getTime() / 1000);
+ if (minValidity) {
+ expiresIn -= minValidity;
+ }
+
+ return expiresIn < 0;
+ }
+
+ kc.updateToken = function(minValidity) {
if (!kc.tokenParsed || !kc.refreshToken) {
throw 'Not authenticated';
}
@@ -152,8 +176,7 @@ var Keycloak = function (config) {
var promise = createPromise();
if (minValidity) {
- var expiresIn = kc.tokenParsed['exp'] - (new Date().getTime() / 1000);
- if (expiresIn > minValidity) {
+ if (!kc.isTokenExpired(minValidity)) {
promise.setSuccess(false);
return promise.promise;
}
@@ -191,15 +214,6 @@ var Keycloak = function (config) {
return promise.promise;
}
- kc.processCallback = function(url) {
- var callback = parseCallback(url);
- if (callback) {
- var promise = createPromise();
- processCallback(callback, promise);
- return promise;
- }
- }
-
function getRealmUrl() {
return kc.authServerUrl + '/rest/realms/' + encodeURIComponent(kc.realm);
}
@@ -231,10 +245,10 @@ var Keycloak = function (config) {
var tokenResponse = JSON.parse(req.responseText);
setToken(tokenResponse['access_token'], tokenResponse['refresh_token']);
kc.onAuthSuccess && kc.onAuthSuccess();
- promise.setSuccess(true);
+ promise && promise.setSuccess();
} else {
kc.onAuthError && kc.onAuthError();
- promise.setError();
+ promise && promise.setError();
}
}
};
@@ -243,33 +257,75 @@ var Keycloak = function (config) {
} else if (error) {
if (prompt != 'none') {
kc.onAuthError && kc.onAuthError();
- promise.setError();
+ promise && promise.setError();
}
}
}
- function loadConfig(url, configPromise) {
- var req = new XMLHttpRequest();
- req.open('GET', url, true);
- req.setRequestHeader('Accept', 'application/json');
+ function loadConfig(url) {
+ var promise = createPromise();
+ var configUrl;
- req.onreadystatechange = function () {
- if (req.readyState == 4) {
- if (req.status == 200) {
- var config = JSON.parse(req.responseText);
+ if (!config) {
+ configUrl = 'keycloak.json';
+ } else if (typeof config === 'string') {
+ configUrl = config;
+ }
+
+ if (configUrl) {
+ var req = new XMLHttpRequest();
+ req.open('GET', configUrl, true);
+ req.setRequestHeader('Accept', 'application/json');
- kc.authServerUrl = config['auth-server-url'];
- kc.realm = config['realm'];
- kc.clientId = config['resource'];
+ req.onreadystatechange = function () {
+ if (req.readyState == 4) {
+ if (req.status == 200) {
+ var config = JSON.parse(req.responseText);
- configPromise.setSuccess();
- } else {
- configPromise.setError();
+ kc.authServerUrl = config['auth-server-url'];
+ kc.realm = config['realm'];
+ kc.clientId = config['resource'];
+
+ promise.setSuccess();
+ } else {
+ promise.setError();
+ }
+ }
+ };
+
+ req.send();
+ } else {
+ if (!config['url']) {
+ var scripts = document.getElementsByTagName('script');
+ for (var i = 0; i < scripts.length; i++) {
+ if (scripts[i].src.match(/.*keycloak\.js/)) {
+ config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'));
+ break;
+ }
}
}
- };
- req.send();
+ if (!config.realm) {
+ throw 'realm missing';
+ }
+
+ if (!config.clientId) {
+ throw 'clientId missing';
+ }
+
+ kc.authServerUrl = config.url;
+ kc.realm = config.realm;
+ kc.clientId = config.clientId;
+
+ promise.setSuccess();
+ }
+
+ return promise.promise;
+ }
+
+ function clearToken() {
+ setToken(null, null);
+ kc.onAuthLogout && kc.onAuthLogout();
}
function setToken(token, refreshToken) {
@@ -310,21 +366,6 @@ var Keycloak = function (config) {
}
}
- function getEncodedRedirectUri(redirectUri) {
- var url;
- if (redirectUri) {
- url = redirectUri;
- } else if (kc.redirectUri) {
- url = kc.redirectUri;
- } else {
- url = (location.protocol + '//' + location.hostname + (location.port && (':' + location.port)) + location.pathname);
- if (location.hash) {
- url += '?redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
- }
- }
- return encodeURI(url);
- }
-
function createUUID() {
var s = [];
var hexDigits = '0123456789abcdef';
@@ -339,7 +380,6 @@ var Keycloak = function (config) {
}
function parseCallback(url) {
-
if (url.indexOf('?') != -1) {
var oauth = {};
@@ -365,7 +405,7 @@ var Keycloak = function (config) {
}
}
- if (oauth.state && oauth.state == sessionStorage.oauthState) {
+ if ((oauth.code || oauth.error) && oauth.state && oauth.state == sessionStorage.oauthState) {
delete sessionStorage.oauthState;
return oauth;
}
@@ -412,6 +452,103 @@ var Keycloak = function (config) {
return p;
}
+ function loadAdapter(type) {
+ if (!type || type == 'default') {
+ return {
+ login: function(options) {
+ window.location.href = kc.createLoginUrl(options);
+ },
+
+ logout: function(options) {
+ window.location.href = kc.createLogoutUrl(options);
+ },
+
+ accountManagement : function() {
+ window.location.href = kc.createAccountUrl();
+ },
+
+ redirectUri: function(options) {
+ if (options && options.redirectUri) {
+ return options.redirectUri;
+ } else if (kc.redirectUri) {
+ return kc.redirectUri;
+ } else {
+ var url = (location.protocol + '//' + location.hostname + (location.port && (':' + location.port)) + location.pathname);
+ if (location.hash) {
+ url += '?redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
+ }
+ return url;
+ }
+ }
+ };
+ }
+
+ if (type == 'cordova') {
+ console.debug('Enabling Cordova support');
+
+ return {
+ login: function(options) {
+ var promise = createPromise();
+
+ var o = 'location=no';
+ if (options && options.prompt == 'none') {
+ o += ',hidden=yes';
+ }
+
+ var loginUrl = kc.createLoginUrl(options);
+ var ref = window.open(loginUrl, '_blank', o);
+ ref.addEventListener('loadstart', function(event) {
+ if (event.url.indexOf('http://localhost') == 0) {
+ var callback = parseCallback(event.url);
+ ref.close();
+ processCallback(callback);
+
+ if (callback.code) {
+ promise.setSuccess();
+ } else {
+ promise.setError();
+ }
+ }
+ });
+
+ return promise.promise;
+ },
+
+ logout: function(options) {
+ var promise = createPromise();
+
+ var logoutUrl = kc.createLogoutUrl(options);
+ var ref = window.open(logoutUrl, '_blank', 'location=no,hidden=yes');
+ ref.addEventListener('loadstart', function(event) {
+ if (event.url.indexOf('http://localhost') == 0) {
+ ref.close();
+ clearToken();
+ promise.setSuccess();
+ }
+ });
+
+ return promise.promise;
+ },
+
+ accountManagement : function() {
+ var accountUrl = kc.createAccountUrl();
+ var ref = window.open(accountUrl, '_blank', 'location=no');
+ ref.addEventListener('loadstart', function(event) {
+ if (event.url.indexOf('http://localhost') == 0) {
+ ref.close();
+ }
+ });
+ },
+
+ redirectUri: function(options) {
+ return 'http://localhost';
+ }
+ }
+ }
+
+ throw 'invalid adapter type: ' + type;
+ }
+
var idTokenProperties = [
"name",
"given_name",
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index 593f3d3..eb53402 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -99,7 +99,7 @@ public class AccountService {
Account account = AccountLoader.load().createAccount(uriInfo).setRealm(realm).setUser(auth.getUser());
- String referrer = getReferrer();
+ String[] referrer = getReferrer();
if (referrer != null) {
account.setReferrer(referrer);
}
@@ -377,13 +377,17 @@ public class AccountService {
uriBuilder.queryParam("path", path);
}
- String referrer = getReferrer();
+ String referrer = uriInfo.getQueryParameters().getFirst("referrer");
if (referrer != null) {
uriBuilder.queryParam("referrer", referrer);
}
- URI accountUri = uriBuilder.build(realm.getName());
+ String referrerUri = uriInfo.getQueryParameters().getFirst("referrer_uri");
+ if (referrerUri != null) {
+ uriBuilder.queryParam("referrer_uri", referrerUri);
+ }
+ URI accountUri = uriBuilder.build(realm.getName());
oauth.setStateCookiePath(accountUri.getRawPath());
return oauth.redirect(uriInfo, accountUri.toString());
@@ -397,20 +401,34 @@ public class AccountService {
return auth;
}
- private String getReferrer() {
+ private String[] getReferrer() {
String referrer = uriInfo.getQueryParameters().getFirst("referrer");
- if (referrer != null) {
- return referrer;
+ if (referrer == null) {
+ return null;
}
- String referrerUrl = headers.getHeaderString("Referer");
- if (referrerUrl != null) {
- for (ApplicationModel a : realm.getApplications()) {
- if (a.getBaseUrl() != null && referrerUrl.startsWith(a.getBaseUrl())) {
- return a.getName();
+ String referrerUri = uriInfo.getQueryParameters().getFirst("referrer_uri");
+
+ ApplicationModel application = realm.getApplicationByName(referrer);
+ if (application != null) {
+ if (referrerUri != null) {
+ referrerUri = TokenService.verifyRedirectUri(referrerUri, application);
+ } else {
+ referrerUri = application.getBaseUrl();
+ }
+
+ if (referrerUri != null) {
+ return new String[] { referrer, referrerUri };
+ }
+ } else if (referrerUri != null) {
+ ClientModel client = realm.getOAuthClient(referrer);
+ if (client != null) {
+ referrerUri = TokenService.verifyRedirectUri(referrerUri, application);
+
+ if (referrerUri != null) {
+ return new String[] { referrer, referrerUri };
}
}
- return null;
}
return null;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AccountTest.java
index 0aa9075..27e013b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AccountTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AccountTest.java
@@ -107,26 +107,21 @@ public class AccountTest {
});
}
- //@Test
- public void returnToAppFromHeader() {
- appPage.open();
- appPage.openAccount();
+ @Test
+ public void returnToAppFromQueryParam() {
+ driver.navigate().to(AccountUpdateProfilePage.PATH + "?referrer=test-app");
loginPage.login("test-user@localhost", "password");
-
Assert.assertTrue(profilePage.isCurrent());
profilePage.backToApplication();
Assert.assertTrue(appPage.isCurrent());
- }
- //@Test
- public void returnToAppFromQueryParam() {
- driver.navigate().to(AccountUpdateProfilePage.PATH + "?referrer=test-app");
- loginPage.login("test-user@localhost", "password");
+ driver.navigate().to(AccountUpdateProfilePage.PATH + "?referrer=test-app&referrer_uri=http://localhost:8081/app?test");
Assert.assertTrue(profilePage.isCurrent());
profilePage.backToApplication();
Assert.assertTrue(appPage.isCurrent());
+ Assert.assertEquals(appPage.baseUrl + "?test", driver.getCurrentUrl());
}
@Test
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java
index 3f9c784..582c112 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java
@@ -42,7 +42,7 @@ public class AccountUpdateProfilePage extends AbstractAccountPage {
private WebElement emailInput;
- @FindBy(linkText = "Back to application")
+ @FindBy(id = "referrer")
private WebElement backToApplicationLink;
@FindBy(css = "button[type=\"submit\"]")
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AppPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AppPage.java
index c0ce139..7fa6c9e 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AppPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AppPage.java
@@ -30,7 +30,7 @@ import org.openqa.selenium.support.FindBy;
*/
public class AppPage extends AbstractPage {
- private String baseUrl = "http://localhost:8081/app";
+ public static final String baseUrl = "http://localhost:8081/app";
@FindBy(id = "account")
private WebElement accountLink;