Skip to content

Commit

Permalink
Remove legacy Promise APIs from Keycloak JS (#19389)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonkoops committed Mar 29, 2023
1 parent df6b9f9 commit 8f62751
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 78 deletions.
3 changes: 3 additions & 0 deletions docs/documentation/release_notes/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ include::topics/templates/document-attributes.adoc[]
:release_header_latest_link: {releasenotes_link_latest}
include::topics/templates/release-header.adoc[]

== {project_name_full} 22.0.0
include::topics/22_0_0.adoc[leveloffset=2]

== {project_name_full} 21.0.0
include::topics/21_0_0.adoc[leveloffset=2]

Expand Down
3 changes: 3 additions & 0 deletions docs/documentation/release_notes/topics/22_0_0.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
= Legacy Promise API removed from Keycloak JS adapter

With this release, we have removed the legacy Promise API methods from the Keycloak JS adapter. This means that calling `.success()` and `.error()` on promises returned from the adapter is no longer possible.
43 changes: 42 additions & 1 deletion docs/documentation/upgrading/topics/keycloak/changes-22_0_0.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,45 @@ Default `Client ID` mapper of `Service Account Client` has been changed. `Token
- https://www.rfc-editor.org/rfc/rfc7662#section-2.2[OAuth 2.0 Token Introspection]
- https://datatracker.ietf.org/doc/html/rfc8693#section-4.3[OAuth 2.0 Token Exchange]
`clientId` userSession note still exists.
`clientId` userSession note still exists.

= Legacy Promise API removed from Keycloak JS adapter

The legacy Promise API methods have been removed from the Keycloak JS adapter. This means that calling `.success()` and `.error()` on promises returned from the adapter is no longer possible. Instead standardized Promise methods such as https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then[`.then()`] and https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch[`.catch()`] should be used.

*Before:*
```javascript
const keycloak = new Keycloak();

keycloak.init()
.success(function(authenticated) {
alert(authenticated ? 'authenticated' : 'not authenticated');
}).error(function() {
alert('failed to initialize');
});
```

*After:*
```javascript
const keycloak = new Keycloak();

keycloak.init()
.then(function(authenticated) {
alert(authenticated ? 'authenticated' : 'not authenticated');
}).catch(function() {
alert('failed to initialize');
});
```

Or alternatively, when using the https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await[`await`] keyword to unwrap these promises:

```javascript
const keycloak = new Keycloak();

try {
const authenticated = await keycloak.init();
alert(authenticated ? 'authenticated' : 'not authenticated');
} catch (error) {
alert('failed to initialize');
}
```
43 changes: 12 additions & 31 deletions js/libs/keycloak-js/dist/keycloak.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,35 +277,16 @@ export interface KeycloakAccountOptions {
*/
redirectUri?: string;
}

export type KeycloakPromiseCallback<T> = (result: T) => void;

export interface 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>;
}

export interface KeycloakError {
error: string;
error_description: string;
}

export interface KeycloakAdapter {
login(options?: KeycloakLoginOptions): KeycloakPromise<void, void>;
logout(options?: KeycloakLogoutOptions): KeycloakPromise<void, void>;
register(options?: KeycloakRegisterOptions): KeycloakPromise<void, void>;
accountManagement(): KeycloakPromise<void, void>;
login(options?: KeycloakLoginOptions): Promise<void>;
logout(options?: KeycloakLogoutOptions): Promise<void>;
register(options?: KeycloakRegisterOptions): Promise<void>;
accountManagement(): Promise<void>;
redirectUri(options: { redirectUri: string; }, encodeHash: boolean): string;
}

Expand Down Expand Up @@ -536,30 +517,30 @@ declare class Keycloak {
* @param initOptions Initialization options.
* @returns A promise to set functions to be invoked on success or error.
*/
init(initOptions: KeycloakInitOptions): KeycloakPromise<boolean, KeycloakError>;
init(initOptions: KeycloakInitOptions): Promise<boolean>;

/**
* Redirects to login form.
* @param options Login options.
*/
login(options?: KeycloakLoginOptions): KeycloakPromise<void, void>;
login(options?: KeycloakLoginOptions): Promise<void>;

/**
* Redirects to logout.
* @param options Logout options.
*/
logout(options?: KeycloakLogoutOptions): KeycloakPromise<void, void>;
logout(options?: KeycloakLogoutOptions): Promise<void>;

/**
* Redirects to registration form.
* @param options The options used for the registration.
*/
register(options?: KeycloakRegisterOptions): KeycloakPromise<void, void>;
register(options?: KeycloakRegisterOptions): Promise<void>;

/**
* Redirects to the Account Management Console.
*/
accountManagement(): KeycloakPromise<void, void>;
accountManagement(): Promise<void>;

/**
* Returns the URL to login form.
Expand Down Expand Up @@ -610,7 +591,7 @@ declare class Keycloak {
* alert('Failed to refresh the token, or the session has expired');
* });
*/
updateToken(minValidity: number): KeycloakPromise<boolean, boolean>;
updateToken(minValidity: number): Promise<boolean>;

/**
* Clears authentication state, including tokens. This can be useful if
Expand All @@ -637,12 +618,12 @@ declare class Keycloak {
* Loads the user's profile.
* @returns A promise to set functions to be invoked on success or error.
*/
loadUserProfile(): KeycloakPromise<KeycloakProfile, void>;
loadUserProfile(): Promise<KeycloakProfile>;

/**
* @private Undocumented.
*/
loadUserInfo(): KeycloakPromise<{}, void>;
loadUserInfo(): Promise<{}>;
}

export default Keycloak;
Expand Down
29 changes: 0 additions & 29 deletions js/libs/keycloak-js/src/keycloak.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,6 @@ 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 Keycloak (config) {
if (!(this instanceof Keycloak)) {
return new Keycloak(config);
Expand Down Expand Up @@ -1166,26 +1157,6 @@ function Keycloak (config) {
p.reject = reject;
});

p.promise.success = function(callback) {
logPromiseDeprecation();

this.then(function handleSuccess(value) {
callback(value);
});

return this;
}

p.promise.error = function(callback) {
logPromiseDeprecation();

this.catch(function handleError(error) {
callback(error);
});

return this;
}

return p;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -811,23 +811,6 @@ public void fragmentInLoginFunction() {
});
}

@Test
public void testRefreshTokenWithDeprecatedPromiseHandles() {
String refreshWithDeprecatedHandles = "var callback = arguments[arguments.length - 1];" +
" window.keycloak.updateToken(9999).success(function (refreshed) {" +
" callback('Success handle');" +
" }).error(function () {" +
" callback('Error handle');" +
" });";

testExecutor.init(defaultArguments(), this::assertInitNotAuth)
.executeAsyncScript(refreshWithDeprecatedHandles, assertOutputContains("Error handle"))
.login(this::assertOnLoginPage)
.loginForm(testUser, this::assertOnTestAppUrl)
.init(defaultArguments(), this::assertInitAuth)
.executeAsyncScript(refreshWithDeprecatedHandles, assertOutputContains("Success handle"));
}

@Test
public void testAIAFromJavascriptAdapterSuccess() {
testExecutor.init(defaultArguments(), this::assertInitNotAuth)
Expand Down

0 comments on commit 8f62751

Please sign in to comment.