Update documentation and release notes for Keycloak JS (#33409)

Closes #32843

Signed-off-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
Jon Koops 2024-10-01 10:29:23 +02:00 committed by GitHub
parent 67b6b4c942
commit 21704a70c1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 154 additions and 19 deletions

View file

@ -19,10 +19,9 @@ import Keycloak from "keycloak-js";
import KeycloakAuthorization from "keycloak-js/authz"; import KeycloakAuthorization from "keycloak-js/authz";
const keycloak = new Keycloak({ const keycloak = new Keycloak({
// Replace this with your own configuration.
url: "http://keycloak-server", url: "http://keycloak-server",
realm: "myrealm", realm: "my-realm",
clientId: "myapp" clientId: "my-app"
}); });
const authorization = new KeycloakAuthorization(keycloak); const authorization = new KeycloakAuthorization(keycloak);

View file

@ -272,3 +272,9 @@ Based on the community feedback, we decided to undeprecate `https-trust-store-*`
= Lightweight access tokens for Admin REST API = Lightweight access tokens for Admin REST API
Lightweight access tokens can now be used on the admin REST API. The `security-admin-console` and `admin-cli` clients are now using lightweight access tokens by default, so “Always Use Lightweight Access Token” and “Full Scope Allowed” are now enabled on these two clients. However, the behavior in the admin console should effectively remain the same. Be cautious if you have made changes to these two clients and if you are using them for other purposes. Lightweight access tokens can now be used on the admin REST API. The `security-admin-console` and `admin-cli` clients are now using lightweight access tokens by default, so “Always Use Lightweight Access Token” and “Full Scope Allowed” are now enabled on these two clients. However, the behavior in the admin console should effectively remain the same. Be cautious if you have made changes to these two clients and if you are using them for other purposes.
= Keycloak JS now standalone
Keycloak JS is now a standalone library and is therefore no longer served statically from the Keycloak server. The goal is to de-couple the library from the Keycloak server, so that it can be refactored independently, simplifing the code and making it easier to maintain in the future. Additionaly the library is now free of third-party dependencies, which makes it more lightweight and easier to use in different environments.
For a complete breakdown of the changes consult the link:{upgradingguide_link}[{upgradingguide_name}].

View file

@ -20,7 +20,11 @@ If you are using the JavaScript adapter, you can also achieve the same behavior
[source,javascript] [source,javascript]
---- ----
const keycloak = new Keycloak('keycloak.json'); const keycloak = new Keycloak({
url: "http://keycloak-server",
realm: "my-realm",
clientId: "my-app"
);
await keycloak.createLoginUrl({ await keycloak.createLoginUrl({
idpHint: 'facebook' idpHint: 'facebook'

View file

@ -316,14 +316,128 @@ This might be the RHEL 9 based system, which itself is compliant with the FIPS 1
The `groups.setOrCreateChild()` method has been removed from that JavaScript-based Admin Client. If you are still using this method then use the `createChildGroup()` or `updateChildGroup()` methods instead. The `groups.setOrCreateChild()` method has been removed from that JavaScript-based Admin Client. If you are still using this method then use the `createChildGroup()` or `updateChildGroup()` methods instead.
= Keycloak JS methods for login are now `async` = Keycloak JS
Keycloak JS now utilizes the Web Crypto API to calculate the SHA-256 digests needed to support PKCE. Due to the asynchronous nature of this API the following public methods now return a `Promise`: This release includes several changes to Keycloak JS library that should be taken into account. The main motivation for these changes is to de-couple the library from the Keycloak server, so that it can be refactored independently, simplifing the code and making it easier to maintain in the future. The changes are as follows:
== The library is no longer served statically from the server
The Keycloak JS library is no longer served statically from the Keycloak server. This means that the following URLs are no longer available:
- `/js/keycloak-authz.js`
- `/js/keycloak-authz.min.js`
- `/js/keycloak.js`
- `/js/keycloak.min.js`
- `/js/{version}/keycloak-authz.js`
- `/js/{version}/keycloak-authz.min.js`
- `/js/{version}/keycloak.js`
- `/js/{version}/keycloak.min.js`
Additionally, the `keycloakJsUrl` property that linked to the library on these URLs has been removed from the the admin console theme. If your custom theme was using this property to include the library, you should update your theme to include the library using a different method.
If you are not already you should include the library in your project using a package manager like link:https://nodejs.org/en/learn/getting-started/an-introduction-to-the-npm-package-manager[NPM]. The library is available on the NPM registry as link:https://www.npmjs.com/package/keycloak-js[`keycloak-js`]. You can install it using the following command:
[source,bash]
----
npm install keycloak-js
----
Alternatively, the distribution of the server includes a copy of the library in the `keycloak-js-26.0.0.tgz` archive. You can copy the library from there into your project. If you are using the library directly in the browser without a build, you'll need to host the library yourself. A package manager is still the recommended way to include the library in your project, as it will make it easier to update the library in the future.
== Support for the UMD distribution has been removed
The UMD distribution link:https://github.com/umdjs/umd?tab=readme-ov-file#umd-universal-module-definition[Universal Module Definition] of the Keycloak JS library has been removed. This means that the library is no longer exposed as a global variable, and instead must be imported as link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules[a module]. This change is in line with modern JavaScript development practices, and allows for a more consitent experience between browsers and build tooling, and generally results in more predictable code with less side-effects.
If you are using a bundler like Vite or Webpack nothing changes, you'll have the same experience as before. If you are using the library directly in the browser, you'll need to update your code to import the library as a module:
[source,html]
----
<!-- Before -->
<script src="/path/to/keycloak.js"></script>
<script>
const keycloak = new Keycloak();
</script>
<!-- After -->
<script type="module">
import Keycloak from '/path/to/keycloak.js';
const keycloak = new Keycloak();
</script>
----
You can also opt to use an link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap[import map] make the import of the library less verbose:
[source,html]
----
<script type="importmap">
{
"imports": {
"keycloak-js": "/path/to/keycloak.js"
}
}
</script>
<script type="module">
// The library can now be imported without specifying the full path, providing a similar experience as with a bundler.
import Keycloak from 'keycloak-js';
const keycloak = new Keycloak();
</script>
----
If you are using TypeScript you may need to update your `tsconfig.json` to be able to resolve the library:
[source,json]
----
{
"compilerOptions": {
"moduleResolution": "Bundler"
}
}
----
== The configuration for the `Keycloak` instance is now required
Previously it was possible to construct a `Keycloak` instance without passing any configuration. The configuration would then automatically be loaded from the server from a `keycloak.json` file based on the path of the included `keycloak.js` script. Since the library is no longer statically served from the server this feature has been removed. You now need to pass the configuration explicitly when constructing a `Keycloak` instance:
[source,javascript]
----
// Before
const keycloak = new Keycloak();
// After
const keycloak = new Keycloak({
url: "http://keycloak-server",
realm: "my-realm",
clientId: "my-app"
});
// Alternatively, you can pass a URL to a `keycloak.json` file.
// Note this is not reccomended as it creates additional network requests, and is prone to change in the future.
const keycloak = new Keycloak('http://keycloak-server/path/to/keycloak.json');
----
== Methods for login are now `async`
Keycloak JS now utilizes the Web Crypto API to calculate the SHA-256 digests needed to support PKCE. Due to the asynchronous nature of this API the following public methods will now always return a `Promise`:
- `login()` - `login()`
- `createLoginUrl()` - `createLoginUrl()`
- `createRegisterUrl()` - `createRegisterUrl()`
Make sure to update your code to `await` these methods:
[source,javascript]
----
// Before
keycloak.login();
const loginUrl = keycloak.createLoginUrl();
const registerUrl = keycloak.createRegisterUrl();
// After
await keycloak.login();
const loginUrl = await keycloak.createLoginUrl();
const registerUrl = await keycloak.createRegisterUrl();
----
Make sure to update your code to `await` these methods. Make sure to update your code to `await` these methods.
= Stricter startup behavior for build-time options = Stricter startup behavior for build-time options

View file

@ -34,9 +34,9 @@ The following example shows how to initialize the adapter. Make sure that you re
import Keycloak from 'keycloak-js'; import Keycloak from 'keycloak-js';
const keycloak = new Keycloak({ const keycloak = new Keycloak({
url: 'http://keycloak-server', url: "http://keycloak-server",
realm: 'myrealm', realm: "my-realm",
clientId: 'myapp' clientId: "my-app"
}); });
try { try {
@ -147,7 +147,7 @@ WARNING: Session Status iframe functionality is limited in some modern browsers.
By default, the adapter uses the https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth[Authorization Code] flow. By default, the adapter uses the https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth[Authorization Code] flow.
With this flow, the {project_name} server returns an authorization code, not an authentication token, to the application. The JavaScript adapter exchanges the `code` for an access token and a refresh token after the browser is redirected back to the application. With this flow, the {project_name} server returns an authorization code, not an authentication token, to the application. The JavaScript adapter exchanges the `code` for an access token and a refresh token after the browser is redirected back to the application.
{project_name} also supports the https://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth[Implicit] flow where an access token is sent immediately after successful authentication with {project_name}. This flow may have better performance than the standard flow because no additional request exists to exchange the code for tokens, but it has implications when the access token expires. {project_name} also supports the https://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth[Implicit] flow where an access token is sent immediately after successful authentication with {project_name}. This flow may have better performance than the standard flow because no additional request exists to exchange the code for tokens, but it has implications when the access token expires.
@ -240,7 +240,11 @@ In some situations, you may need to run the adapter in environments that are not
import Keycloak from 'keycloak-js'; import Keycloak from 'keycloak-js';
import KeycloakCapacitorAdapter from 'keycloak-capacitor-adapter'; import KeycloakCapacitorAdapter from 'keycloak-capacitor-adapter';
const keycloak = new Keycloak(); const keycloak = new Keycloak({
url: "http://keycloak-server",
realm: "my-realm",
clientId: "my-app"
});
await keycloak.init({ await keycloak.init({
adapter: KeycloakCapacitorAdapter, adapter: KeycloakCapacitorAdapter,
@ -264,7 +268,11 @@ const MyCustomAdapter: KeycloakAdapter = {
// The other methods go here... // The other methods go here...
}; };
const keycloak = new Keycloak(); const keycloak = new Keycloak({
url: "http://keycloak-server",
realm: "my-realm",
clientId: "my-app"
});
await keycloak.init({ await keycloak.init({
adapter: MyCustomAdapter, adapter: MyCustomAdapter,
@ -297,9 +305,17 @@ An affected browser is for example Safari starting with version 13.1.
[source,javascript,subs="attributes+"] [source,javascript,subs="attributes+"]
---- ----
new Keycloak(); // Recommended way to initialize the adapter.
new Keycloak('http://localhost/keycloak.json'); new Keycloak({
new Keycloak({ url: 'http://localhost', realm: 'myrealm', clientId: 'myApp' }); url: "http://keycloak-server",
realm: "my-realm",
clientId: "my-app"
});
// Alternatively a string to the path of the `keycloak.json` file.
// Has some performance implications, as it will load the keycloak.json file from the server.
// This version might also change in the future and is therefore not recommended.
new Keycloak("http://keycloak-server/keycloak.json");
---- ----
=== Properties === Properties

View file

@ -74,8 +74,6 @@ export interface KeycloakInitOptions {
* For example: * For example:
* *
* ```ts * ```ts
* import Keycloak, { KeycloakAdapter } from 'keycloak-js';
*
* // Implement the 'KeycloakAdapter' interface so that all required methods are guaranteed to be present. * // Implement the 'KeycloakAdapter' interface so that all required methods are guaranteed to be present.
* const MyCustomAdapter: KeycloakAdapter = { * const MyCustomAdapter: KeycloakAdapter = {
* login(options) { * login(options) {
@ -85,8 +83,6 @@ export interface KeycloakInitOptions {
* // The other methods go here... * // The other methods go here...
* }; * };
* *
* const keycloak = new Keycloak();
*
* keycloak.init({ * keycloak.init({
* adapter: MyCustomAdapter, * adapter: MyCustomAdapter,
* }); * });