KEYCLOAK-9346 Add new KeycloakPromise to support native promises

Co-authored-by: mhajas <mhajas@redhat.com>
This commit is contained in:
Jon Koops 2019-12-23 19:52:30 +01:00 committed by Stian Thorgersen
parent bcb542d9cc
commit c1bf183998
5 changed files with 161 additions and 258 deletions

View file

@ -26,7 +26,7 @@ export = Keycloak;
* Creates a new Keycloak client instance.
* @param config A configuration object or path to a JSON config file.
*/
declare function Keycloak<TPromise extends Keycloak.KeycloakPromiseType = 'legacy'>(config?: Keycloak.KeycloakConfig | string): Keycloak.KeycloakInstance<TPromise>;
declare function Keycloak(config?: Keycloak.KeycloakConfig | string): Keycloak.KeycloakInstance;
declare namespace Keycloak {
type KeycloakAdapterName = 'cordova' | 'cordova-native' |'default' | any;
@ -34,7 +34,6 @@ declare namespace Keycloak {
type KeycloakResponseMode = 'query'|'fragment';
type KeycloakResponseType = 'code'|'id_token token'|'code id_token token';
type KeycloakFlow = 'standard'|'implicit'|'hybrid';
type KeycloakPromiseType = 'legacy' | 'native';
type KeycloakPkceMethod = 'S256';
interface KeycloakConfig {
@ -136,21 +135,6 @@ declare namespace Keycloak {
*/
flow?: KeycloakFlow;
/**
* Set the promise type. If set to `native` all methods returning a promise
* will return a native JavaScript promise. If not not specified then
* Keycloak specific legacy promise objects will be returned instead.
*
* Since native promises have become the industry standard it is highly
* recommended that you always specify `native` as the promise type.
*
* Note that in upcoming versions of Keycloak the default will be changed
* to `native`, and support for legacy promises will eventually be removed.
*
* @default legacy
*/
promiseType?: KeycloakPromiseType;
/**
* Configures the Proof Key for Code Exchange (PKCE) method to use.
* The currently allowed method is 'S256'.
@ -226,14 +210,18 @@ declare namespace Keycloak {
type KeycloakPromiseCallback<T> = (result: T) => void;
interface KeycloakPromise<TSuccess, TError> {
class KeycloakPromise<TSuccess, TError> extends Promise<TSuccess> {
/**
* Function to call if the promised action succeeds.
*
* @deprecated Use `.then()` instead.
*/
success(callback: KeycloakPromiseCallback<TSuccess>): KeycloakPromise<TSuccess, TError>;
/**
* Function to call if the promised action throws an error.
*
* @deprecated Use `.catch()` instead.
*/
error(callback: KeycloakPromiseCallback<TError>): KeycloakPromise<TSuccess, TError>;
}
@ -281,20 +269,11 @@ declare namespace Keycloak {
roles: string[];
}
// export interface KeycloakUserInfo {}
/**
* Conditional CompatPromise type in order to support
* both legacy promises and native promises as return types.
*/
type CompatPromise<TPromiseType extends KeycloakPromiseType, TSuccess, TError> =
TPromiseType extends 'native' ? Promise<TSuccess> : KeycloakPromise<TSuccess, TError>;
/**
* A client for the Keycloak authentication server.
* @see {@link https://keycloak.gitbooks.io/securing-client-applications-guide/content/topics/oidc/javascript-adapter.html|Keycloak JS adapter documentation}
*/
interface KeycloakInstance<TPromise extends KeycloakPromiseType = 'legacy'> {
interface KeycloakInstance {
/**
* Is true if the user is authenticated, false otherwise.
*/
@ -459,32 +438,32 @@ declare namespace Keycloak {
* @param initOptions Initialization options.
* @returns A promise to set functions to be invoked on success or error.
*/
init(initOptions: KeycloakInitOptions): CompatPromise<TPromise, boolean, KeycloakError>;
init(initOptions: KeycloakInitOptions): KeycloakPromise<boolean, KeycloakError>;
/**
* Redirects to login form.
* @param options Login options.
*/
login(options?: KeycloakLoginOptions): CompatPromise<TPromise, void, void>;
login(options?: KeycloakLoginOptions): KeycloakPromise<void, void>;
/**
* Redirects to logout.
* @param options Logout options.
* @param options.redirectUri Specifies the uri to redirect to after logout.
*/
logout(options?: any): CompatPromise<TPromise, void, void>;
logout(options?: any): KeycloakPromise<void, void>;
/**
* Redirects to registration form.
* @param options Supports same options as Keycloak#login but `action` is
* set to `'register'`.
*/
register(options?: any): CompatPromise<TPromise, void, void>;
register(options?: any): KeycloakPromise<void, void>;
/**
* Redirects to the Account Management Console.
*/
accountManagement(): CompatPromise<TPromise, void, void>;
accountManagement(): KeycloakPromise<void, void>;
/**
* Returns the URL to login form.
@ -536,7 +515,7 @@ declare namespace Keycloak {
* alert('Failed to refresh the token, or the session has expired');
* });
*/
updateToken(minValidity: number): CompatPromise<TPromise, boolean, boolean>;
updateToken(minValidity: number): KeycloakPromise<boolean, boolean>;
/**
* Clears authentication state, including tokens. This can be useful if
@ -563,11 +542,11 @@ declare namespace Keycloak {
* Loads the user's profile.
* @returns A promise to set functions to be invoked on success or error.
*/
loadUserProfile(): CompatPromise<TPromise, KeycloakProfile, void>;
loadUserProfile(): KeycloakPromise<KeycloakProfile, void>;
/**
* @private Undocumented.
*/
loadUserInfo(): CompatPromise<TPromise, {}, void>;
loadUserInfo(): KeycloakPromise<{}, void>;
}
}

View file

@ -43,6 +43,16 @@
*/
(function (r) { if (typeof exports === "object" && typeof module !== "undefined") { module.exports = r() } else if (typeof define === "function" && define.amd) { define([], r) } else { var e; if (typeof window !== "undefined") { e = window } else if (typeof global !== "undefined") { e = global } else if (typeof self !== "undefined") { e = self } else { e = this } e.base64js = r() } })(function () { var r, e, n; return function () { function r(e, n, t) { function o(f, i) { if (!n[f]) { if (!e[f]) { var u = "function" == typeof require && require; if (!i && u) return u(f, !0); if (a) return a(f, !0); var v = new Error("Cannot find module '" + f + "'"); throw v.code = "MODULE_NOT_FOUND", v } var d = n[f] = { exports: {} }; e[f][0].call(d.exports, function (r) { var n = e[f][1][r]; return o(n || r) }, d, d.exports, r, e, n, t) } return n[f].exports } for (var a = "function" == typeof require && require, f = 0; f < t.length; f++)o(t[f]); return o } return r }()({ "/": [function (r, e, n) { "use strict"; n.byteLength = d; n.toByteArray = h; n.fromByteArray = p; var t = []; var o = []; var a = typeof Uint8Array !== "undefined" ? Uint8Array : Array; var f = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; for (var i = 0, u = f.length; i < u; ++i) { t[i] = f[i]; o[f.charCodeAt(i)] = i } o["-".charCodeAt(0)] = 62; o["_".charCodeAt(0)] = 63; function v(r) { var e = r.length; if (e % 4 > 0) { throw new Error("Invalid string. Length must be a multiple of 4") } var n = r.indexOf("="); if (n === -1) n = e; var t = n === e ? 0 : 4 - n % 4; return [n, t] } function d(r) { var e = v(r); var n = e[0]; var t = e[1]; return (n + t) * 3 / 4 - t } function c(r, e, n) { return (e + n) * 3 / 4 - n } function h(r) { var e; var n = v(r); var t = n[0]; var f = n[1]; var i = new a(c(r, t, f)); var u = 0; var d = f > 0 ? t - 4 : t; for (var h = 0; h < d; h += 4) { e = o[r.charCodeAt(h)] << 18 | o[r.charCodeAt(h + 1)] << 12 | o[r.charCodeAt(h + 2)] << 6 | o[r.charCodeAt(h + 3)]; i[u++] = e >> 16 & 255; i[u++] = e >> 8 & 255; i[u++] = e & 255 } if (f === 2) { e = o[r.charCodeAt(h)] << 2 | o[r.charCodeAt(h + 1)] >> 4; i[u++] = e & 255 } if (f === 1) { e = o[r.charCodeAt(h)] << 10 | o[r.charCodeAt(h + 1)] << 4 | o[r.charCodeAt(h + 2)] >> 2; i[u++] = e >> 8 & 255; i[u++] = e & 255 } return i } function s(r) { return t[r >> 18 & 63] + t[r >> 12 & 63] + t[r >> 6 & 63] + t[r & 63] } function l(r, e, n) { var t; var o = []; for (var a = e; a < n; a += 3) { t = (r[a] << 16 & 16711680) + (r[a + 1] << 8 & 65280) + (r[a + 2] & 255); o.push(s(t)) } return o.join("") } function p(r) { var e; var n = r.length; var o = n % 3; var a = []; var f = 16383; for (var i = 0, u = n - o; i < u; i += f) { a.push(l(r, i, i + f > u ? u : i + f)) } if (o === 1) { e = r[n - 1]; a.push(t[e >> 2] + t[e << 4 & 63] + "==") } else if (o === 2) { e = (r[n - 2] << 8) + r[n - 1]; a.push(t[e >> 10] + t[e >> 4 & 63] + t[e << 2 & 63] + "=") } return a.join("") } }, {}] }, {}, [])("/") });
/**
* [promise-polyfill]{@link https://github.com/taylorhakes/promise-polyfill}
*
* @version v8.1.3
* @author Hakes, Taylor
* @copyright Hakes, Taylor 2014
* @license MIT
*/
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n():"function"==typeof define&&define.amd?define(n):n()}(0,function(){"use strict";function e(e){var n=this.constructor;return this.then(function(t){return n.resolve(e()).then(function(){return t})},function(t){return n.resolve(e()).then(function(){return n.reject(t)})})}function n(e){return!(!e||"undefined"==typeof e.length)}function t(){}function o(e){if(!(this instanceof o))throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=undefined,this._deferreds=[],c(e,this)}function r(e,n){for(;3===e._state;)e=e._value;0!==e._state?(e._handled=!0,o._immediateFn(function(){var t=1===e._state?n.onFulfilled:n.onRejected;if(null!==t){var o;try{o=t(e._value)}catch(r){return void f(n.promise,r)}i(n.promise,o)}else(1===e._state?i:f)(n.promise,e._value)})):e._deferreds.push(n)}function i(e,n){try{if(n===e)throw new TypeError("A promise cannot be resolved with itself.");if(n&&("object"==typeof n||"function"==typeof n)){var t=n.then;if(n instanceof o)return e._state=3,e._value=n,void u(e);if("function"==typeof t)return void c(function(e,n){return function(){e.apply(n,arguments)}}(t,n),e)}e._state=1,e._value=n,u(e)}catch(r){f(e,r)}}function f(e,n){e._state=2,e._value=n,u(e)}function u(e){2===e._state&&0===e._deferreds.length&&o._immediateFn(function(){e._handled||o._unhandledRejectionFn(e._value)});for(var n=0,t=e._deferreds.length;t>n;n++)r(e,e._deferreds[n]);e._deferreds=null}function c(e,n){var t=!1;try{e(function(e){t||(t=!0,i(n,e))},function(e){t||(t=!0,f(n,e))})}catch(o){if(t)return;t=!0,f(n,o)}}var a=setTimeout;o.prototype["catch"]=function(e){return this.then(null,e)},o.prototype.then=function(e,n){var o=new this.constructor(t);return r(this,new function(e,n,t){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof n?n:null,this.promise=t}(e,n,o)),o},o.prototype["finally"]=e,o.all=function(e){return new o(function(t,o){function r(e,n){try{if(n&&("object"==typeof n||"function"==typeof n)){var u=n.then;if("function"==typeof u)return void u.call(n,function(n){r(e,n)},o)}i[e]=n,0==--f&&t(i)}catch(c){o(c)}}if(!n(e))return o(new TypeError("Promise.all accepts an array"));var i=Array.prototype.slice.call(e);if(0===i.length)return t([]);for(var f=i.length,u=0;i.length>u;u++)r(u,i[u])})},o.resolve=function(e){return e&&"object"==typeof e&&e.constructor===o?e:new o(function(n){n(e)})},o.reject=function(e){return new o(function(n,t){t(e)})},o.race=function(e){return new o(function(t,r){if(!n(e))return r(new TypeError("Promise.race accepts an array"));for(var i=0,f=e.length;f>i;i++)o.resolve(e[i]).then(t,r)})},o._immediateFn="function"==typeof setImmediate&&function(e){setImmediate(e)}||function(e){a(e,0)},o._unhandledRejectionFn=function(e){void 0!==console&&console&&console.warn("Possible Unhandled Promise Rejection:",e)};var l=function(){if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if("undefined"!=typeof global)return global;throw Error("unable to locate global object")}();"Promise"in l?l.Promise.prototype["finally"]||(l.Promise.prototype["finally"]=e):l.Promise=o});
var Keycloak = factory( root["sha256"], root["base64js"] );
root["Keycloak"] = Keycloak;
@ -51,6 +61,51 @@
}
}
})(window, function (sha256_imported, base64js_imported) {
if (typeof Promise === 'undefined') {
throw Error('Keycloak requires an environment that supports Promises. Make sure that you include the appropriate polyfill.');
}
var loggedPromiseDeprecation = false;
function logPromiseDeprecation() {
if (!loggedPromiseDeprecation) {
loggedPromiseDeprecation = true;
console.warn('[KEYCLOAK] Usage of legacy style promise methods such as `.error()` and `.success()` has been deprecated and support will be removed in future versions. Use standard style promise methods such as `.then() and `.catch()` instead.');
}
}
function toKeycloakPromise(promise) {
promise.__proto__ = KeycloakPromise.prototype;
return promise;
}
function KeycloakPromise(executor) {
return toKeycloakPromise(new Promise(executor));
}
KeycloakPromise.prototype = Object.create(Promise.prototype);
KeycloakPromise.prototype.constructor = KeycloakPromise;
KeycloakPromise.prototype.success = function(callback) {
logPromiseDeprecation();
var promise = this.then(function handleSuccess(value) {
callback(value);
});
return toKeycloakPromise(promise);
};
KeycloakPromise.prototype.error = function(callback) {
logPromiseDeprecation();
var promise = this.catch(function handleError(error) {
callback(error);
});
return toKeycloakPromise(promise);
};
function Keycloak (config) {
if (!(this instanceof Keycloak)) {
return new Keycloak(config);
@ -109,13 +164,6 @@
loginIframe.interval = initOptions.checkLoginIframeInterval;
}
if (initOptions.promiseType === 'native') {
kc.useNativePromise = true;
} else {
console.warn('[KEYCLOAK] Using legacy promises is deprecated and will be removed in future versions. You can opt in to using native promises by setting `promiseType` to \'native\' when initializing Keycloak.');
kc.useNativePromise = false;
}
if (initOptions.onLoad === 'login-required') {
kc.loginRequired = true;
}
@ -179,13 +227,13 @@
kc.flow = 'standard';
}
var promise = createPromise(false);
var promise = createPromise();
var initPromise = createPromise(true);
initPromise.promise.success(function() {
var initPromise = createPromise();
initPromise.promise.then(function() {
kc.onReady && kc.onReady(kc.authenticated);
promise.setSuccess(kc.authenticated);
}).error(function(errorData) {
}).catch(function(errorData) {
promise.setError(errorData);
});
@ -196,19 +244,12 @@
if (!prompt) {
options.prompt = 'none';
}
if (kc.useNativePromise) {
kc.login(options).then(function () {
initPromise.setSuccess();
}).catch(function () {
initPromise.setError();
});
} else {
kc.login(options).success(function () {
initPromise.setSuccess();
}).error(function () {
initPromise.setError();
});
}
kc.login(options).then(function () {
initPromise.setSuccess();
}).catch(function () {
initPromise.setError();
});
}
var checkSsoSilently = function() {
@ -238,14 +279,14 @@
switch (initOptions.onLoad) {
case 'check-sso':
if (loginIframe.enable) {
setupCheckLoginIframe().success(function() {
checkLoginIframe().success(function (unchanged) {
setupCheckLoginIframe().then(function() {
checkLoginIframe().then(function (unchanged) {
if (!unchanged) {
kc.silentCheckSsoRedirectUri ? checkSsoSilently() : doLogin(false);
} else {
initPromise.setSuccess();
}
}).error(function () {
}).catch(function () {
initPromise.setError();
});
});
@ -269,9 +310,9 @@
}
if (callback && callback.valid) {
return setupCheckLoginIframe().success(function() {
return setupCheckLoginIframe().then(function() {
processCallback(callback, initPromise);
}).error(function (e) {
}).catch(function (e) {
initPromise.setError();
});
} else if (initOptions) {
@ -279,8 +320,8 @@
setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken);
if (loginIframe.enable) {
setupCheckLoginIframe().success(function() {
checkLoginIframe().success(function (unchanged) {
setupCheckLoginIframe().then(function() {
checkLoginIframe().then(function (unchanged) {
if (unchanged) {
kc.onAuthSuccess && kc.onAuthSuccess();
initPromise.setSuccess();
@ -288,15 +329,15 @@
} else {
initPromise.setSuccess();
}
}).error(function () {
}).catch(function () {
initPromise.setError();
});
});
} else {
kc.updateToken(-1).success(function() {
kc.updateToken(-1).then(function() {
kc.onAuthSuccess && kc.onAuthSuccess();
initPromise.setSuccess();
}).error(function() {
}).catch(function() {
kc.onAuthError && kc.onAuthError();
if (initOptions.onLoad) {
onLoad();
@ -315,8 +356,8 @@
}
}
configPromise.success(processInit);
configPromise.error(function() {
configPromise.then(processInit);
configPromise.catch(function() {
promise.setError();
});
@ -512,7 +553,7 @@
req.setRequestHeader('Accept', 'application/json');
req.setRequestHeader('Authorization', 'bearer ' + kc.token);
var promise = createPromise(false);
var promise = createPromise();
req.onreadystatechange = function () {
if (req.readyState == 4) {
@ -537,7 +578,7 @@
req.setRequestHeader('Accept', 'application/json');
req.setRequestHeader('Authorization', 'bearer ' + kc.token);
var promise = createPromise(false);
var promise = createPromise();
req.onreadystatechange = function () {
if (req.readyState == 4) {
@ -576,7 +617,7 @@
}
kc.updateToken = function(minValidity) {
var promise = createPromise(false);
var promise = createPromise();
if (!kc.refreshToken) {
promise.setError();
@ -650,9 +691,9 @@
if (loginIframe.enable) {
var iframePromise = checkLoginIframe();
iframePromise.success(function() {
iframePromise.then(function() {
exec();
}).error(function() {
}).catch(function() {
promise.setError();
});
} else {
@ -769,7 +810,7 @@
}
function loadConfig(url) {
var promise = createPromise(true);
var promise = createPromise();
var configUrl;
if (!config) {
@ -1117,15 +1158,7 @@
return result;
}
function createPromise(internal) {
if (!internal && kc.useNativePromise) {
return createNativePromise();
} else {
return createLegacyPromise();
}
}
function createNativePromise() {
function createPromise() {
// Need to create a native Promise which also preserves the
// interface of the custom promise type previously used by the API
var p = {
@ -1137,55 +1170,16 @@
p.reject(result);
}
};
p.promise = new Promise(function(resolve, reject) {
p.promise = new KeycloakPromise(function(resolve, reject) {
p.resolve = resolve;
p.reject = reject;
});
return p;
}
function createLegacyPromise() {
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(true);
var promise = createPromise();
if (!loginIframe.enable) {
promise.setSuccess();
@ -1251,7 +1245,7 @@
if (loginIframe.enable) {
if (kc.token) {
setTimeout(function() {
checkLoginIframe().success(function(unchanged) {
checkLoginIframe().then(function(unchanged) {
if (unchanged) {
scheduleCheckIframe();
}
@ -1262,7 +1256,7 @@
}
function checkLoginIframe() {
var promise = createPromise(true);
var promise = createPromise();
if (loginIframe.iframe && loginIframe.iframeOrigin ) {
var msg = kc.clientId + ' ' + (kc.sessionId ? kc.sessionId : '');
@ -1283,17 +1277,17 @@
return {
login: function(options) {
window.location.replace(kc.createLoginUrl(options));
return createPromise(false).promise;
return createPromise().promise;
},
logout: function(options) {
window.location.replace(kc.createLogoutUrl(options));
return createPromise(false).promise;
return createPromise().promise;
},
register: function(options) {
window.location.replace(kc.createRegisterUrl(options));
return createPromise(false).promise;
return createPromise().promise;
},
accountManagement : function() {
@ -1303,7 +1297,7 @@
} else {
throw "Not supported by the OIDC server";
}
return createPromise(false).promise;
return createPromise().promise;
},
redirectUri: function(options, encodeHash) {
@ -1362,7 +1356,7 @@
return {
login: function(options) {
var promise = createPromise(false);
var promise = createPromise();
var cordovaOptions = createCordovaOptions(options);
var loginUrl = kc.createLoginUrl(options);
@ -1410,7 +1404,7 @@
},
logout: function(options) {
var promise = createPromise(false);
var promise = createPromise();
var logoutUrl = kc.createLogoutUrl(options);
var ref = cordovaOpenWindowWrapper(logoutUrl, '_blank', 'location=no,hidden=yes');
@ -1445,7 +1439,7 @@
},
register : function(options) {
var promise = createPromise(false);
var promise = createPromise();
var registerUrl = kc.createRegisterUrl();
var cordovaOptions = createCordovaOptions(options);
var ref = cordovaOpenWindowWrapper(registerUrl, '_blank', cordovaOptions);
@ -1484,7 +1478,7 @@
return {
login: function(options) {
var promise = createPromise(false);
var promise = createPromise();
var loginUrl = kc.createLoginUrl(options);
universalLinks.subscribe('keycloak', function(event) {
@ -1499,7 +1493,7 @@
},
logout: function(options) {
var promise = createPromise(false);
var promise = createPromise();
var logoutUrl = kc.createLogoutUrl(options);
universalLinks.subscribe('keycloak', function(event) {
@ -1514,7 +1508,7 @@
},
register : function(options) {
var promise = createPromise(false);
var promise = createPromise();
var registerUrl = kc.createRegisterUrl(options);
universalLinks.subscribe('keycloak' , function(event) {
universalLinks.unsubscribe('keycloak');

View file

@ -131,25 +131,12 @@ public class JavascriptTestExecutor {
String arguments = argumentsBuilder.build();
String script;
// phantomjs do not support Native promises
if (argumentsBuilder.contains("promiseType", "native") && !"phantomjs".equals(System.getProperty("js.browser"))) {
script = "var callback = arguments[arguments.length - 1];" +
" window.keycloak.init(" + arguments + ").then(function (authenticated) {" +
" callback(\"Init Success (\" + (authenticated ? \"Authenticated\" : \"Not Authenticated\") + \")\");" +
" }, function () {" +
" callback(\"Init Error\");" +
" });";
} else {
script = "var callback = arguments[arguments.length - 1];" +
" window.keycloak.init(" + arguments + ").success(function (authenticated) {" +
" callback(\"Init Success (\" + (authenticated ? \"Authenticated\" : \"Not Authenticated\") + \")\");" +
" }).error(function () {" +
" callback(\"Init Error\");" +
" });";
}
String script = "var callback = arguments[arguments.length - 1];" +
" window.keycloak.init(" + arguments + ").then(function (authenticated) {" +
" callback(\"Init Success (\" + (authenticated ? \"Authenticated\" : \"Not Authenticated\") + \")\");" +
" }).catch(function () {" +
" callback(\"Init Error\");" +
" });";
Object output = jsExecutor.executeAsyncScript(script);
@ -174,30 +161,16 @@ public class JavascriptTestExecutor {
}
public JavascriptTestExecutor refreshToken(int value, JavascriptStateValidator validator) {
String script;
if (useNativePromises()) {
script = "var callback = arguments[arguments.length - 1];" +
" window.keycloak.updateToken(" + Integer.toString(value) + ").then(function (refreshed) {" +
" if (refreshed) {" +
" callback(window.keycloak.tokenParsed);" +
" } else {" +
" callback('Token not refreshed, valid for ' + Math.round(window.keycloak.tokenParsed.exp + window.keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds');" +
" }" +
" }, function () {" +
" callback('Failed to refresh token');" +
" });";
} else {
script = "var callback = arguments[arguments.length - 1];" +
" window.keycloak.updateToken(" + Integer.toString(value) + ").success(function (refreshed) {" +
" if (refreshed) {" +
" callback(window.keycloak.tokenParsed);" +
" } else {" +
" callback('Token not refreshed, valid for ' + Math.round(window.keycloak.tokenParsed.exp + window.keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds');" +
" }" +
" }).error(function () {" +
" callback('Failed to refresh token');" +
" });";
}
String script = "var callback = arguments[arguments.length - 1];" +
" window.keycloak.updateToken(" + Integer.toString(value) + ").then(function (refreshed) {" +
" if (refreshed) {" +
" callback(window.keycloak.tokenParsed);" +
" } else {" +
" callback('Token not refreshed, valid for ' + Math.round(window.keycloak.tokenParsed.exp + window.keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds');" +
" }" +
" }).catch(function () {" +
" callback('Failed to refresh token');" +
" });";
Object output = jsExecutor.executeAsyncScript(script);
@ -208,12 +181,6 @@ public class JavascriptTestExecutor {
return this;
}
public boolean useNativePromises() {
return (boolean) jsExecutor.executeScript("if (typeof window.keycloak !== 'undefined') {" +
"return window.keycloak.useNativePromise" +
"} else { return false}");
}
public JavascriptTestExecutor openAccountPage(JavascriptStateValidator validator) {
jsExecutor.executeScript("window.keycloak.accountManagement()");
waitForPageToLoad();
@ -234,22 +201,12 @@ public class JavascriptTestExecutor {
public JavascriptTestExecutor getProfile(JavascriptStateValidator validator) {
String script;
if (useNativePromises()) {
script = "var callback = arguments[arguments.length - 1];" +
String script = "var callback = arguments[arguments.length - 1];" +
" window.keycloak.loadUserProfile().then(function (profile) {" +
" callback(profile);" +
" }, function () {" +
" callback('Failed to load profile');" +
" });";
} else {
script = "var callback = arguments[arguments.length - 1];" +
" window.keycloak.loadUserProfile().success(function (profile) {" +
" callback(profile);" +
" }).error(function () {" +
" callback('Failed to load profile');" +
" });";
}
Object output = jsExecutor.executeAsyncScript(script);

View file

@ -601,22 +601,22 @@ public class JavascriptAdapterTest extends AbstractJavascriptTest {
@Test
public void reentrancyCallbackTest() {
testExecutor.logInAndInit(defaultArguments(), testUser, this::assertSuccessfullyLoggedIn)
.executeAsyncScript(
"var callback = arguments[arguments.length - 1];" +
"keycloak.updateToken(60).success(function () {" +
" event(\"First callback\");" +
" keycloak.updateToken(60).success(function () {" +
" event(\"Second callback\");" +
" callback(\"Success\");" +
" });" +
" }" +
");"
, (driver1, output, events) -> {
waitUntilElement(events).text().contains("First callback");
waitUntilElement(events).text().contains("Second callback");
waitUntilElement(events).text().not().contains("Auth Logout");
}
);
.executeAsyncScript(
"var callback = arguments[arguments.length - 1];" +
"keycloak.updateToken(60).then(function () {" +
" event(\"First callback\");" +
" keycloak.updateToken(60).then(function () {" +
" event(\"Second callback\");" +
" callback(\"Success\");" +
" });" +
" }" +
");"
, (driver1, output, events) -> {
waitUntilElement(events).text().contains("First callback");
waitUntilElement(events).text().contains("Second callback");
waitUntilElement(events).text().not().contains("Auth Logout");
}
);
}
@Test
@ -644,5 +644,20 @@ public class JavascriptAdapterTest extends AbstractJavascriptTest {
});
}
@Test
public void testRefreshTokenWithDeprecatedPromiseHandles() {
String refreshWithDeprecatedHandles = "var callback = arguments[arguments.length - 1];" +
" window.keycloak.updateToken(9999).success(function (refreshed) {" +
" callback('Success handle');" +
" }).catch(function () {" +
" callback('Catch handle');" +
" });";
testExecutor.init(defaultArguments(), this::assertInitNotAuth)
.executeAsyncScript(refreshWithDeprecatedHandles, assertOutputContains("Catch handle"))
.login(this::assertOnLoginPage)
.loginForm(testUser, this::assertOnTestAppUrl)
.init(defaultArguments(), this::assertSuccessfullyLoggedIn)
.executeAsyncScript(refreshWithDeprecatedHandles, assertOutputContains("Success handle"));
}
}

View file

@ -1,42 +0,0 @@
package org.keycloak.testsuite.javascript;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.testsuite.util.javascript.JSObjectBuilder;
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
public class JavascriptAdapterWithNativePromisesTest extends JavascriptAdapterTest {
@Override
protected JSObjectBuilder defaultArguments() {
return super.defaultArguments().add("promiseType", "native");
}
@Before
public void skipOnPhantomJS() {
Assume.assumeTrue("Native promises are not supported on PhantomJS", !"phantomjs".equals(System.getProperty("js.browser")));
}
@Test
@Override
public void reentrancyCallbackTest() {
testExecutor.logInAndInit(defaultArguments(), testUser, this::assertSuccessfullyLoggedIn)
.executeAsyncScript(
"var callback = arguments[arguments.length - 1];" +
"keycloak.updateToken(60).then(function () {" +
" event(\"First callback\");" +
" keycloak.updateToken(60).then(function () {" +
" event(\"Second callback\");" +
" callback(\"Success\");" +
" });" +
" }" +
");"
, (driver1, output, events) -> {
waitUntilElement(events).text().contains("First callback");
waitUntilElement(events).text().contains("Second callback");
waitUntilElement(events).text().not().contains("Auth Logout");
}
);
}
}