Updated session status iframe for JavaScript adapter to be created automatically, and to invoke onAuthLogout callback if user is logged out
This commit is contained in:
parent
7bea4af6c9
commit
643daadf60
7 changed files with 315 additions and 170 deletions
|
@ -50,7 +50,7 @@ var keycloak = Keycloak({
|
||||||
required will redirect to the login form on the server, while check-sso will redirect to the auth server to check
|
required will redirect to the login form on the server, while check-sso will redirect to the auth server to check
|
||||||
if the user is already logged in to the realm. For example:
|
if the user is already logged in to the realm. For example:
|
||||||
<programlisting><![CDATA[
|
<programlisting><![CDATA[
|
||||||
keycloak.init('login-required')
|
keycloak.init({ onLoad: 'login-required' })
|
||||||
]]></programlisting>
|
]]></programlisting>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
@ -125,6 +125,17 @@ keycloak.updateToken(30).success(function() {
|
||||||
]]></programlisting>
|
]]></programlisting>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title>Session status iframe</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
By default the JavaScript adapter creates a non-visible iframe that is used to detect if a single-sign out has occured.
|
||||||
|
This does not require any network traffic, instead the status is retrieved from a special status cookie. This feature can be disabled
|
||||||
|
by setting <literal>checkLoginIframe: false</literal> in the options passed to the <literal>init</literal>
|
||||||
|
method.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<title>JavaScript Adapter reference</title>
|
<title>JavaScript Adapter reference</title>
|
||||||
|
|
||||||
|
@ -156,20 +167,127 @@ new Keycloak({ url: 'http://localhost/auth', realm: 'myrealm', clientId: 'myApp'
|
||||||
<section>
|
<section>
|
||||||
<title>Methods</title>
|
<title>Methods</title>
|
||||||
|
|
||||||
|
<simplesect>
|
||||||
|
<title>init(options)</title>
|
||||||
|
|
||||||
|
<para>Called to initialize the adapter.</para>
|
||||||
|
<para>Options is an Object, where:
|
||||||
<itemizedlist>
|
<itemizedlist>
|
||||||
<listitem>init - called to initialize the adapter. Returns promise to set functions to be invoked on success or error</listitem>
|
<listitem>onLoad - specifies an action to do on load, can be either 'login-required' or 'check-sso'</listitem>
|
||||||
<listitem>login(options) - redirects to login form on (options is an optional object with redirectUri and/or prompt fields)</listitem>
|
<listitem>token - set an initial value for the token</listitem>
|
||||||
<listitem>createLoginUrl(options) - returns the url to login form on (options is an optional object with redirectUri and/or prompt fields)</listitem>
|
<listitem>refreshToken - set an initial value for the refresh token</listitem>
|
||||||
<listitem>logout(options) - redirects to logout (options is an optional object with redirectUri)</listitem>
|
<listitem>checkLoginIframe - set to enable/disable monitoring login state (default is true)</listitem>
|
||||||
<listitem>createLogoutUrl(options) - returns the url to logout (options is an optional object with redirectUri)</listitem>
|
<listitem>checkLoginIframeInterval - set the interval to check login state (default is 5 seconds)</listitem>
|
||||||
<listitem>accountManagement - redirects to account management</listitem>
|
|
||||||
<listitem>createAccountUrl - returns the url to account management</listitem>
|
|
||||||
<listitem>hasRealmRole(role) - returns true if the token has the given realm role</listitem>
|
|
||||||
<listitem>hasResourceRole(role, resource) - returns true if the token has the given role for the resource (resource is optional, if not specified clientId is used)</listitem>
|
|
||||||
<listitem>loadUserProfile() - loads the users profile. Returns promise to set functions to be invoked on success or error</listitem>
|
|
||||||
<listitem>isTokenExpired(minValidity) - returns true if the token has less than minValidity seconds left before it expires (minValidity is optional, if not specified 0 is used)</listitem>
|
|
||||||
<listitem>updateToken(minValidity) - refreshes the token if the token expires within minValidity seconds (minValidity is optional, if not specified 0 is used). Returns promise to set functions to be invoked on success or error</listitem>
|
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
<para>Returns promise to set functions to be invoked on success or error.</para>
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
<simplesect>
|
||||||
|
<title>login(options)</title>
|
||||||
|
|
||||||
|
<para>Redirects to login form on (options is an optional object with redirectUri and/or prompt fields)</para>
|
||||||
|
<para>Options is an Object, where:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>redirectUri - specifies the uri to redirect to after login</listitem>
|
||||||
|
<listitem>prompt - can be set to 'none' to check if the user is logged in already (if not logged in a login form is not displayed)</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
</simplesect>
|
||||||
|
<simplesect>
|
||||||
|
<title>createLoginUrl(options)</title>
|
||||||
|
|
||||||
|
<para>Returns the url to login form on (options is an optional object with redirectUri and/or prompt fields)</para>
|
||||||
|
<para>Options is an Object, where:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>redirectUri - specifies the uri to redirect to after login</listitem>
|
||||||
|
<listitem>prompt - can be set to 'none' to check if the user is logged in already (if not logged in a login form is not displayed)</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
<simplesect>
|
||||||
|
<title>logout(options)</title>
|
||||||
|
|
||||||
|
<para>Redirects to logout</para>
|
||||||
|
<para>Options is an Object, where:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>redirectUri - specifies the uri to redirect to after logout</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
<simplesect>
|
||||||
|
<title>createLogoutUrl(options)</title>
|
||||||
|
|
||||||
|
<para>Returns logout out</para>
|
||||||
|
<para>Options is an Object, where:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>redirectUri - specifies the uri to redirect to after logout</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
<simplesect>
|
||||||
|
<title>accountManagement()</title>
|
||||||
|
|
||||||
|
<para>Redirects to account management</para>
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
<simplesect>
|
||||||
|
<title>createAccountUrl()</title>
|
||||||
|
|
||||||
|
<para>Returns the url to account management</para>
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
<simplesect>
|
||||||
|
<title>hasRealmRole(role)</title>
|
||||||
|
|
||||||
|
<para>Returns true if the token has the given realm role</para>
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
<simplesect>
|
||||||
|
<title>hasResourceRole(role, resource)</title>
|
||||||
|
|
||||||
|
<para>Returns true if the token has the given role for the resource (resource is optional, if not specified clientId is used)</para>
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
<simplesect>
|
||||||
|
<title>loadUserProfile()</title>
|
||||||
|
|
||||||
|
<para>Loads the users profile</para>
|
||||||
|
|
||||||
|
<para>Returns promise to set functions to be invoked on success or error.</para>
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
<simplesect>
|
||||||
|
<title>isTokenExpired(minValidity)</title>
|
||||||
|
|
||||||
|
<para>Returns true if the token has less than minValidity seconds left before it expires (minValidity is optional, if not specified 0 is used)</para>
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
<simplesect>
|
||||||
|
<title>updateToken(minValidity)</title>
|
||||||
|
|
||||||
|
<para>If the token expires within minValidity seconds (minValidity is optional, if not specified 0 is used) the token is refreshed.
|
||||||
|
If the session status iframe is enabled, the session status is also checked.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>Returns promise to set functions that can be invoked if the token is still valid, or if the token is no longer valid. For example:</para>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[
|
||||||
|
keycloak.updateToken(5).success(function(refreshed) {
|
||||||
|
if (refreshed) {
|
||||||
|
alert('token was successfully refreshed');
|
||||||
|
} else {
|
||||||
|
alert('token is still valid');
|
||||||
|
}
|
||||||
|
}).error(function() {
|
||||||
|
alert('failed to refresh the token, or the session has expired');
|
||||||
|
});
|
||||||
|
]]></programlisting>
|
||||||
|
|
||||||
|
</simplesect>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
|
@ -187,7 +305,7 @@ keycloak.onAuthSuccess = function() { alert('authenticated'); }
|
||||||
<listitem>onAuthError - called if there was an error during authentication</listitem>
|
<listitem>onAuthError - called if there was an error during authentication</listitem>
|
||||||
<listitem>onAuthRefreshSuccess - called when the token is refreshed</listitem>
|
<listitem>onAuthRefreshSuccess - called when the token is refreshed</listitem>
|
||||||
<listitem>onAuthRefreshError - called if there was an error while trying to refresh the token</listitem>
|
<listitem>onAuthRefreshError - called if there was an error while trying to refresh the token</listitem>
|
||||||
<listitem>onAuthLogout - called when the user is logged out (only relevant to Cordova)</listitem>
|
<listitem>onAuthLogout - called if the user is logged out (will only be called if the session status iframe is enabled, or in Cordova mode)</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -73,32 +73,14 @@ User <b id="subject"></b> made this request.
|
||||||
};
|
};
|
||||||
|
|
||||||
var reloadData = function () {
|
var reloadData = function () {
|
||||||
keycloak.checkLoginIframe(
|
keycloak.updateToken(10)
|
||||||
function() {
|
.success(loadData)
|
||||||
keycloak.updateToken(10).success(loadData).error(loadFailure);
|
.error(function() {
|
||||||
},
|
|
||||||
function() {
|
|
||||||
document.getElementById('customers').innerHTML = '<b>Failed to load data. User is logged out.</b>';
|
document.getElementById('customers').innerHTML = '<b>Failed to load data. User is logged out.</b>';
|
||||||
//window.location.reload();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// NOTE!!!: keycloak.setupCheckLoginIframe and checkLoginIframe are an optional way
|
|
||||||
// of discovering if you are logged out. This approach may not always be feasible, but it is a lightweight
|
|
||||||
// approach that requires no network call to determine if the user is logged in. The downside is that it
|
|
||||||
// requires a callback.
|
|
||||||
//
|
|
||||||
// Alternatively, on a single log out, the access token will eventually time out, then refresh token will fail
|
|
||||||
// as the session will be logged out on the auth server.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
keycloak.init('login-required').success(function() {
|
|
||||||
// Use an iframe to detect if the user has done an SSO logout in a different window.
|
|
||||||
keycloak.setupCheckLoginIframe(document, window);
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
keycloak.init({ onLoad: 'login-required' }).success(reloadData);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -48,9 +48,12 @@
|
||||||
"name": "js-console",
|
"name": "js-console",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"publicClient": true,
|
"publicClient": true,
|
||||||
"baseUrl": "http://localhost:8080/js-console",
|
"baseUrl": "http://localhost/js-console",
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"http://localhost:8080/js-console/*"
|
"http://localhost/js-console/*"
|
||||||
|
],
|
||||||
|
"webOrigins": [
|
||||||
|
"http://localhost"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<script src="/auth/js/keycloak.js"></script>
|
<script src="//localhost:8081/auth/js/keycloak.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
@ -86,6 +86,10 @@
|
||||||
event('Auth Refresh Error');
|
event('Auth Refresh Error');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
keycloak.onAuthLogout = function () {
|
||||||
|
event('Auth Logout');
|
||||||
|
};
|
||||||
|
|
||||||
keycloak.init().success(function(authenticated) {
|
keycloak.init().success(function(authenticated) {
|
||||||
output('Init Success (' + (authenticated ? 'Authenticated' : 'Not Authenticated') + ')');
|
output('Init Success (' + (authenticated ? 'Authenticated' : 'Not Authenticated') + ')');
|
||||||
}).error(function() {
|
}).error(function() {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"realm" : "example",
|
"realm" : "example",
|
||||||
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||||
"auth-server-url" : "http://localhost:8080/auth",
|
"auth-server-url" : "http://localhost:8081/auth",
|
||||||
"ssl-not-required" : true,
|
"ssl-not-required" : true,
|
||||||
"resource" : "js-console",
|
"resource" : "js-console",
|
||||||
"public-client" : true
|
"public-client" : true
|
||||||
|
|
|
@ -9,9 +9,6 @@ var logoutUrl = consoleBaseUrl + "/logout";
|
||||||
var auth = {};
|
var auth = {};
|
||||||
var logout = function(){
|
var logout = function(){
|
||||||
console.log('*** LOGOUT');
|
console.log('*** LOGOUT');
|
||||||
auth.loggedIn = false;
|
|
||||||
auth.authz = null;
|
|
||||||
auth.user = null;
|
|
||||||
window.location = logoutUrl;
|
window.location = logoutUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,7 +25,11 @@ angular.element(document).ready(function ($http) {
|
||||||
var keycloakAuth = new Keycloak(configUrl);
|
var keycloakAuth = new Keycloak(configUrl);
|
||||||
auth.loggedIn = false;
|
auth.loggedIn = false;
|
||||||
|
|
||||||
keycloakAuth.init('login-required').success(function () {
|
keycloakAuth.onAuthLogout = function() {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
keycloakAuth.init({ onLoad: 'login-required' }).success(function () {
|
||||||
auth.loggedIn = true;
|
auth.loggedIn = true;
|
||||||
auth.authz = keycloakAuth;
|
auth.authz = keycloakAuth;
|
||||||
module.factory('Auth', function() {
|
module.factory('Auth', function() {
|
||||||
|
@ -38,7 +39,6 @@ angular.element(document).ready(function ($http) {
|
||||||
}).error(function () {
|
}).error(function () {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
module.factory('authInterceptor', function($q, Auth) {
|
module.factory('authInterceptor', function($q, Auth) {
|
||||||
|
@ -52,7 +52,7 @@ module.factory('authInterceptor', function($q, Auth) {
|
||||||
|
|
||||||
deferred.resolve(config);
|
deferred.resolve(config);
|
||||||
}).error(function() {
|
}).error(function() {
|
||||||
deferred.reject('Failed to refresh token');
|
location.reload();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
|
|
|
@ -6,11 +6,13 @@ var Keycloak = function (config) {
|
||||||
var kc = this;
|
var kc = this;
|
||||||
var adapter;
|
var adapter;
|
||||||
|
|
||||||
kc.callbackMap = new Object();
|
var loginIframe = {
|
||||||
|
enable: true,
|
||||||
|
callbackMap: [],
|
||||||
|
interval: 5
|
||||||
|
};
|
||||||
|
|
||||||
|
kc.init = function (initOptions) {
|
||||||
|
|
||||||
kc.init = function (init) {
|
|
||||||
kc.authenticated = false;
|
kc.authenticated = false;
|
||||||
|
|
||||||
if (window.Cordova) {
|
if (window.Cordova) {
|
||||||
|
@ -19,6 +21,16 @@ var Keycloak = function (config) {
|
||||||
adapter = loadAdapter();
|
adapter = loadAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (initOptions) {
|
||||||
|
if (typeof initOptions.checkLoginIframe !== 'undefined') {
|
||||||
|
loginIframe.enable = initOptions.checkLoginIframe;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (initOptions.checkLoginIframeInterval) {
|
||||||
|
loginIframe.interval = initOptions.checkLoginIframeInterval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var promise = createPromise();
|
var promise = createPromise();
|
||||||
|
|
||||||
var initPromise = createPromise();
|
var initPromise = createPromise();
|
||||||
|
@ -33,19 +45,21 @@ var Keycloak = function (config) {
|
||||||
|
|
||||||
function processInit() {
|
function processInit() {
|
||||||
var callback = parseCallback(window.location.href);
|
var callback = parseCallback(window.location.href);
|
||||||
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
window.history.replaceState({}, null, location.protocol + '//' + location.host + location.pathname + (callback.fragment ? '#' + callback.fragment : ''));
|
window.history.replaceState({}, null, location.protocol + '//' + location.host + location.pathname + (callback.fragment ? '#' + callback.fragment : ''));
|
||||||
processCallback(callback, initPromise);
|
processCallback(callback, initPromise);
|
||||||
return;
|
return;
|
||||||
} else if (init) {
|
} else if (initOptions) {
|
||||||
if (init.code || init.error) {
|
if (initOptions.token || initOptions.refreshToken) {
|
||||||
processCallback(init, initPromise);
|
setToken(initOptions.token, initOptions.refreshToken);
|
||||||
return;
|
} else if (initOptions.onLoad) {
|
||||||
} else if (init.token || init.refreshToken) {
|
if (initOptions.onLoad == 'check-sso' || initOptions.onLoad == 'login-required') {
|
||||||
setToken(init.token, init.refreshToken);
|
var options = {};
|
||||||
initPromise.setSuccess();
|
if (initOptions.onLoad == 'check-sso') {
|
||||||
} else if (init == 'login-required') {
|
options.prompt = 'none';
|
||||||
var p = kc.login();
|
}
|
||||||
|
var p = kc.login(options);
|
||||||
if (p) {
|
if (p) {
|
||||||
p.success(function() {
|
p.success(function() {
|
||||||
initPromise.setSuccess();
|
initPromise.setSuccess();
|
||||||
|
@ -53,17 +67,9 @@ var Keycloak = function (config) {
|
||||||
initPromise.setError();
|
initPromise.setError();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
} else if (init == 'check-sso') {
|
|
||||||
var p = kc.login({ prompt: 'none' });
|
|
||||||
if (p) {
|
|
||||||
p.success(function() {
|
|
||||||
initPromise.setSuccess();
|
|
||||||
}).error(function() {
|
|
||||||
initPromise.setSuccess();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
throw 'invalid init: ' + init;
|
throw 'Invalid value for onLoad';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
initPromise.setSuccess();
|
initPromise.setSuccess();
|
||||||
|
@ -82,59 +88,6 @@ var Keycloak = function (config) {
|
||||||
return adapter.login(options);
|
return adapter.login(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
kc.setupCheckLoginIframe = function(doc, win) {
|
|
||||||
kc.iframe = doc.createElement('iframe');
|
|
||||||
var src = getRealmUrl() + "/login-status-iframe.html?client_id=" + encodeURIComponent(kc.clientId);
|
|
||||||
console.log('iframe src='+ src);
|
|
||||||
kc.iframe.setAttribute('src', src );
|
|
||||||
kc.iframe.style.display = "none";
|
|
||||||
doc.body.appendChild(kc.iframe);
|
|
||||||
if (!kc.iframe.contentWindow.location.origin) {
|
|
||||||
kc.iframe.contentWindow.location.origin = kc.iframe.contentWindow.location.protocol + "//" + kc.iframe.contentWindow.location.host;
|
|
||||||
}
|
|
||||||
|
|
||||||
var messageCallback = function(event) {
|
|
||||||
if (event.origin !== kc.iframe.contentWindow.location.origin) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var data = event.data;
|
|
||||||
var success = kc.callbackMap[data.successId];
|
|
||||||
var failure = kc.callbackMap[data.failureId];
|
|
||||||
delete kc.callbackMap[data.successId];
|
|
||||||
delete kc.callbackMap[data.failureId];
|
|
||||||
if (kc.sessionId != data.session) {
|
|
||||||
console.log("session doesn't match received session: " + event.data.session);
|
|
||||||
console.log("forcing loggedIn to be false");
|
|
||||||
failure();
|
|
||||||
}
|
|
||||||
if (data.loggedIn) {
|
|
||||||
success();
|
|
||||||
} else {
|
|
||||||
failure();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
win.addEventListener("message", messageCallback, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
kc.checkLoginIframe = function(success, failure) {
|
|
||||||
|
|
||||||
var msg = {};
|
|
||||||
if (!success) {
|
|
||||||
throw "You must define a success method";
|
|
||||||
}
|
|
||||||
if (!failure) {
|
|
||||||
throw "You must define a failure method";
|
|
||||||
}
|
|
||||||
msg.successId = createCallbackId();
|
|
||||||
msg.failureId = createCallbackId();
|
|
||||||
kc.callbackMap[msg.successId] = success;
|
|
||||||
kc.callbackMap[msg.failureId] = failure;
|
|
||||||
var origin = kc.iframe.contentWindow.location.origin;
|
|
||||||
console.log('*** origin: ' + origin);
|
|
||||||
var iframe = kc.iframe;
|
|
||||||
iframe.contentWindow.postMessage(msg, origin);
|
|
||||||
}
|
|
||||||
|
|
||||||
kc.createLoginUrl = function(options) {
|
kc.createLoginUrl = function(options) {
|
||||||
var state = createUUID();
|
var state = createUUID();
|
||||||
|
|
||||||
|
@ -241,19 +194,19 @@ var Keycloak = function (config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
kc.updateToken = function(minValidity) {
|
kc.updateToken = function(minValidity) {
|
||||||
if (!kc.tokenParsed || !kc.refreshToken) {
|
|
||||||
throw 'Not authenticated';
|
|
||||||
}
|
|
||||||
|
|
||||||
var promise = createPromise();
|
var promise = createPromise();
|
||||||
|
|
||||||
if (minValidity) {
|
if (!kc.tokenParsed || !kc.refreshToken) {
|
||||||
if (!kc.isTokenExpired(minValidity)) {
|
promise.setError();
|
||||||
promise.setSuccess(false);
|
|
||||||
return promise.promise;
|
return promise.promise;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
minValidity = minValidity || 5;
|
||||||
|
|
||||||
|
var exec = function() {
|
||||||
|
if (!kc.isTokenExpired(minValidity)) {
|
||||||
|
promise.setSuccess(false);
|
||||||
|
} else {
|
||||||
var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken;
|
var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken;
|
||||||
var url = getRealmUrl() + '/tokens/refresh';
|
var url = getRealmUrl() + '/tokens/refresh';
|
||||||
|
|
||||||
|
@ -282,6 +235,23 @@ var Keycloak = function (config) {
|
||||||
};
|
};
|
||||||
|
|
||||||
req.send(params);
|
req.send(params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loginIframe.enable) {
|
||||||
|
if (loginIframe.enable) {
|
||||||
|
var iframePromise = checkLoginIframe();
|
||||||
|
iframePromise.success(function() {
|
||||||
|
exec();
|
||||||
|
}).error(function() {
|
||||||
|
promise.setError();
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
promise.setSuccess(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
exec();
|
||||||
|
}
|
||||||
|
|
||||||
return promise.promise;
|
return promise.promise;
|
||||||
}
|
}
|
||||||
|
@ -398,11 +368,17 @@ var Keycloak = function (config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearToken() {
|
function clearToken() {
|
||||||
|
if (kc.token) {
|
||||||
setToken(null, null);
|
setToken(null, null);
|
||||||
kc.onAuthLogout && kc.onAuthLogout();
|
kc.onAuthLogout && kc.onAuthLogout();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function setToken(token, refreshToken) {
|
function setToken(token, refreshToken) {
|
||||||
|
if (token || refreshToken) {
|
||||||
|
setupCheckLoginIframe();
|
||||||
|
}
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
kc.token = token;
|
kc.token = token;
|
||||||
kc.tokenParsed = JSON.parse(decodeURIComponent(escape(window.atob( token.split('.')[1] ))));
|
kc.tokenParsed = JSON.parse(decodeURIComponent(escape(window.atob( token.split('.')[1] ))));
|
||||||
|
@ -539,6 +515,68 @@ var Keycloak = function (config) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setupCheckLoginIframe() {
|
||||||
|
if (!loginIframe.enable || loginIframe.iframe) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var iframe = document.createElement('iframe');
|
||||||
|
|
||||||
|
iframe.onload = function() {
|
||||||
|
var realmUrl = getRealmUrl();
|
||||||
|
loginIframe.iframeOrigin = realmUrl.substring(0, realmUrl.indexOf('/', 8));
|
||||||
|
loginIframe.iframe = iframe;
|
||||||
|
}
|
||||||
|
|
||||||
|
var src = getRealmUrl() + '/login-status-iframe.html?client_id=' + encodeURIComponent(kc.clientId);
|
||||||
|
console.log('iframe src='+ src);
|
||||||
|
iframe.setAttribute('src', src );
|
||||||
|
iframe.style.display = 'none';
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
|
||||||
|
var messageCallback = function(event) {
|
||||||
|
if (event.origin !== loginIframe.iframeOrigin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var data = event.data;
|
||||||
|
var promise = loginIframe.callbackMap[data.callbackId];
|
||||||
|
delete loginIframe.callbackMap[data.callbackId];
|
||||||
|
if (kc.sessionId == data.session && data.loggedIn) {
|
||||||
|
promise.setSuccess();
|
||||||
|
} else {
|
||||||
|
clearToken();
|
||||||
|
promise.setError();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener('message', messageCallback, false);
|
||||||
|
|
||||||
|
var check = function() {
|
||||||
|
checkLoginIframe();
|
||||||
|
if (kc.token) {
|
||||||
|
setTimeout(check, loginIframe.interval * 1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setTimeout(check, loginIframe.interval * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkLoginIframe() {
|
||||||
|
var promise = createPromise();
|
||||||
|
|
||||||
|
if (loginIframe.iframe || loginIframe.iframeOrigin) {
|
||||||
|
var msg = {};
|
||||||
|
msg.callbackId = createCallbackId();
|
||||||
|
loginIframe.callbackMap[msg.callbackId] = promise;
|
||||||
|
var origin = loginIframe.iframeOrigin;
|
||||||
|
console.log('*** origin: ' + origin);
|
||||||
|
loginIframe.iframe.contentWindow.postMessage(msg, origin);
|
||||||
|
} else {
|
||||||
|
promise.setSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise.promise;
|
||||||
|
}
|
||||||
|
|
||||||
function loadAdapter(type) {
|
function loadAdapter(type) {
|
||||||
if (!type || type == 'default') {
|
if (!type || type == 'default') {
|
||||||
return {
|
return {
|
||||||
|
|
Loading…
Reference in a new issue