@forgerock/login-widget 1.3.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +361 -531
- package/dist/browser-DluKPU6C.js +1500 -0
- package/dist/browser-DluKPU6C.js.map +1 -0
- package/dist/core/captcha.config.d.ts +15 -0
- package/dist/core/component.store.d.ts +40 -0
- package/dist/core/interfaces.d.ts +12 -0
- package/dist/core/journey/config.store.d.ts +50 -0
- package/dist/core/journey/journey.interfaces.d.ts +91 -0
- package/dist/core/links.store.d.ts +26 -0
- package/dist/core/locale.store.d.ts +417 -0
- package/dist/core/oauth/oauth.store.d.ts +38 -0
- package/dist/core/oidc/oidc.store.d.ts +58 -0
- package/dist/core/protect/protect.store.d.ts +77 -0
- package/dist/core/style.store.d.ts +182 -0
- package/dist/core/user/user.store.d.ts +32 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +25312 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces.d.ts +76 -0
- package/dist/signals-sdk-DbtqPU1J.js +12331 -0
- package/dist/signals-sdk-DbtqPU1J.js.map +1 -0
- package/dist/stats.html +4950 -0
- package/dist/types.d.ts +79 -0
- package/dist/widget.config.d.ts +36 -0
- package/dist/widget.css +1 -0
- package/dist/widget.iife.js +94 -0
- package/dist/widget.iife.js.map +1 -0
- package/package.json +66 -8
- package/CHANGELOG.md +0 -586
- package/index.cjs +0 -47270
- package/index.cjs.map +0 -1
- package/index.d.ts +0 -2565
- package/index.js +0 -47260
- package/index.js.map +0 -1
- package/types.d.ts +0 -2568
- package/widget.css +0 -1881
package/README.md
CHANGED
|
@@ -1,117 +1,101 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
[](https://www.npmjs.com/package/@forgerock/login-widget)
|
|
3
2
|
[](http://commitizen.github.io/cz-cli/)
|
|
4
|
-
|
|
3
|
+
|
|
4
|
+
<p align="center">
|
|
5
|
+
<a href="https://github.com/ForgeRock">
|
|
6
|
+
<img src="https://www.forgerock.com/themes/custom/forgerock/images/fr-logo-horz-color.svg" alt="ForgeRock Logo">
|
|
7
|
+
</a>
|
|
8
|
+
<h2 align="center">ForgeRock Login Widget</h2>
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://www.npmjs.com/package/@forgerock/login-widget">npm</a>
|
|
11
|
+
·
|
|
12
|
+
<a href="#support">Support</a>
|
|
13
|
+
·
|
|
14
|
+
<a href="https://backstage.forgerock.com/docs/sdks/latest/javascript/webloginframework.html">Docs</a>
|
|
15
|
+
</p>
|
|
16
|
+
<hr/>
|
|
17
|
+
</p>
|
|
5
18
|
|
|
6
19
|
## Table of Contents
|
|
7
20
|
|
|
8
21
|
- [Overview](#overview)
|
|
9
22
|
- [Requirements](#requirements)
|
|
10
|
-
- [Quick Start
|
|
11
|
-
- [
|
|
12
|
-
- [
|
|
13
|
-
- [
|
|
14
|
-
- [
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
18
|
-
- [
|
|
19
|
-
- [
|
|
20
|
-
- [Requesting values from the ForgeRock platform](#requesting-values-from-the-forgerock-platform)
|
|
21
|
-
- [User Promises](#using-promises)
|
|
23
|
+
- [Quick Start](#quick-start)
|
|
24
|
+
- [Install](#install)
|
|
25
|
+
- [Add CSS](#add-css)
|
|
26
|
+
- [Mount the Widget](#mount-the-widget)
|
|
27
|
+
- [Observables Pattern](#observables-pattern)
|
|
28
|
+
- [Subscribe to Events](#subscribe-to-events)
|
|
29
|
+
- [Unsubscribe](#unsubscribe)
|
|
30
|
+
- [Get Current Values](#get-current-values)
|
|
31
|
+
- [Request Fresh Values](#request-fresh-values)
|
|
32
|
+
- [Use Promises](#use-promises)
|
|
22
33
|
- [Complete Widget API](#complete-widget-api)
|
|
23
34
|
- [Widget](#widget)
|
|
24
35
|
- [Configuration](#configuration)
|
|
25
36
|
- [Journey](#journey)
|
|
26
37
|
- [Component](#component)
|
|
27
38
|
- [User](#user)
|
|
28
|
-
- [
|
|
29
|
-
- [
|
|
30
|
-
- [
|
|
31
|
-
|
|
32
|
-
- [Currently unsupported](#currently-unsupported)
|
|
39
|
+
- [Styling Configuration](#styling-configuration)
|
|
40
|
+
- [Links Configuration](#links-configuration)
|
|
41
|
+
- [Content Configuration](#content-configuration)
|
|
42
|
+
- [Supported Callbacks](#supported-callbacks)
|
|
33
43
|
- [Disclaimer](#disclaimer)
|
|
34
44
|
- [License](#license)
|
|
35
45
|
|
|
36
46
|
## Overview
|
|
37
47
|
|
|
38
|
-
The Login Widget
|
|
48
|
+
The Login Widget is an all-inclusive UI component for handling login, registration, and related user flows in any modern JavaScript app. It works with React, Vue, Angular, Svelte, or vanilla JavaScript — it does not currently support Node.js or server-side rendering (SSR).
|
|
39
49
|
|
|
40
|
-
|
|
50
|
+
The widget uses [Journey Client](https://developer.pingidentity.com/orchsdks/journey/usage/javascript/index.html) for journey execution, and [OIDC Client](https://developer.pingidentity.com/orchsdks/oidc/usage/javascript-centralized-login.html) for OAuth/OIDC tokens and user info. It adds a UI rendering layer on top of these SDKs to eliminate the need to develop and maintain UI components for complex authentication flows. Although this rendering layer is developed with Svelte and Tailwind, both are "compiled away" and have no runtime dependencies. The resulting widget is library- and framework-agnostic.
|
|
41
51
|
|
|
42
|
-
|
|
52
|
+
The widget can be rendered in two form factors:
|
|
43
53
|
|
|
44
|
-
1. **Modal**
|
|
45
|
-
2. **Inline
|
|
54
|
+
1. **Modal** (default): Renders form elements inside a modal dialog that opens and closes. Mounted _outside_ your app's controlled DOM.
|
|
55
|
+
2. **Inline**: Just the form elements with no container. Mounted _inside_ your app's controlled DOM.
|
|
46
56
|
|
|
47
|
-
Both
|
|
48
|
-
|
|
49
|
-
It is highly recommended to start with the Modal form factor when in the experimenting or prototyping phase. It provides the quickest development experience for providing login and registration flows into your app with the least disruption to your existing codebase. The Modal will be controlled within your app, but rendered in its own DOM root node and visual layer.
|
|
57
|
+
Both form factors provide the same authentication, OAuth/OIDC, and user features. Start with Modal — it provides the quickest path to adding login/registration with minimal disruption to your existing codebase.
|
|
50
58
|
|
|
51
59
|
## Requirements
|
|
52
60
|
|
|
53
|
-
|
|
54
|
-
|
|
61
|
+
- An ECMAScript module or CommonJS enabled client-side JavaScript app
|
|
62
|
+
- A modern, fully-supported browser: Chrome, Firefox, Safari, or Chromium Edge
|
|
55
63
|
|
|
56
|
-
|
|
64
|
+
**Not supported**: Internet Explorer, Legacy Edge, WebView, Electron, and other modified browser-like environments.
|
|
57
65
|
|
|
58
|
-
## Quick Start
|
|
66
|
+
## Quick Start
|
|
59
67
|
|
|
60
|
-
###
|
|
68
|
+
### Install
|
|
61
69
|
|
|
62
70
|
```shell
|
|
63
71
|
npm install @forgerock/login-widget
|
|
64
72
|
```
|
|
65
73
|
|
|
66
|
-
###
|
|
67
|
-
|
|
68
|
-
There are a few ways to add the Widget's CSS to your product:
|
|
69
|
-
|
|
70
|
-
1. Import it into your JavaScript project as a module
|
|
71
|
-
2. Import it using a CSS preprocessor, like Sass, Less or PostCSS
|
|
72
|
-
3. Copy the CSS file from the package and link it into your HTML
|
|
73
|
-
|
|
74
|
-
If you decide to import the CSS into your JavaScript, make sure your bundler knows how to import and process the CSS as a module. If using a CSS preprocessor, ensure you configure your preprocessor to access files from within your `package/` directory.
|
|
75
|
-
|
|
76
|
-
Copying the file and pasting it into your project for linking in the HTML is the easiest.
|
|
74
|
+
### Add CSS
|
|
77
75
|
|
|
78
|
-
|
|
76
|
+
Import into your JavaScript:
|
|
79
77
|
|
|
80
78
|
```js
|
|
81
|
-
// app.js
|
|
82
79
|
import '@forgerock/login-widget/widget.css';
|
|
83
80
|
```
|
|
84
81
|
|
|
85
|
-
|
|
82
|
+
Import into your CSS:
|
|
86
83
|
|
|
87
84
|
```css
|
|
88
|
-
/* style.css */
|
|
89
85
|
@import '@forgerock/login-widget/widget.css';
|
|
90
86
|
```
|
|
91
87
|
|
|
92
|
-
|
|
88
|
+
Or link in HTML (copy the CSS file from the npm module into your static files):
|
|
93
89
|
|
|
94
90
|
```html
|
|
95
|
-
|
|
96
|
-
<!doctype html>
|
|
97
|
-
<html lang="en">
|
|
98
|
-
<head>
|
|
99
|
-
<!-- ... -->
|
|
100
|
-
<link rel="stylesheet" href="/path/to/file/widget.css" />
|
|
101
|
-
</head>
|
|
102
|
-
<body>
|
|
103
|
-
<!-- ... -->
|
|
104
|
-
</body>
|
|
105
|
-
</html>
|
|
91
|
+
<link rel="stylesheet" href="/path/to/file/widget.css" />
|
|
106
92
|
```
|
|
107
93
|
|
|
108
|
-
#### Controlling the CSS
|
|
94
|
+
#### Controlling the CSS Cascade
|
|
109
95
|
|
|
110
|
-
Though not required,
|
|
96
|
+
Though not required, using `@layer` ensures the browser applies CSS in the intended order regardless of import order. [Read more about @layer](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer).
|
|
111
97
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
1. Wrap your current CSS in a layer called `app`:
|
|
98
|
+
1. Wrap your CSS in a layer:
|
|
115
99
|
|
|
116
100
|
```css
|
|
117
101
|
@layer app {
|
|
@@ -119,14 +103,11 @@ Steps recommended:
|
|
|
119
103
|
}
|
|
120
104
|
```
|
|
121
105
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
2. Declare the order of layers in your index HTML file before any CSS is loaded:
|
|
106
|
+
2. Declare layer order in your HTML before any CSS loads:
|
|
125
107
|
|
|
126
108
|
```html
|
|
127
109
|
<style type="text/css">
|
|
128
110
|
@layer app;
|
|
129
|
-
/* List the Widget layers last */
|
|
130
111
|
@layer 'fr-widget.base';
|
|
131
112
|
@layer 'fr-widget.utilities';
|
|
132
113
|
@layer 'fr-widget.components';
|
|
@@ -134,246 +115,69 @@ Steps recommended:
|
|
|
134
115
|
</style>
|
|
135
116
|
```
|
|
136
117
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
### Using the Widget component
|
|
118
|
+
Widget CSS is fully namespaced — it will not overwrite your app's CSS unless you use the same selector naming convention.
|
|
140
119
|
|
|
141
|
-
|
|
120
|
+
### Mount the Widget
|
|
142
121
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
Example:
|
|
122
|
+
Add a dedicated element to your HTML file as a direct child of `<body>`, separate from your main app root:
|
|
146
123
|
|
|
147
124
|
```html
|
|
148
|
-
|
|
149
|
-
<
|
|
150
|
-
<
|
|
151
|
-
|
|
152
|
-
</head>
|
|
153
|
-
<body>
|
|
154
|
-
<!-- Root element for main app -->
|
|
155
|
-
<div id="root"></div>
|
|
156
|
-
|
|
157
|
-
<!-- Root element for Widget -->
|
|
158
|
-
<div id="widget-root"></div>
|
|
159
|
-
|
|
160
|
-
<!-- scripts ... -->
|
|
161
|
-
</body>
|
|
162
|
-
</html>
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
NOTE: We do not recommend mounting the Widget on a dynamic element that's inside your main application's "controlled DOM". If you're not experienced with mixed rendering types in the Virtual DOM, you may find this challenging at first. So, to ensure success at the start, manually adding it outside of your application's controlled DOM is best. It's recommended to put the element for Widget mounting in your static HTML file as a child of the `<body>` tag.
|
|
166
|
-
|
|
167
|
-
#### Instantiate the Widget
|
|
168
|
-
|
|
169
|
-
You can import the Widget into your app wherever you would like as a modal dialog (aka "lightbox"), this form factor is the default. Once the Widget is imported, you will need to instantiate it.
|
|
170
|
-
|
|
171
|
-
```js
|
|
172
|
-
// As modal dialog
|
|
173
|
-
import Widget from '@forgerock/login-widget';
|
|
174
|
-
|
|
175
|
-
// ...
|
|
176
|
-
|
|
177
|
-
// Grab the root element added to your HTML file
|
|
178
|
-
const widgetRootEl = document.getElementById('widget-root');
|
|
179
|
-
|
|
180
|
-
// Instantiate Widget with the `new` keyword
|
|
181
|
-
new Widget({
|
|
182
|
-
target: widgetRootEl, // Any existing element from static HTML file
|
|
183
|
-
});
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
This instantiates the component and mounts the Widget into the DOM. By default, it will be the Modal version, and will therefore be be hidden at first. Component controls will be discussed shortly.
|
|
187
|
-
|
|
188
|
-
Note: [See additional documentation about configuring the JS SDK](https://backstage.forgerock.com/docs/sdks/3.3/javascript/configuring/configuring-forgerock-sdk-settings-for-your-javascript-app.html).
|
|
189
|
-
|
|
190
|
-
#### The component lifecycle
|
|
191
|
-
|
|
192
|
-
When using the default Modal form factor, the first thing you'll want to do is open it. To do this, you need to import the `component` function. Executing this function will return a `componentEvents` object, you can name this whatever you'd like. Calling the `componentEvents.open` method will trigger the modal to animate into view.
|
|
193
|
-
|
|
194
|
-
```js
|
|
195
|
-
import Widget, { component } from '@forgerock/login-widget';
|
|
196
|
-
|
|
197
|
-
// ...
|
|
198
|
-
|
|
199
|
-
const componentEvents = component();
|
|
200
|
-
|
|
201
|
-
new Widget({ target: widgetRootEl }); // Instantiate the Widget
|
|
202
|
-
|
|
203
|
-
componentEvents.open(); // Ensure this is called after the Widget has been instantiated
|
|
204
|
-
|
|
205
|
-
// A more realistic pattern is calling this within a button click
|
|
206
|
-
const loginButton = document.getElementById('loginButton');
|
|
207
|
-
loginButton.addEventListener('click', () => {
|
|
208
|
-
componentEvents.open();
|
|
209
|
-
});
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
NOTE: Opening the modal by itself will just reveal the modal with a loading spinner. This is because the Widget has no data to inform it on what to render. We will solve this in the "Starting a journey" section.
|
|
213
|
-
|
|
214
|
-
In addition to being able to open the modal, you'll likely want to know when it's mounted, or closed by the user or upon completed authentication. This is done via the same API. A `subscribe` method will be on this `componentEvents` object (which technically makes this an observable), and all component related events will trigger the `subscribe` function to be called passing the `event` state to your callback function.
|
|
215
|
-
|
|
216
|
-
```js
|
|
217
|
-
componentEvents.subscribe((event) => {
|
|
218
|
-
if (event.mounted) {
|
|
219
|
-
console.log('Widget is mounted!');
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
When the user successfully authenticates, the modal will close itself and emit an event. You can detect this within your `subscribe` callback function and the `event` object.
|
|
225
|
-
|
|
226
|
-
```js
|
|
227
|
-
componentEvents.subscribe((event) => {
|
|
228
|
-
if (event.mounted) {
|
|
229
|
-
console.log('Widget is mounted!');
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (event.open === false) {
|
|
233
|
-
console.log(event.reason); // The reason for closing will be provided
|
|
234
|
-
}
|
|
235
|
-
});
|
|
125
|
+
<body>
|
|
126
|
+
<div id="root"></div>
|
|
127
|
+
<div id="widget-root"></div>
|
|
128
|
+
</body>
|
|
236
129
|
```
|
|
237
130
|
|
|
238
|
-
|
|
131
|
+
> **Tip**: Do not mount the modal form factor on a dynamic element inside your app's Virtual DOM. Hard-code it in your static HTML file.
|
|
239
132
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
Before the Widget can interact with the ForgeRock platform, it will need to be configured. We have a `configuration` function for this API.
|
|
243
|
-
|
|
244
|
-
First, import the `configuration` function from the module and call it to receive the config API. This API has a single method called `set`. Let's start with the minimal configuration.
|
|
133
|
+
Import, configure, and instantiate:
|
|
245
134
|
|
|
246
135
|
```js
|
|
247
|
-
import {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
136
|
+
import Widget, { configure, journey } from '@forgerock/login-widget';
|
|
137
|
+
|
|
138
|
+
// 1. Configure — async; awaiting it ensures both clients are ready before use
|
|
139
|
+
await configure({
|
|
140
|
+
// REQUIRED — the well-known URL, shared by the journey and OIDC clients
|
|
141
|
+
serverConfig: {
|
|
142
|
+
wellknown:
|
|
143
|
+
'https://your-tenant.forgeblocks.com/am/oauth2/alpha/.well-known/openid-configuration',
|
|
144
|
+
},
|
|
145
|
+
// REQUIRED if you use OAuth/OIDC tokens, user info, or logout
|
|
146
|
+
oidcClient: {
|
|
147
|
+
clientId: 'YourOauthClient',
|
|
148
|
+
redirectUri: `${window.location.origin}/callback`,
|
|
149
|
+
scope: 'openid profile email', // OPTIONAL — defaults to 'openid'
|
|
256
150
|
},
|
|
257
151
|
});
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
NOTE: It's best to configure the Widget at the top level of your application, like its `index.js` or `app.js` file. This will ensure the Widget has the configuration needed to call out to the ForgeRock platform whenever you use the other Widget APIs.
|
|
261
152
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
```js
|
|
267
|
-
import Widget, { journey } from '@forgerock/login-widget';
|
|
153
|
+
// 2. Instantiate
|
|
154
|
+
const widgetRootEl = document.getElementById('widget-root');
|
|
155
|
+
new Widget({ target: widgetRootEl });
|
|
268
156
|
|
|
157
|
+
// 3. Start a journey
|
|
269
158
|
const journeyEvents = journey();
|
|
270
|
-
|
|
271
|
-
// ...
|
|
272
|
-
|
|
273
|
-
new Widget({
|
|
274
|
-
target: widgetRootEl, // Any existing element in the DOM
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
// Ensure you call `.start` *AFTER* instantiating the Widget
|
|
278
159
|
journeyEvents.start();
|
|
279
|
-
|
|
280
|
-
// OR, in a more typical situation, call on button click
|
|
281
|
-
buttonElement.addEventListener('click', (event) => {
|
|
282
|
-
journeyEvents.start();
|
|
283
|
-
});
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
NOTE: This `journeyEvents.start` method can be called anywhere in your application, or anytime, as long as it's _after_ calling the configuration's `set` method and _after_ instantiating the Widget (which mounts it to the DOM) as both are requirements for a journey.
|
|
287
|
-
|
|
288
|
-
#### Getting a session
|
|
289
|
-
|
|
290
|
-
By default, the Widget is going to do more than just get a user's session. It will also get OAuth/OIDC tokens and retrieve user information. But, let's start small.
|
|
291
|
-
|
|
292
|
-
Let's only ask for session information, and disable OAuth and user info. This will simplify our initial setup. To do this, call the `journey` function passing false for `oauth` and `user`.
|
|
293
|
-
|
|
294
|
-
```js
|
|
295
|
-
const journeyEvents = journey({ oauth: false, user: false });
|
|
296
160
|
```
|
|
297
161
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
NOTE: It's also worth nothing that if you don't declare what journey you want to use, the ForgeRock platform will use what is marked as the default journey, usually just the basic Login journey.
|
|
301
|
-
|
|
302
|
-
#### Listening for journey completion
|
|
303
|
-
|
|
304
|
-
Use the `journeyEvents.subscribe` method (this is the observable part) to know when a user has completed their journey. Pass a callback function into this method to run on journey related events (there will be a quite a few of them). You will receive an event object with a lot of data in it. You'll want to conditionally check for the events you're interested in and ignore what you don't need.
|
|
305
|
-
|
|
306
|
-
```js
|
|
307
|
-
// ...
|
|
308
|
-
|
|
309
|
-
journeyEvents.subscribe((event) => {
|
|
310
|
-
// Will be called multiple times, so narrowing what you're interested in is important
|
|
311
|
-
if (event.journey.successful) {
|
|
312
|
-
// Will log once to the console when authentication has succeeded and returned session data
|
|
313
|
-
console.log(event);
|
|
314
|
-
}
|
|
315
|
-
});
|
|
316
|
-
```
|
|
162
|
+
> **Tip**: `configure()` is async — always `await` it at the top level of your application (`index.js` or `app.js`) before calling `journey().start()` or any other Widget API. This ensures both the OIDC and journey clients are fully constructed before any fetch is attempted.
|
|
317
163
|
|
|
318
|
-
|
|
164
|
+
## Observables Pattern
|
|
319
165
|
|
|
320
|
-
|
|
166
|
+
Most Widget APIs are asynchronous and use an event-centric observable pattern. The widget uses Svelte's simplified, standard observable implementation called a "store" — these are embedded in the widget and are not a dependency your app needs to manage.
|
|
321
167
|
|
|
322
|
-
|
|
168
|
+
[Read more about the Svelte store contract](https://svelte.dev/docs#component-format-script-4-prefix-stores-with-$-to-access-their-values-store-contract).
|
|
323
169
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
#### Instantiate the Widget (Inline)
|
|
327
|
-
|
|
328
|
-
Now, import the Widget where you'd like to mount it. In whatever way your framework requires, provide a reference to the element mounted in the actual DOM as the target of the Widget instantiation.
|
|
329
|
-
|
|
330
|
-
```js
|
|
331
|
-
// As inline
|
|
332
|
-
import Widget from '@forgerock/login-widget';
|
|
333
|
-
|
|
334
|
-
// ...
|
|
335
|
-
|
|
336
|
-
new Widget({
|
|
337
|
-
target: mountedDomElement, // ensure this is a reference to a real DOM element
|
|
338
|
-
props: {
|
|
339
|
-
type: 'inline', // Your JS SDK configuration; see below
|
|
340
|
-
},
|
|
341
|
-
});
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
This mounts your Widget into the DOM. If you choose the modal version, it will be hidden at first.
|
|
345
|
-
|
|
346
|
-
Note: [See additional documentation about configuring the JS SDK](https://backstage.forgerock.com/docs/sdks/3.3/javascript/configuring/configuring-forgerock-sdk-settings-for-your-javascript-app.html).
|
|
347
|
-
|
|
348
|
-
## What you need to know
|
|
349
|
-
|
|
350
|
-
Most of this Widget's APIs are asynchronous. As with many things in the JavaScript world, there are multiple patterns to handle asynchronous behavior. We've decided to centralize the Widget's APIs around the event-centric, Observable pattern. Since this Widget is powered by Svelte's compiler, we use Svelte's simplified, but standard Observable implementation called a "store". You can [read more about the Svelte store contract in their docs](https://svelte.dev/docs#component-format-script-4-prefix-stores-with-$-to-access-their-values-store-contract).
|
|
351
|
-
|
|
352
|
-
We believe this is an optimal pattern for UI development as it allows for a more dynamic, user experience. Your application will be updated with each event from emitted from within the Widget. These events could be "loading", "completed", "success" or "failure". To help illustrate this, let's take a look at one of the `user` APIs:
|
|
353
|
-
|
|
354
|
-
### Import the API object
|
|
170
|
+
### Subscribe to Events
|
|
355
171
|
|
|
356
172
|
```js
|
|
357
173
|
import { user } from '@forgerock/login-widget';
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
### Call API method to initiate observable
|
|
361
174
|
|
|
362
|
-
Create a variable and assign the created observable:
|
|
363
|
-
|
|
364
|
-
```js
|
|
365
175
|
const userInfoEvents = user.info();
|
|
366
|
-
```
|
|
367
176
|
|
|
368
|
-
### Use the subscribe method
|
|
369
|
-
|
|
370
|
-
It's important to note that an observable is a "stream" of events over time. This means that the callback function in a subscribe will be called for each and every event until you unsubscribe from it (more on this later).
|
|
371
|
-
|
|
372
|
-
```js
|
|
373
177
|
userInfoEvents.subscribe((event) => {
|
|
374
178
|
if (event.loading) {
|
|
375
179
|
console.log('User info is being requested from server');
|
|
376
|
-
} else if (event.
|
|
180
|
+
} else if (event.successful) {
|
|
377
181
|
console.log('User info request was successful');
|
|
378
182
|
console.log(event.response);
|
|
379
183
|
} else if (event.error) {
|
|
@@ -383,432 +187,458 @@ userInfoEvents.subscribe((event) => {
|
|
|
383
187
|
});
|
|
384
188
|
```
|
|
385
189
|
|
|
386
|
-
###
|
|
190
|
+
### Unsubscribe
|
|
387
191
|
|
|
388
|
-
Observables are not like
|
|
389
|
-
|
|
390
|
-
To unsubscribe, you assign a function that is returned from calling the subscribe method to a variable that can be called at a later time.
|
|
192
|
+
Observables are not like Promises — they don't resolve and get cleaned up. You must unsubscribe when no longer needed, especially in components that are created and destroyed frequently:
|
|
391
193
|
|
|
392
194
|
```js
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
// ...
|
|
195
|
+
const unsubscribe = userInfoEvents.subscribe((event) => console.log(event));
|
|
396
196
|
|
|
397
|
-
//
|
|
398
|
-
|
|
197
|
+
// Later, when no longer needed:
|
|
198
|
+
unsubscribe();
|
|
399
199
|
```
|
|
400
200
|
|
|
401
|
-
|
|
201
|
+
> **Note**: If subscribing at a top-level component that persists over the lifetime of your app, unsubscribing is not needed.
|
|
402
202
|
|
|
403
|
-
###
|
|
203
|
+
### Get Current Values
|
|
404
204
|
|
|
405
|
-
|
|
205
|
+
To get the current value stored within the Widget without subscribing to future events, call `subscribe` and immediately call the returned unsubscribe function:
|
|
406
206
|
|
|
407
207
|
```js
|
|
408
|
-
// Create variable for user info
|
|
409
208
|
let userInfo;
|
|
410
|
-
|
|
411
|
-
userInfoEvents.subscribe((event) => (userinfo = event.response))(); // <-- notice the second pair of parentheses
|
|
209
|
+
userInfoEvents.subscribe((event) => (userInfo = event.response))();
|
|
412
210
|
```
|
|
413
211
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
### Requesting values from the ForgeRock platform
|
|
212
|
+
### Request Fresh Values
|
|
417
213
|
|
|
418
|
-
|
|
214
|
+
Call action methods like `get` to request new values from the server. Any active `subscribe` callbacks will receive the resulting events:
|
|
419
215
|
|
|
420
216
|
```js
|
|
421
217
|
userInfoEvents.get();
|
|
422
218
|
```
|
|
423
219
|
|
|
424
|
-
|
|
220
|
+
### Use Promises
|
|
425
221
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
We recommend Observables, but the choice is up to you. All of the Widget's APIs that involve network calls have an alternative Promise implementation that can be used. Let's take the `get` method on `userInfoEvents` we saw above as a way to fetch new user info, and convert it into a Promise.
|
|
222
|
+
All Widget APIs that involve network calls also support Promises:
|
|
429
223
|
|
|
430
224
|
```js
|
|
431
|
-
// async
|
|
432
|
-
|
|
433
|
-
async function example() {
|
|
434
|
-
try {
|
|
435
|
-
userInfo = await userInfoEvents.get();
|
|
436
|
-
} catch (err) {
|
|
437
|
-
console.log(err);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
225
|
+
// async/await
|
|
226
|
+
const userInfo = await userInfoEvents.get();
|
|
440
227
|
|
|
441
|
-
// Promise
|
|
442
|
-
let userInfo;
|
|
228
|
+
// Promise chain
|
|
443
229
|
userInfoEvents
|
|
444
230
|
.get()
|
|
445
|
-
.then((data) => (
|
|
446
|
-
.catch((err) => console.
|
|
231
|
+
.then((data) => console.log(data))
|
|
232
|
+
.catch((err) => console.error(err));
|
|
447
233
|
```
|
|
448
234
|
|
|
449
235
|
## Complete Widget API
|
|
450
236
|
|
|
451
|
-
The Widget comes with methods and event handlers used to control the lifecycle of user journeys/authentication.
|
|
452
|
-
|
|
453
237
|
### Widget
|
|
454
238
|
|
|
455
239
|
```js
|
|
456
240
|
import Widget from '@forgerock/login-widget';
|
|
457
241
|
|
|
458
|
-
// Instantiate
|
|
242
|
+
// Instantiate
|
|
459
243
|
const widget = new Widget({
|
|
460
|
-
target: widgetRootEl, // REQUIRED;
|
|
244
|
+
target: widgetRootEl, // REQUIRED; a real DOM element
|
|
461
245
|
props: {
|
|
462
|
-
type: 'modal', // OPTIONAL; "modal" or "inline"
|
|
246
|
+
type: 'modal', // OPTIONAL; "modal" (default) or "inline"
|
|
463
247
|
},
|
|
464
248
|
});
|
|
465
249
|
|
|
466
|
-
//
|
|
250
|
+
// Remove widget from DOM and destroy listeners
|
|
467
251
|
widget.$destroy();
|
|
468
252
|
```
|
|
469
253
|
|
|
470
|
-
|
|
254
|
+
> **Note**: Call `$destroy()` if instantiating the Widget in a component that is frequently created and destroyed. We recommend instantiating higher in your application tree, in a component created once and preserved.
|
|
471
255
|
|
|
472
256
|
### Configuration
|
|
473
257
|
|
|
474
|
-
This configuration function produces a config API, and its `set` method is required for the underlying JavaScript SDK to interaction with the ForgeRock platform or access stored tokens.
|
|
475
|
-
|
|
476
258
|
```js
|
|
477
|
-
import {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
* Remaining config is optional with fallback values shown
|
|
492
|
-
*/
|
|
493
|
-
clientId: 'WebLoginWidgetClient', // String; defaults to 'WebLoginWidgetClient'
|
|
494
|
-
realmPath: 'alpha', // String; defaults to 'alpha'
|
|
495
|
-
redirectUri: window.location.href, // URL string; defaults to `window.location.href`
|
|
496
|
-
scope: 'openid email', // String; defaults to 'openid email'
|
|
497
|
-
/**
|
|
498
|
-
* NOT RECOMMENDED
|
|
499
|
-
* Rather, configure a journey/tree through the `.start({ journey: 'Login' })` options object,
|
|
500
|
-
* avoid setting it here
|
|
501
|
-
*/
|
|
502
|
-
tree: 'Login', // String, but NOT recommended.
|
|
259
|
+
import { configure } from '@forgerock/login-widget';
|
|
260
|
+
|
|
261
|
+
// configure() is async — await it before calling any other Widget API
|
|
262
|
+
await configure({
|
|
263
|
+
// REQUIRED — the well-known URL, shared by the journey and OIDC clients
|
|
264
|
+
serverConfig: {
|
|
265
|
+
wellknown:
|
|
266
|
+
'https://your-tenant.forgeblocks.com/am/oauth2/realms/root/realms/alpha/.well-known/openid-configuration',
|
|
267
|
+
},
|
|
268
|
+
// REQUIRED if you use OAuth/OIDC tokens, user info, or logout
|
|
269
|
+
oidcClient: {
|
|
270
|
+
clientId: 'WebOAuthClient',
|
|
271
|
+
redirectUri: `${window.location.origin}/callback`,
|
|
272
|
+
scope: 'openid profile email',
|
|
503
273
|
},
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
274
|
+
// OPTIONAL — logger for both clients; `level` gates verbosity and `custom`
|
|
275
|
+
// redirects SDK log output to your own sink. See the full example below.
|
|
276
|
+
logger: { level: 'warn' },
|
|
277
|
+
// OPTIONAL — request middleware for both clients; see the full example below
|
|
278
|
+
middleware: [],
|
|
279
|
+
// OPTIONAL — token storage config; see Storage section below
|
|
280
|
+
storage: { type: 'sessionStorage', name: 'tokens' },
|
|
281
|
+
// OPTIONAL — see dedicated sections below
|
|
507
282
|
content: {},
|
|
508
|
-
/**
|
|
509
|
-
* OPTIONAL; See below for the Links Configuration section
|
|
510
|
-
*/
|
|
511
283
|
links: {},
|
|
512
|
-
/**
|
|
513
|
-
* OPTIONAL; See below for Styling Configuration section
|
|
514
|
-
*/
|
|
515
284
|
style: {},
|
|
516
285
|
});
|
|
517
286
|
```
|
|
518
287
|
|
|
519
|
-
|
|
288
|
+
> **Migration note (2.0.0):** The `forgerock` config object has been replaced by `oidcClient`.
|
|
289
|
+
> Endpoint discovery is now driven by `serverConfig.wellknown`, shared by the journey and OIDC
|
|
290
|
+
> clients — `baseUrl`, `realmPath`, `timeout`, and `support` are no longer used. `clientId` and
|
|
291
|
+
> `redirectUri` are required when configuring `oidcClient`; `scope` defaults to `'openid'`. `tokenStore` has
|
|
292
|
+
> moved to a top-level `storage` option.
|
|
520
293
|
|
|
521
|
-
|
|
294
|
+
#### Logger
|
|
295
|
+
|
|
296
|
+
The top-level `logger` option is forwarded to both the journey and OIDC clients.
|
|
522
297
|
|
|
523
|
-
|
|
298
|
+
| Property | Type | Default | Description |
|
|
299
|
+
| -------- | -------------------------------------------------- | --------- | --------------------------------------------------------------------------------------- |
|
|
300
|
+
| `level` | `'none' \| 'error' \| 'warn' \| 'info' \| 'debug'` | `'error'` | Gates SDK log verbosity. `'none'` silences all SDK logs. |
|
|
301
|
+
| `custom` | `{ error, warn, info, debug }` | — | Sink for SDK log output. When set, the SDK calls your methods instead of the `console`. |
|
|
302
|
+
|
|
303
|
+
```js
|
|
304
|
+
await configure({
|
|
305
|
+
serverConfig: {
|
|
306
|
+
wellknown:
|
|
307
|
+
'https://your-tenant.forgeblocks.com/am/oauth2/alpha/.well-known/openid-configuration',
|
|
308
|
+
},
|
|
309
|
+
logger: {
|
|
310
|
+
level: 'debug',
|
|
311
|
+
// OPTIONAL — route SDK logs to your own sink instead of the console.
|
|
312
|
+
custom: {
|
|
313
|
+
error: (...args) => myLogger.error(...args),
|
|
314
|
+
warn: (...args) => myLogger.warn(...args),
|
|
315
|
+
info: (...args) => myLogger.info(...args),
|
|
316
|
+
debug: (...args) => myLogger.debug(...args),
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
oidcClient: {
|
|
320
|
+
clientId: 'WebOAuthClient',
|
|
321
|
+
redirectUri: `${window.location.origin}/callback`,
|
|
322
|
+
scope: 'openid profile email',
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
#### OIDC Client Options
|
|
328
|
+
|
|
329
|
+
All properties are nested inside `oidcClient`.
|
|
330
|
+
|
|
331
|
+
| Property | Type | Default | Description |
|
|
332
|
+
| ---------------- | ------------------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
333
|
+
| `clientId` | `string` | — | **Required.** OAuth 2.0 client ID. |
|
|
334
|
+
| `redirectUri` | `string` | — | **Required.** URI AM redirects to after authorization. |
|
|
335
|
+
| `scope` | `string` | `'openid'` | OAuth 2.0 scopes. |
|
|
336
|
+
| `oauthThreshold` | `number` | `30000` | Milliseconds before expiry to trigger background renewal. |
|
|
337
|
+
| `par` | `boolean` | auto | Use Pushed Authorization Requests. When omitted, the SDK auto-detects from the authorization server's `require_pushed_authorization_requests` metadata. Setting `false` while the server requires PAR is an error. |
|
|
338
|
+
| `loginHint` | `string` | — | Pre-fills the login identifier; bridged onto silent token renewal. |
|
|
339
|
+
| `acrValues` | `string` | — | Requested ACR values; bridged onto silent token renewal. |
|
|
340
|
+
| `query` | `Record<string, string>` | — | Extra authorize query params; bridged onto silent token renewal. |
|
|
341
|
+
|
|
342
|
+
Example with all optional OIDC options:
|
|
343
|
+
|
|
344
|
+
```js
|
|
345
|
+
await configure({
|
|
346
|
+
serverConfig: {
|
|
347
|
+
wellknown:
|
|
348
|
+
'https://your-tenant.forgeblocks.com/am/oauth2/alpha/.well-known/openid-configuration',
|
|
349
|
+
},
|
|
350
|
+
logger: { level: 'debug' },
|
|
351
|
+
middleware: [
|
|
352
|
+
(req, action, next) => {
|
|
353
|
+
console.log('[middleware]', action.type, req.url);
|
|
354
|
+
next();
|
|
355
|
+
},
|
|
356
|
+
],
|
|
357
|
+
storage: { type: 'sessionStorage', name: 'tokens', prefix: 'myapp' },
|
|
358
|
+
oidcClient: {
|
|
359
|
+
clientId: 'WebOAuthClient',
|
|
360
|
+
redirectUri: `${window.location.origin}/callback`,
|
|
361
|
+
scope: 'openid profile email',
|
|
362
|
+
oauthThreshold: 60000,
|
|
363
|
+
par: true,
|
|
364
|
+
loginHint: 'user@example.com',
|
|
365
|
+
acrValues: 'urn:acr:2fa',
|
|
366
|
+
query: { ui_locales: 'en-US' },
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
#### Storage
|
|
372
|
+
|
|
373
|
+
The top-level `storage` option configures where the OIDC client persists tokens. It mirrors
|
|
374
|
+
the SDK's `StorageConfig` union — `type` selects a browser store or a custom sink.
|
|
375
|
+
|
|
376
|
+
| Property | Type | Default | Description |
|
|
377
|
+
| -------- | ------------------------------------------------ | ---------------- | ----------------------------------------------------------------------- |
|
|
378
|
+
| `type` | `'localStorage' \| 'sessionStorage' \| 'custom'` | `'localStorage'` | Storage backend. `'custom'` requires a `custom` sink. |
|
|
379
|
+
| `name` | `string` | — | **Required.** Storage key name. |
|
|
380
|
+
| `prefix` | `string` | `'pic'` | Key prefix for storage entries. |
|
|
381
|
+
| `custom` | `{ get, set, remove }` | — | **Required when `type: 'custom'`.** Async functions for your own store. |
|
|
382
|
+
|
|
383
|
+
```js
|
|
384
|
+
// Browser store
|
|
385
|
+
storage: { type: 'sessionStorage', name: 'tokens', prefix: 'myapp' },
|
|
386
|
+
|
|
387
|
+
// Custom store
|
|
388
|
+
storage: {
|
|
389
|
+
type: 'custom',
|
|
390
|
+
name: 'tokens',
|
|
391
|
+
custom: {
|
|
392
|
+
get: async (key) => myStore.read(key),
|
|
393
|
+
set: async (key, value) => myStore.write(key, value),
|
|
394
|
+
remove: async (key) => myStore.delete(key),
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Journey
|
|
524
400
|
|
|
525
401
|
```js
|
|
526
402
|
import { journey } from '@forgerock/login-widget';
|
|
527
403
|
|
|
528
|
-
// Call to start the journey
|
|
529
|
-
// Optional config can be passed in, see below for more details
|
|
530
404
|
const journeyEvents = journey({
|
|
531
|
-
oauth: true, // OPTIONAL;
|
|
532
|
-
user: true, // OPTIONAL; default
|
|
405
|
+
oauth: true, // OPTIONAL; default true; use OAuth flow for tokens
|
|
406
|
+
user: true, // OPTIONAL; default true; fetch user info from /userinfo
|
|
533
407
|
});
|
|
534
408
|
|
|
535
409
|
// Start a journey
|
|
536
410
|
journeyEvents.start({
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
resumeUrl: window.location.href, // OPTIONAL; the full URL for resuming a tree (see resuming a journey section)
|
|
411
|
+
journey: 'Login', // OPTIONAL; journey name (omit to let AM choose the default)
|
|
412
|
+
resumeUrl: window.location.href, // OPTIONAL; URL for resuming a suspended journey
|
|
540
413
|
});
|
|
541
414
|
|
|
542
|
-
// Change a journey
|
|
415
|
+
// Change to a different journey
|
|
543
416
|
journeyEvents.change({
|
|
544
|
-
|
|
545
|
-
journey: 'Registration', // OPTIONAL; choice the journey or tree you want to start
|
|
417
|
+
journey: 'Registration',
|
|
546
418
|
});
|
|
547
419
|
|
|
548
|
-
//
|
|
549
|
-
|
|
550
|
-
journey.
|
|
551
|
-
|
|
420
|
+
// Listen for events
|
|
421
|
+
journeyEvents.subscribe((event) => {
|
|
422
|
+
if (event.journey.successful) {
|
|
423
|
+
console.log('Authentication succeeded', event);
|
|
424
|
+
}
|
|
552
425
|
});
|
|
553
426
|
```
|
|
554
427
|
|
|
555
|
-
|
|
428
|
+
**Journey event schema:**
|
|
556
429
|
|
|
557
430
|
```js
|
|
558
|
-
// response
|
|
559
431
|
{
|
|
560
432
|
journey: {
|
|
561
|
-
completed: false,
|
|
562
|
-
error: null,
|
|
563
|
-
loading: false,
|
|
564
|
-
step: null,
|
|
565
|
-
successful: false,
|
|
566
|
-
response: null,
|
|
433
|
+
completed: false, // boolean
|
|
434
|
+
error: null, // null or { code, message, step }
|
|
435
|
+
loading: false, // boolean
|
|
436
|
+
step: null, // null or step object from ForgeRock AM
|
|
437
|
+
successful: false, // boolean
|
|
438
|
+
response: null, // null or success response from AM
|
|
567
439
|
},
|
|
568
440
|
oauth: {
|
|
569
|
-
completed: false,
|
|
570
|
-
error: null,
|
|
571
|
-
loading: false,
|
|
572
|
-
successful: false,
|
|
573
|
-
response: null,
|
|
441
|
+
completed: false, // boolean
|
|
442
|
+
error: null, // null or { code, message }
|
|
443
|
+
loading: false, // boolean
|
|
444
|
+
successful: false, // boolean
|
|
445
|
+
response: null, // null or OAuth/OIDC tokens
|
|
574
446
|
},
|
|
575
447
|
user: {
|
|
576
|
-
completed: false,
|
|
577
|
-
error: null,
|
|
578
|
-
loading: false,
|
|
579
|
-
successful: false,
|
|
580
|
-
response: null,
|
|
448
|
+
completed: false, // boolean
|
|
449
|
+
error: null, // null or { code, message }
|
|
450
|
+
loading: false, // boolean
|
|
451
|
+
successful: false, // boolean
|
|
452
|
+
response: null, // null or user info (driven by OAuth scope)
|
|
581
453
|
},
|
|
582
454
|
}
|
|
583
455
|
```
|
|
584
456
|
|
|
585
457
|
### Component
|
|
586
458
|
|
|
587
|
-
The
|
|
459
|
+
The `component` API controls the widget's lifecycle. For the modal form factor, it provides `open` and `close` methods.
|
|
588
460
|
|
|
589
461
|
```js
|
|
590
462
|
import { component } from '@forgerock/login-widget';
|
|
591
463
|
|
|
592
|
-
// Initiate the component API
|
|
593
464
|
const componentEvents = component();
|
|
594
465
|
|
|
595
|
-
//
|
|
596
|
-
// When using the modal type, you will also receive open and close events.
|
|
597
|
-
// The property `reason` will be either "auto", "external", or "user" (see below)
|
|
598
|
-
componentEvents.subscribe((event) => {
|
|
599
|
-
/* Run anything you want */
|
|
600
|
-
});
|
|
601
|
-
|
|
602
|
-
// Open the modal
|
|
466
|
+
// Open/close the modal
|
|
603
467
|
componentEvents.open();
|
|
604
|
-
|
|
605
|
-
// Close the modal
|
|
606
468
|
componentEvents.close();
|
|
469
|
+
|
|
470
|
+
// Listen for lifecycle events
|
|
471
|
+
componentEvents.subscribe((event) => {
|
|
472
|
+
if (event.mounted) console.log('Widget is mounted');
|
|
473
|
+
if (event.open === false) console.log('Modal closed:', event.reason);
|
|
474
|
+
});
|
|
607
475
|
```
|
|
608
476
|
|
|
609
|
-
|
|
477
|
+
**Component event schema:**
|
|
610
478
|
|
|
611
479
|
```js
|
|
612
480
|
{
|
|
613
|
-
error: null,
|
|
481
|
+
error: null, // null or { code, message, step }
|
|
614
482
|
mounted: false, // boolean
|
|
615
|
-
open: null,
|
|
616
|
-
reason: null,
|
|
617
|
-
type: null,
|
|
483
|
+
open: null, // boolean or null (null for inline type)
|
|
484
|
+
reason: null, // "user" | "auto" | "external"
|
|
485
|
+
type: null, // "modal" | "inline"
|
|
618
486
|
}
|
|
619
487
|
```
|
|
620
488
|
|
|
621
|
-
|
|
489
|
+
**Close reasons:**
|
|
622
490
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
491
|
+
| Reason | Description |
|
|
492
|
+
| ------------ | -------------------------------------------- |
|
|
493
|
+
| `"user"` | User closed the dialog via UI |
|
|
494
|
+
| `"auto"` | Modal closed after successful authentication |
|
|
495
|
+
| `"external"` | Application called `componentEvents.close()` |
|
|
626
496
|
|
|
627
497
|
### User
|
|
628
498
|
|
|
629
499
|
```js
|
|
630
500
|
import { user } from '@forgerock/login-widget';
|
|
631
501
|
|
|
632
|
-
|
|
633
|
-
* User info API
|
|
634
|
-
*/
|
|
502
|
+
// User info
|
|
635
503
|
const userEvents = user.info();
|
|
636
|
-
|
|
637
|
-
userEvents.
|
|
638
|
-
// Will return current, *local*, user info and future state changes
|
|
639
|
-
console.log(event);
|
|
640
|
-
});
|
|
641
|
-
// Fetch/get fresh user info from ForgeRock server
|
|
642
|
-
userEvents.get(); // New state will be returned in your `userEvents.subscribe` callback function
|
|
504
|
+
userEvents.subscribe((event) => console.log(event));
|
|
505
|
+
userEvents.get(); // Fetch fresh user info from server
|
|
643
506
|
|
|
644
|
-
|
|
645
|
-
* User tokens API
|
|
646
|
-
*/
|
|
507
|
+
// User tokens
|
|
647
508
|
const tokenEvents = user.tokens();
|
|
648
|
-
|
|
649
|
-
tokenEvents.
|
|
650
|
-
// Will return current, *local*, user tokens and future state changes
|
|
651
|
-
console.log(event);
|
|
652
|
-
});
|
|
653
|
-
// Fetch/get fresh user tokens from ForgeRock server
|
|
654
|
-
tokenEvents.get(); // New state will be returned in your tokenEvents.subscribe` callback function
|
|
655
|
-
|
|
656
|
-
/**
|
|
657
|
-
* Logout
|
|
658
|
-
* Log user out and clear user data (info and tokens)
|
|
659
|
-
*/
|
|
660
|
-
user.logout(); // Resets user and emits event to your info and tokens' `.subscribe` callback function
|
|
661
|
-
```
|
|
662
|
-
|
|
663
|
-
Schema for user info `event`:
|
|
509
|
+
tokenEvents.subscribe((event) => console.log(event));
|
|
510
|
+
tokenEvents.get(); // Fetch fresh tokens from server
|
|
664
511
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
completed: false, // boolean
|
|
668
|
-
error: null, // null or object with `code`, `message` and `step` that failed
|
|
669
|
-
loading: false, // boolean
|
|
670
|
-
successful: false, // boolean
|
|
671
|
-
response: null, // object returned from the `/userinfo` endpoint configured in ForgeRock
|
|
672
|
-
}
|
|
512
|
+
// Logout
|
|
513
|
+
user.logout(); // Clears user data and emits events to subscribers
|
|
673
514
|
```
|
|
674
515
|
|
|
675
|
-
|
|
516
|
+
**User info / token event schema:**
|
|
676
517
|
|
|
677
518
|
```js
|
|
678
519
|
{
|
|
679
|
-
completed: false,
|
|
680
|
-
error: null,
|
|
681
|
-
loading: false,
|
|
520
|
+
completed: false, // boolean
|
|
521
|
+
error: null, // null or { code, message }
|
|
522
|
+
loading: false, // boolean
|
|
682
523
|
successful: false, // boolean
|
|
683
|
-
response: null,
|
|
524
|
+
response: null, // object from /userinfo or /access_token endpoint
|
|
684
525
|
}
|
|
685
526
|
```
|
|
686
527
|
|
|
687
|
-
###
|
|
688
|
-
|
|
689
|
-
The Widget has an alias to the JavaScript SDK's `HttpClient.request`, which is a convenience wrapper around the native `fetch`. All this does is auto-inject the Access Token into the `Authorization` header and manage some of the lifecycle around the token.
|
|
528
|
+
### Calling protected resources
|
|
690
529
|
|
|
691
|
-
|
|
530
|
+
> **Removed in 2.0.0:** The `request` export (an alias to the legacy `HttpClient.request`) has been
|
|
531
|
+
> removed. `@forgerock/oidc-client` does not provide an HTTP client. Get the access token from
|
|
532
|
+
> `user.tokens()` and call `fetch` directly, adding the `Authorization` header yourself:
|
|
692
533
|
|
|
693
534
|
```js
|
|
694
|
-
import
|
|
695
|
-
|
|
696
|
-
// See below for more details on the options
|
|
697
|
-
const response = await request({ init: { method: 'GET' }, url: 'https://protected.resource.com' });
|
|
698
|
-
```
|
|
535
|
+
import { user } from '@forgerock/login-widget';
|
|
699
536
|
|
|
700
|
-
|
|
537
|
+
const tokenEvents = user.tokens();
|
|
538
|
+
const { response: tokens } = await tokenEvents.get();
|
|
701
539
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
// Options object for `fetch` API: https://developer.mozilla.org/en-US/docs/Web/API/fetch
|
|
540
|
+
const response = await fetch('https://protected.resource.com', {
|
|
541
|
+
method: 'GET',
|
|
542
|
+
headers: {
|
|
543
|
+
Authorization: `Bearer ${tokens.accessToken}`,
|
|
707
544
|
},
|
|
708
|
-
|
|
709
|
-
url: 'https://protected.resource.com', // String; the URL of the resource
|
|
710
|
-
|
|
711
|
-
// Unsupported properties
|
|
712
|
-
authorization: {},
|
|
713
|
-
requiresNewToken: () => {},
|
|
714
|
-
}
|
|
545
|
+
});
|
|
715
546
|
```
|
|
716
547
|
|
|
717
|
-
|
|
548
|
+
> **Note**: The legacy `request` automatically refreshed tokens on a 401 and parsed Identity Gateway
|
|
549
|
+
> policy advice. Those behaviors are not provided by the new SDK and must be implemented by the consumer if needed.
|
|
718
550
|
|
|
719
551
|
### Styling Configuration
|
|
720
552
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
Example:
|
|
553
|
+
Configure the widget's visual appearance:
|
|
724
554
|
|
|
725
555
|
```js
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
config.set({
|
|
556
|
+
await configure({
|
|
729
557
|
style: {
|
|
730
|
-
checksAndRadios: 'animated', // OPTIONAL;
|
|
731
|
-
labels: 'floating', // OPTIONAL;
|
|
558
|
+
checksAndRadios: 'animated', // OPTIONAL; 'animated' or 'standard'
|
|
559
|
+
labels: 'floating', // OPTIONAL; 'floating' or 'stacked'
|
|
732
560
|
logo: {
|
|
733
|
-
// OPTIONAL;
|
|
734
|
-
dark: 'https://example.com/img/white-logo.png',
|
|
735
|
-
light: 'https://example.com/img/black-logo.png', // REQUIRED if logo
|
|
736
|
-
height: 300,
|
|
737
|
-
width: 400,
|
|
561
|
+
// OPTIONAL; modal only
|
|
562
|
+
dark: 'https://example.com/img/white-logo.png',
|
|
563
|
+
light: 'https://example.com/img/black-logo.png', // REQUIRED if logo provided
|
|
564
|
+
height: 300,
|
|
565
|
+
width: 400,
|
|
738
566
|
},
|
|
739
567
|
sections: {
|
|
740
|
-
// OPTIONAL;
|
|
741
|
-
header: false, //
|
|
568
|
+
// OPTIONAL; modal only
|
|
569
|
+
header: false, // Display a header section with logo
|
|
742
570
|
},
|
|
743
571
|
stage: {
|
|
744
|
-
icon: true, // OPTIONAL;
|
|
572
|
+
icon: true, // OPTIONAL; display generic stage icons
|
|
745
573
|
},
|
|
746
574
|
},
|
|
747
575
|
});
|
|
748
576
|
```
|
|
749
577
|
|
|
750
|
-
Note
|
|
578
|
+
> **Note**: The `logo` and `sections` properties only apply to the modal form factor.
|
|
751
579
|
|
|
752
580
|
### Links Configuration
|
|
753
581
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
Example:
|
|
582
|
+
Set the URL for your Terms & Conditions page (used by `TermsAndConditionsCallback`):
|
|
757
583
|
|
|
758
584
|
```js
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
config.set({
|
|
585
|
+
await configure({
|
|
762
586
|
links: {
|
|
763
|
-
termsAndConditions: 'https://example.com/terms',
|
|
587
|
+
termsAndConditions: 'https://example.com/terms',
|
|
764
588
|
},
|
|
765
589
|
});
|
|
766
590
|
```
|
|
767
591
|
|
|
768
592
|
### Content Configuration
|
|
769
593
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
Example:
|
|
594
|
+
Override the widget's default content with custom text. For the full schema, see the [en-US locale file](https://github.com/ForgeRock/forgerock-web-login-framework/tree/main/core/locales).
|
|
773
595
|
|
|
774
596
|
```js
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
597
|
+
await configure({
|
|
598
|
+
content: {
|
|
599
|
+
// Custom content that overrides Widget defaults
|
|
600
|
+
},
|
|
779
601
|
});
|
|
780
602
|
```
|
|
781
603
|
|
|
782
|
-
|
|
604
|
+
### CAPTCHA Configuration
|
|
605
|
+
|
|
606
|
+
AM does not signal invisible mode in the callback payload for either `ReCaptchaCallback` or `ReCaptchaEnterpriseCallback`. Use the `captcha` option to configure invisible rendering:
|
|
607
|
+
|
|
608
|
+
```js
|
|
609
|
+
await configure({
|
|
610
|
+
captcha: {
|
|
611
|
+
mode: 'invisible', // 'visible' (default) | 'invisible'
|
|
612
|
+
},
|
|
613
|
+
});
|
|
614
|
+
```
|
|
783
615
|
|
|
784
|
-
|
|
616
|
+
**Script loading:** The widget automatically injects the required CAPTCHA script at mount time — no manual `<script>` tag is needed. If the provider API (`window.grecaptcha` / `window.hcaptcha`) is already present on the page when the widget mounts, injection is skipped.
|
|
785
617
|
|
|
786
|
-
|
|
787
|
-
2. Device Profile
|
|
618
|
+
## Supported Callbacks
|
|
788
619
|
|
|
789
|
-
|
|
620
|
+
The widget supports the following ForgeRock callbacks:
|
|
790
621
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
622
|
+
- Page node
|
|
623
|
+
- Username, Password
|
|
624
|
+
- WebAuthn (registration and login)
|
|
625
|
+
- Push authentication
|
|
626
|
+
- One-time password verification
|
|
627
|
+
- Social login (Apple, Facebook, Google)
|
|
628
|
+
- Email suspend ("magic links")
|
|
629
|
+
- Device profile
|
|
630
|
+
- reCAPTCHA v2 (visible + invisible), reCAPTCHA Enterprise, hCaptcha (visible + invisible)
|
|
631
|
+
- QR codes
|
|
632
|
+
- Ping Protect
|
|
798
633
|
|
|
799
634
|
## Disclaimer
|
|
800
635
|
|
|
801
|
-
> **This code is provided by ForgeRock on an
|
|
802
|
-
|
|
803
|
-
<!---------------------------------------------------------------------------------------------------------->
|
|
804
|
-
<!-- LICENSE - Links to the MIT LICENSE file in each repo. -->
|
|
636
|
+
> **This code is provided by ForgeRock on an "as is" basis, without warranty of any kind, to the fullest extent permitted by law. ForgeRock does not represent or warrant or make any guarantee regarding the use of this code or the accuracy, timeliness or completeness of any data or information relating to this code, and ForgeRock hereby disclaims all warranties whether express, or implied or statutory, including without limitation the implied warranties of merchantability, fitness for a particular purpose, and any warranty of non-infringement. ForgeRock shall not have any liability arising out of or related to any use, implementation or configuration of this code, including but not limited to use for any commercial purpose. Any action or suit relating to the use of the code may be brought only in the courts of a jurisdiction wherein ForgeRock resides or in which ForgeRock conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions.**
|
|
805
637
|
|
|
806
638
|
## License
|
|
807
639
|
|
|
808
|
-
This project is licensed under the MIT License
|
|
640
|
+
This project is licensed under the MIT License — see the [LICENSE](https://github.com/ForgeRock/forgerock-web-login-framework/blob/main/LICENSE) file for details.
|
|
809
641
|
|
|
810
642
|
---
|
|
811
643
|
|
|
812
|
-
© Copyright 2022-
|
|
813
|
-
|
|
814
|
-
[forgerock-logo]: https://www.forgerock.com/themes/custom/forgerock/images/fr-logo-horz-color.svg 'ForgeRock Logo'
|
|
644
|
+
© Copyright 2022-2026 Ping Identity Corporation. All Rights Reserved.
|