Merge pull request #299 from stianst/master

Added support for Cordova to keycloak.js
This commit is contained in:
Stian Thorgersen 2014-03-19 17:02:05 +00:00
commit 2403e1f465
22 changed files with 537 additions and 184 deletions

4
examples/cordova/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
platforms
plugins
www/keycloak.js
www/keycloak.json

View file

@ -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'.

View file

@ -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"]
}
]
}
}

View file

@ -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>

View file

@ -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>

View file

@ -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>
@ -32,6 +24,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>

View file

@ -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>

View file

@ -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
}

View file

@ -51,6 +51,10 @@
"client": "customer-portal",
"roles": ["user"]
},
{
"client": "customer-portal-js",
"roles": ["user"]
},
{
"client": "product-portal",
"roles": ["user"]

View file

@ -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'.

View file

@ -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 {

View file

@ -25,6 +25,6 @@ public interface Account {
public Account setRealm(RealmModel realm);
public Account setReferrer(String referrer);
public Account setReferrer(String[] referrer);
}

View file

@ -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;
}

View file

@ -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];
}
}

View file

@ -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>

View file

@ -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;
}
}
}
if (!config.realm) {
throw 'realm missing';
}
if (!config.clientId) {
throw 'clientId missing';
}
kc.authServerUrl = config.url;
kc.realm = config.realm;
kc.clientId = config.clientId;
configPromise.setSuccess();
}
var adapter;
kc.init = function (init) {
kc.authenticated = false;
if (window.Cordova) {
adapter = loadAdapter('cordova');
} else {
adapter = loadAdapter();
}
var promise = createPromise();
var callback = parseCallback(window.location.href);
var initPromise = createPromise();
initPromise.promise.success(function() {
kc.onReady && kc.onReady(kc.authenticated);
promise.setSuccess(kc.authenticated);
}).error(promise.error);
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(redirectUri) {
kc.createLogoutUrl = function(options) {
var url = getRealmUrl()
+ '/tokens/logout'
+ '?redirect_uri=' + getEncodedRedirectUri(redirectUri);
+ '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options));
return url;
}
kc.createAccountUrl = function(options) {
var url = getRealmUrl()
+ '/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;
}
kc.authServerUrl = config['auth-server-url'];
kc.realm = config['realm'];
kc.clientId = config['resource'];
if (configUrl) {
var req = new XMLHttpRequest();
req.open('GET', configUrl, true);
req.setRequestHeader('Accept', 'application/json');
configPromise.setSuccess();
} else {
configPromise.setError();
req.onreadystatechange = function () {
if (req.readyState == 4) {
if (req.status == 200) {
var config = JSON.parse(req.responseText);
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",

View file

@ -1,8 +0,0 @@
{
"realm" : "test",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDddHi8/MoYNtdafydQ+e4P0qrvClCW0o/x9fAbZ09dET/VBZCU28M54LmgJT7snXfreyYBpaQpHkW52/tyuEcJ5KO28LIcvObDQFDq3Z7esrovHl4NRETJBF9Xqwt+XLTZF6m37fYzaUK6MVJzUHP9qmu90LYyyvQ+hBJD0GSw1QIDAQAB",
"auth-server-url" : "http://localhost:8081/auth",
"ssl-not-required" : true,
"resource" : "test",
"public-client" : true
}

View file

@ -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;

View file

@ -107,19 +107,7 @@ public class AccountTest {
});
}
//@Test
public void returnToAppFromHeader() {
appPage.open();
appPage.openAccount();
loginPage.login("test-user@localhost", "password");
Assert.assertTrue(profilePage.isCurrent());
profilePage.backToApplication();
Assert.assertTrue(appPage.isCurrent());
}
//@Test
@Test
public void returnToAppFromQueryParam() {
driver.navigate().to(AccountUpdateProfilePage.PATH + "?referrer=test-app");
loginPage.login("test-user@localhost", "password");
@ -127,6 +115,13 @@ public class AccountTest {
profilePage.backToApplication();
Assert.assertTrue(appPage.isCurrent());
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

View file

@ -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\"]")

View file

@ -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;

View file