keycloak-uncached
Changes
examples/js-console/src/main/webapp/keycloak.js 886(+886 -0)
Details
diff --git a/docbook/reference/en/en-US/modules/javascript-adapter.xml b/docbook/reference/en/en-US/modules/javascript-adapter.xml
index 996f29a..2e9f941 100755
--- a/docbook/reference/en/en-US/modules/javascript-adapter.xml
+++ b/docbook/reference/en/en-US/modules/javascript-adapter.xml
@@ -177,6 +177,7 @@ new Keycloak({ url: 'http://localhost/auth', realm: 'myrealm', clientId: 'myApp'
<listitem>resourceAccess - the resource roles assocaited with the token</listitem>
<listitem>refreshToken - the base64 encoded token that can be used to retrieve a new token</listitem>
<listitem>refreshTokenParsed - the parsed refresh token</listitem>
+ <listitem>timeSkew - estimated skew between local time and Keycloak server in seconds</listitem>
</itemizedlist>
</section>
diff --git a/examples/js-console/src/main/webapp/index.html b/examples/js-console/src/main/webapp/index.html
index 153053b..d85b63b 100644
--- a/examples/js-console/src/main/webapp/index.html
+++ b/examples/js-console/src/main/webapp/index.html
@@ -49,7 +49,7 @@
if (refreshed) {
output(keycloak.tokenParsed);
} else {
- output('Token not refreshed, valid for ' + Math.round(keycloak.tokenParsed.exp - new Date().getTime() / 1000) + ' seconds');
+ output('Token not refreshed, valid for ' + Math.round(keycloak.tokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds');
}
}).error(function() {
output('Failed to refresh token');
@@ -62,11 +62,11 @@
return;
}
- var o = 'Token Expires:\t\t' + new Date(keycloak.tokenParsed.exp * 1000).toLocaleString() + '\n';
- o += 'Token Expires in:\t' + Math.round(keycloak.tokenParsed.exp - new Date().getTime() / 1000) + ' seconds\n';
+ var o = 'Token Expires:\t\t' + new Date((keycloak.tokenParsed.exp + keycloak.timeSkew) * 1000).toLocaleString() + '\n';
+ o += 'Token Expires in:\t' + Math.round(keycloak.tokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds\n';
- o += 'Refresh Token Expires:\t' + new Date(keycloak.refreshTokenParsed.exp * 1000).toLocaleString() + '\n';
- o += 'Refresh Expires in:\t' + Math.round(keycloak.refreshTokenParsed.exp - new Date().getTime() / 1000) + ' seconds';
+ o += 'Refresh Token Expires:\t' + new Date((keycloak.refreshTokenParsed.exp + keycloak.timeSkew) * 1000).toLocaleString() + '\n';
+ o += 'Refresh Expires in:\t' + Math.round(keycloak.refreshTokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds';
output(o);
}
examples/js-console/src/main/webapp/keycloak.js 886(+886 -0)
diff --git a/examples/js-console/src/main/webapp/keycloak.js b/examples/js-console/src/main/webapp/keycloak.js
new file mode 100755
index 0000000..8917581
--- /dev/null
+++ b/examples/js-console/src/main/webapp/keycloak.js
@@ -0,0 +1,886 @@
+(function( window, undefined ) {
+
+ var Keycloak = function (config) {
+ if (!(this instanceof Keycloak)) {
+ return new Keycloak(config);
+ }
+
+ var kc = this;
+ var adapter;
+ var refreshQueue = [];
+
+ var loginIframe = {
+ enable: true,
+ callbackMap: [],
+ interval: 5
+ };
+
+ kc.init = function (initOptions) {
+ kc.authenticated = false;
+
+ if (window.Cordova) {
+ adapter = loadAdapter('cordova');
+ } else {
+ adapter = loadAdapter();
+ }
+
+ if (initOptions) {
+ if (typeof initOptions.checkLoginIframe !== 'undefined') {
+ loginIframe.enable = initOptions.checkLoginIframe;
+ }
+
+ if (initOptions.checkLoginIframeInterval) {
+ loginIframe.interval = initOptions.checkLoginIframeInterval;
+ }
+
+ if (initOptions.onLoad === 'login-required') {
+ kc.loginRequired = true;
+ }
+ }
+
+ var promise = createPromise();
+
+ var initPromise = createPromise();
+ initPromise.promise.success(function() {
+ kc.onReady && kc.onReady(kc.authenticated);
+ promise.setSuccess(kc.authenticated);
+ }).error(function() {
+ promise.setError();
+ });
+
+ var configPromise = loadConfig(config);
+
+ function onLoad() {
+ var doLogin = function(prompt) {
+ if (!prompt) {
+ options.prompt = 'none';
+ }
+ kc.login(options).success(function () {
+ initPromise.setSuccess();
+ }).error(function () {
+ initPromise.setError();
+ });
+ }
+
+ var options = {};
+ switch (initOptions.onLoad) {
+ case 'check-sso':
+ if (loginIframe.enable) {
+ setupCheckLoginIframe().success(function() {
+ checkLoginIframe().success(function () {
+ doLogin(false);
+ }).error(function () {
+ initPromise.setSuccess();
+ });
+ });
+ } else {
+ doLogin(false);
+ }
+ break;
+ case 'login-required':
+ doLogin(true);
+ break;
+ default:
+ throw 'Invalid value for onLoad';
+ }
+ }
+
+ function processInit() {
+ var callback = parseCallback(window.location.href);
+
+ if (callback) {
+ setupCheckLoginIframe();
+ window.history.replaceState({}, null, callback.newUrl);
+ processCallback(callback, initPromise);
+ return;
+ } else if (initOptions) {
+ if (initOptions.token || initOptions.refreshToken) {
+ setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken);
+
+ if (loginIframe.enable) {
+ setupCheckLoginIframe().success(function() {
+ checkLoginIframe().success(function () {
+ initPromise.setSuccess();
+ }).error(function () {
+ if (initOptions.onLoad) {
+ onLoad();
+ }
+ });
+ });
+ } else {
+ initPromise.setSuccess();
+ }
+ } else if (initOptions.onLoad) {
+ onLoad();
+ }
+ } else {
+ initPromise.setSuccess();
+ }
+ }
+
+ configPromise.success(processInit);
+ configPromise.error(function() {
+ promise.setError();
+ });
+
+ return promise.promise;
+ }
+
+ kc.login = function (options) {
+ return adapter.login(options);
+ }
+
+ kc.createLoginUrl = function(options) {
+ var state = createUUID();
+
+ var redirectUri = adapter.redirectUri(options);
+ if (options && options.prompt) {
+ redirectUri += (redirectUri.indexOf('?') == -1 ? '?' : '&') + 'prompt=' + options.prompt;
+ }
+
+ sessionStorage.oauthState = JSON.stringify({ state: state, redirectUri: encodeURIComponent(redirectUri) });
+
+ var action = 'auth';
+ if (options && options.action == 'register') {
+ action = 'registrations';
+ }
+
+ var url = getRealmUrl()
+ + '/protocol/openid-connect/' + action
+ + '?client_id=' + encodeURIComponent(kc.clientId)
+ + '&redirect_uri=' + encodeURIComponent(redirectUri)
+ + '&state=' + encodeURIComponent(state)
+ + '&response_type=code';
+
+ if (options && options.prompt) {
+ url += '&prompt=' + options.prompt;
+ }
+
+ if (options && options.loginHint) {
+ url += '&login_hint=' + options.loginHint;
+ }
+
+ if (options && options.idpHint) {
+ url += '&kc_idp_hint=' + options.idpHint;
+ }
+
+ return url;
+ }
+
+ kc.logout = function(options) {
+ return adapter.logout(options);
+ }
+
+ kc.createLogoutUrl = function(options) {
+ var url = getRealmUrl()
+ + '/protocol/openid-connect/logout'
+ + '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options));
+
+ return url;
+ }
+
+ kc.createAccountUrl = function(options) {
+ var url = getRealmUrl()
+ + '/account'
+ + '?referrer=' + encodeURIComponent(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;
+ }
+
+ kc.hasResourceRole = function(role, resource) {
+ if (!kc.resourceAccess) {
+ return false;
+ }
+
+ var access = kc.resourceAccess[resource || kc.clientId];
+ return !!access && access.roles.indexOf(role) >= 0;
+ }
+
+ kc.loadUserProfile = function() {
+ var url = getRealmUrl() + '/account';
+ var req = new XMLHttpRequest();
+ req.open('GET', url, true);
+ req.setRequestHeader('Accept', 'application/json');
+ req.setRequestHeader('Authorization', 'bearer ' + kc.token);
+
+ var promise = createPromise();
+
+ req.onreadystatechange = function () {
+ if (req.readyState == 4) {
+ if (req.status == 200) {
+ kc.profile = JSON.parse(req.responseText);
+ promise.setSuccess(kc.profile);
+ } else {
+ promise.setError();
+ }
+ }
+ }
+
+ req.send();
+
+ return promise.promise;
+ }
+
+ kc.loadUserInfo = function() {
+ var url = getRealmUrl() + '/protocol/openid-connect/userinfo';
+ var req = new XMLHttpRequest();
+ req.open('GET', url, true);
+ req.setRequestHeader('Accept', 'application/json');
+ req.setRequestHeader('Authorization', 'bearer ' + kc.token);
+
+ var promise = createPromise();
+
+ req.onreadystatechange = function () {
+ if (req.readyState == 4) {
+ if (req.status == 200) {
+ kc.userInfo = JSON.parse(req.responseText);
+ promise.setSuccess(kc.userInfo);
+ } else {
+ promise.setError();
+ }
+ }
+ }
+
+ req.send();
+
+ return promise.promise;
+ }
+
+ kc.isTokenExpired = function(minValidity) {
+ if (!kc.tokenParsed || !kc.refreshToken) {
+ throw 'Not authenticated';
+ }
+
+ var expiresIn = kc.tokenParsed['exp'] - (new Date().getTime() / 1000) + kc.timeSkew;
+ if (minValidity) {
+ expiresIn -= minValidity;
+ }
+
+ return expiresIn < 0;
+ }
+
+ kc.updateToken = function(minValidity) {
+ var promise = createPromise();
+
+ if (!kc.tokenParsed || !kc.refreshToken) {
+ promise.setError();
+ 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 url = getRealmUrl() + '/protocol/openid-connect/token';
+
+ refreshQueue.push(promise);
+
+ if (refreshQueue.length == 1) {
+ var req = new XMLHttpRequest();
+ req.open('POST', url, true);
+ req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+
+ if (kc.clientId && kc.clientSecret) {
+ req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
+ } else {
+ params += '&client_id=' + encodeURIComponent(kc.clientId);
+ }
+
+ var timeLocal = new Date().getTime();
+
+ req.onreadystatechange = function () {
+ if (req.readyState == 4) {
+ if (req.status == 200) {
+ timeLocal = (timeLocal + new Date().getTime()) / 2;
+
+ var tokenResponse = JSON.parse(req.responseText);
+ setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token']);
+
+ kc.timeSkew = Math.floor(timeLocal / 1000) - keycloak.tokenParsed.iat;
+
+ kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess();
+ for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
+ p.setSuccess(true);
+ }
+ } else {
+ kc.onAuthRefreshError && kc.onAuthRefreshError();
+ for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
+ p.setError(true);
+ }
+ }
+ }
+ };
+
+ req.send(params);
+ }
+ }
+ }
+
+ if (loginIframe.enable) {
+ var iframePromise = checkLoginIframe();
+ iframePromise.success(function() {
+ exec();
+ }).error(function() {
+ promise.setError();
+ });
+ } else {
+ exec();
+ }
+
+ return promise.promise;
+ }
+
+ kc.clearToken = function() {
+ if (kc.token) {
+ setToken(null, null, null);
+ kc.onAuthLogout && kc.onAuthLogout();
+ if (kc.loginRequired) {
+ kc.login();
+ }
+ }
+ }
+
+ function getRealmUrl() {
+ if (kc.authServerUrl.charAt(kc.authServerUrl.length - 1) == '/') {
+ return kc.authServerUrl + 'realms/' + encodeURIComponent(kc.realm);
+ } else {
+ return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
+ }
+ }
+
+ function getOrigin() {
+ if (!window.location.origin) {
+ return window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
+ } else {
+ return window.location.origin;
+ }
+ }
+
+ function processCallback(oauth, promise) {
+ var code = oauth.code;
+ var error = oauth.error;
+ var prompt = oauth.prompt;
+
+ if (code) {
+ var params = 'code=' + code + '&grant_type=authorization_code';
+ var url = getRealmUrl() + '/protocol/openid-connect/token';
+
+ var req = new XMLHttpRequest();
+ req.open('POST', url, true);
+ req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+
+ if (kc.clientId && kc.clientSecret) {
+ req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
+ } else {
+ params += '&client_id=' + encodeURIComponent(kc.clientId);
+ }
+
+ params += '&redirect_uri=' + oauth.redirectUri;
+
+ req.withCredentials = true;
+
+ var timeLocal = new Date().getTime();
+
+ req.onreadystatechange = function() {
+ if (req.readyState == 4) {
+ if (req.status == 200) {
+ timeLocal = (timeLocal + new Date().getTime()) / 2;
+
+ var tokenResponse = JSON.parse(req.responseText);
+ setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token']);
+
+ kc.timeSkew = Math.floor(timeLocal / 1000) - keycloak.tokenParsed.iat;
+
+ kc.onAuthSuccess && kc.onAuthSuccess();
+ promise && promise.setSuccess();
+ } else {
+ kc.onAuthError && kc.onAuthError();
+ promise && promise.setError();
+ }
+ }
+ };
+
+ req.send(params);
+ } else if (error) {
+ if (prompt != 'none') {
+ kc.onAuthError && kc.onAuthError();
+ promise && promise.setError();
+ } else {
+ promise && promise.setSuccess();
+ }
+ }
+ }
+
+ function loadConfig(url) {
+ var promise = createPromise();
+ var configUrl;
+
+ 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');
+
+ 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'];
+ kc.clientSecret = (config['credentials'] || {})['secret'];
+
+ 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;
+ }
+ }
+ }
+
+ if (!config.realm) {
+ throw 'realm missing';
+ }
+
+ if (!config.clientId) {
+ throw 'clientId missing';
+ }
+
+ kc.authServerUrl = config.url;
+ kc.realm = config.realm;
+ kc.clientId = config.clientId;
+ kc.clientSecret = (config.credentials || {}).secret;
+
+ promise.setSuccess();
+ }
+
+ return promise.promise;
+ }
+
+ function setToken(token, refreshToken, idToken) {
+ if (token) {
+ kc.token = token;
+ kc.tokenParsed = decodeToken(token);
+ var sessionId = kc.realm + '/' + kc.tokenParsed.sub;
+ if (kc.tokenParsed.session_state) {
+ sessionId = sessionId + '/' + kc.tokenParsed.session_state;
+ }
+ kc.sessionId = sessionId;
+ kc.authenticated = true;
+ kc.subject = kc.tokenParsed.sub;
+ kc.realmAccess = kc.tokenParsed.realm_access;
+ kc.resourceAccess = kc.tokenParsed.resource_access;
+ } else {
+ delete kc.token;
+ delete kc.tokenParsed;
+ delete kc.subject;
+ delete kc.realmAccess;
+ delete kc.resourceAccess;
+
+ kc.authenticated = false;
+ }
+
+ if (refreshToken) {
+ kc.refreshToken = refreshToken;
+ kc.refreshTokenParsed = decodeToken(refreshToken);
+ } else {
+ delete kc.refreshToken;
+ delete kc.refreshTokenParsed;
+ }
+
+ if (idToken) {
+ kc.idToken = idToken;
+ kc.idTokenParsed = decodeToken(idToken);
+ } else {
+ delete kc.idToken;
+ delete kc.idTokenParsed;
+ }
+ }
+
+ function decodeToken(str) {
+ str = str.split('.')[1];
+
+ str = str.replace('/-/g', '+');
+ str = str.replace('/_/g', '/');
+ switch (str.length % 4)
+ {
+ case 0:
+ break;
+ case 2:
+ str += '==';
+ break;
+ case 3:
+ str += '=';
+ break;
+ default:
+ throw 'Invalid token';
+ }
+
+ str = (str + '===').slice(0, str.length + (str.length % 4));
+ str = str.replace(/-/g, '+').replace(/_/g, '/');
+
+ str = decodeURIComponent(escape(atob(str)));
+
+ str = JSON.parse(str);
+ return str;
+ }
+
+ function createUUID() {
+ var s = [];
+ var hexDigits = '0123456789abcdef';
+ for (var i = 0; i < 36; i++) {
+ s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
+ }
+ s[14] = '4';
+ s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
+ s[8] = s[13] = s[18] = s[23] = '-';
+ var uuid = s.join('');
+ return uuid;
+ }
+
+ kc.callback_id = 0;
+
+ function createCallbackId() {
+ var id = '<id: ' + (kc.callback_id++) + (Math.random()) + '>';
+ return id;
+
+ }
+
+ function parseCallback(url) {
+ if (url.indexOf('?') != -1) {
+ var oauth = {};
+
+ oauth.newUrl = url.split('?')[0];
+ var paramString = url.split('?')[1];
+ var fragIndex = paramString.indexOf('#');
+ if (fragIndex != -1) {
+ paramString = paramString.substring(0, fragIndex);
+ }
+ var params = paramString.split('&');
+ for (var i = 0; i < params.length; i++) {
+ var p = params[i].split('=');
+ switch (decodeURIComponent(p[0])) {
+ case 'code':
+ oauth.code = p[1];
+ break;
+ case 'error':
+ oauth.error = p[1];
+ break;
+ case 'state':
+ oauth.state = decodeURIComponent(p[1]);
+ break;
+ case 'redirect_fragment':
+ oauth.fragment = decodeURIComponent(p[1]);
+ break;
+ case 'prompt':
+ oauth.prompt = p[1];
+ break;
+ default:
+ oauth.newUrl += (oauth.newUrl.indexOf('?') == -1 ? '?' : '&') + p[0] + '=' + p[1];
+ break;
+ }
+ }
+
+ var sessionState = sessionStorage.oauthState && JSON.parse(sessionStorage.oauthState);
+
+ if (sessionState && (oauth.code || oauth.error) && oauth.state && oauth.state == sessionState.state) {
+ delete sessionStorage.oauthState;
+
+ oauth.redirectUri = sessionState.redirectUri;
+
+ if (oauth.fragment) {
+ oauth.newUrl += '#' + oauth.fragment;
+ }
+
+ return oauth;
+ }
+ }
+ }
+
+ function createPromise() {
+ var p = {
+ setSuccess: function(result) {
+ p.success = true;
+ p.result = result;
+ if (p.successCallback) {
+ p.successCallback(result);
+ }
+ },
+
+ setError: function(result) {
+ p.error = true;
+ p.result = result;
+ if (p.errorCallback) {
+ p.errorCallback(result);
+ }
+ },
+
+ promise: {
+ success: function(callback) {
+ if (p.success) {
+ callback(p.result);
+ } else if (!p.error) {
+ p.successCallback = callback;
+ }
+ return p.promise;
+ },
+ error: function(callback) {
+ if (p.error) {
+ callback(p.result);
+ } else if (!p.success) {
+ p.errorCallback = callback;
+ }
+ return p.promise;
+ }
+ }
+ }
+ return p;
+ }
+
+ function setupCheckLoginIframe() {
+ var promise = createPromise();
+
+ if (!loginIframe.enable) {
+ promise.setSuccess();
+ return promise.promise;
+ }
+
+ if (loginIframe.iframe) {
+ promise.setSuccess();
+ return promise.promise;
+ }
+
+ var iframe = document.createElement('iframe');
+ loginIframe.iframe = iframe;
+
+ iframe.onload = function() {
+ var realmUrl = getRealmUrl();
+ if (realmUrl.charAt(0) === '/') {
+ loginIframe.iframeOrigin = getOrigin();
+ } else {
+ loginIframe.iframeOrigin = realmUrl.substring(0, realmUrl.indexOf('/', 8));
+ }
+ promise.setSuccess();
+
+ setTimeout(check, loginIframe.interval * 1000);
+ }
+
+ var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html?client_id=' + encodeURIComponent(kc.clientId) + '&origin=' + getOrigin();
+ iframe.setAttribute('src', src );
+ iframe.style.display = 'none';
+ document.body.appendChild(iframe);
+
+ var messageCallback = function(event) {
+ if (event.origin !== loginIframe.iframeOrigin) {
+ return;
+ }
+ var data = JSON.parse(event.data);
+ var promise = loginIframe.callbackMap[data.callbackId];
+ delete loginIframe.callbackMap[data.callbackId];
+
+ if ((!kc.sessionId || kc.sessionId == data.session) && data.loggedIn) {
+ promise.setSuccess();
+ } else {
+ kc.clearToken();
+ promise.setError();
+ }
+ };
+ window.addEventListener('message', messageCallback, false);
+
+ var check = function() {
+ checkLoginIframe();
+ if (kc.token) {
+ setTimeout(check, loginIframe.interval * 1000);
+ }
+ };
+
+ return promise.promise;
+ }
+
+ function checkLoginIframe() {
+ var promise = createPromise();
+
+ if (loginIframe.iframe && loginIframe.iframeOrigin) {
+ var msg = {};
+ msg.callbackId = createCallbackId();
+ loginIframe.callbackMap[msg.callbackId] = promise;
+ var origin = loginIframe.iframeOrigin;
+ loginIframe.iframe.contentWindow.postMessage(JSON.stringify(msg), origin);
+ } else {
+ promise.setSuccess();
+ }
+
+ return promise.promise;
+ }
+
+ function loadAdapter(type) {
+ if (!type || type == 'default') {
+ return {
+ login: function(options) {
+ window.location.href = kc.createLoginUrl(options);
+ return createPromise().promise;
+ },
+
+ logout: function(options) {
+ window.location.href = kc.createLogoutUrl(options);
+ return createPromise().promise;
+ },
+
+ accountManagement : function() {
+ window.location.href = kc.createAccountUrl();
+ return createPromise().promise;
+ },
+
+ redirectUri: function(options) {
+ if (options && options.redirectUri) {
+ return options.redirectUri;
+ } else if (kc.redirectUri) {
+ return kc.redirectUri;
+ } else {
+ var redirectUri = location.href;
+ if (location.hash) {
+ redirectUri = redirectUri.substring(0, location.href.indexOf('#'));
+ redirectUri += (redirectUri.indexOf('?') == -1 ? '?' : '&') + 'redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
+ }
+ return redirectUri;
+ }
+ }
+ };
+ }
+
+ if (type == 'cordova') {
+ loginIframe.enable = false;
+
+ 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);
+
+ var callback;
+ var error;
+
+ ref.addEventListener('loadstart', function(event) {
+ if (event.url.indexOf('http://localhost') == 0) {
+ callback = parseCallback(event.url);
+ ref.close();
+ }
+ });
+
+ ref.addEventListener('loaderror', function(event) {
+ if (event.url.indexOf('http://localhost') != 0) {
+ error = true;
+ ref.close();
+ }
+ });
+
+ ref.addEventListener('exit', function(event) {
+ if (error || !callback) {
+ promise.setError();
+ } else {
+ processCallback(callback, promise);
+ }
+ });
+
+ return promise.promise;
+ },
+
+ logout: function(options) {
+ var promise = createPromise();
+
+ var logoutUrl = kc.createLogoutUrl(options);
+ var ref = window.open(logoutUrl, '_blank', 'location=no,hidden=yes');
+
+ var error;
+
+ ref.addEventListener('loadstart', function(event) {
+ if (event.url.indexOf('http://localhost') == 0) {
+ ref.close();
+ }
+ });
+
+ ref.addEventListener('loaderror', function(event) {
+ if (event.url.indexOf('http://localhost') != 0) {
+ error = true;
+ ref.close();
+ }
+ });
+
+ ref.addEventListener('exit', function(event) {
+ if (error) {
+ promise.setError();
+ } else {
+ kc.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;
+ }
+ }
+
+ if ( typeof module === "object" && module && typeof module.exports === "object" ) {
+ module.exports = Keycloak;
+ } else {
+ window.Keycloak = Keycloak;
+
+ if ( typeof define === "function" && define.amd ) {
+ define( "keycloak", [], function () { return Keycloak; } );
+ }
+ }
+})( window );
diff --git a/integration/js/src/main/resources/keycloak.js b/integration/js/src/main/resources/keycloak.js
index 798bfdd..8917581 100755
--- a/integration/js/src/main/resources/keycloak.js
+++ b/integration/js/src/main/resources/keycloak.js
@@ -261,7 +261,7 @@
throw 'Not authenticated';
}
- var expiresIn = kc.tokenParsed['exp'] - (new Date().getTime() / 1000);
+ var expiresIn = kc.tokenParsed['exp'] - (new Date().getTime() / 1000) + kc.timeSkew;
if (minValidity) {
expiresIn -= minValidity;
}
@@ -299,11 +299,18 @@
params += '&client_id=' + encodeURIComponent(kc.clientId);
}
+ var timeLocal = new Date().getTime();
+
req.onreadystatechange = function () {
if (req.readyState == 4) {
if (req.status == 200) {
+ timeLocal = (timeLocal + new Date().getTime()) / 2;
+
var tokenResponse = JSON.parse(req.responseText);
setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token']);
+
+ kc.timeSkew = Math.floor(timeLocal / 1000) - keycloak.tokenParsed.iat;
+
kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess();
for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
p.setSuccess(true);
@@ -385,11 +392,18 @@
req.withCredentials = true;
+ var timeLocal = new Date().getTime();
+
req.onreadystatechange = function() {
if (req.readyState == 4) {
if (req.status == 200) {
+ timeLocal = (timeLocal + new Date().getTime()) / 2;
+
var tokenResponse = JSON.parse(req.responseText);
setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token']);
+
+ kc.timeSkew = Math.floor(timeLocal / 1000) - keycloak.tokenParsed.iat;
+
kc.onAuthSuccess && kc.onAuthSuccess();
promise && promise.setSuccess();
} else {