Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .changeset/sour-jobs-count.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
'@forgerock/sdk-request-middleware': patch
'@forgerock/iframe-manager': patch
'@forgerock/storage': patch
'@forgerock/sdk-logger': patch
'@forgerock/davinci-client': patch
'@forgerock/device-client': patch
'@forgerock/sdk-utilities': patch
'@forgerock/oidc-client': patch
'@forgerock/sdk-types': patch
'@forgerock/protect': patch
---

Update READMES. Fix types and comments.
4 changes: 1 addition & 3 deletions e2e/davinci-suites/src/fido.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,7 @@ test.describe('FIDO/WebAuthn Tests', () => {
await expect(page.getByText('FIDO2 Test Form')).toBeVisible();
});

// Note: This test is currently not working due to a DaVinci issue where the authentication options
// are not included in the response.
test.skip('Register and authenticate with usernameless', async ({ page }) => {
test('Register and authenticate with usernameless', async ({ page }) => {
const { navigate } = asyncEvents(page);

await navigate(
Expand Down
2 changes: 2 additions & 0 deletions packages/davinci-client/src/lib/fido/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

The `fido` API provides an interface for registering and authenticating with the WebAuthn API and transforming data to and from DaVinci. These methods transform options from DaVinci into WebAuthn compatible options, then call `navigator.credentials.create` or `navigator.credentials.get`, and finally transform the output of the WebAuthn API into a valid payload to send back to DaVinci.

**Note**: To use this module, browser support is required for `navigator.credentials.create` and `navigator.credentials.get`

## Installation and Initialization

The `fido` module is exported as a member of the `@forgerock/davinci-client` package and is intended to be used alongside the `davinciClient` to progress through a flow. To install the necessary dependencies, run:
Expand Down
30 changes: 14 additions & 16 deletions packages/device-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ The `deviceClient` API provides a structured interface for managing various type
2. [Installation](#installation)
3. [Configuration](#configuration)
4. [API Methods](#api-methods)

- [OATH Management](#oath-management)
- [PUSH Management](#push-management)
- [WebAuthn Management](#webauthn-management)
Expand Down Expand Up @@ -36,8 +35,7 @@ npm install @forgerock/device-client --save
To configure the `deviceClient`, you need to provide a `ConfigOptions` object that includes the base URL for the server and the realm path.

```typescript
import { deviceClient } from '@forgerock/device-client';
import { type ConfigOptions } from '@forgerock/javascript-sdk';
import { deviceClient, type ConfigOptions } from '@forgerock/device-client';

const config: ConfigOptions = {
serverConfig: {
Expand All @@ -59,59 +57,59 @@ const apiClient = deviceClient(config);

#### Methods

- **get(query: RetrieveOathQuery): Promise<OathDevice[]>**
- **get(query: RetrieveOathQuery): Promise<OathDevice[] | { error: unknown }>**
- Retrieves Oath devices based on the specified query.

- **delete(query: RetrieveOathQuery & { device: OathDevice }): Promise\<null>**
- **delete(query: RetrieveOathQuery & { device: OathDevice }): Promise<null | { error: unknown }>**
- Deletes an Oath device based on the provided query and device information.

### PUSH Management

#### Methods

- **get(query: PushDeviceQuery): Promise<PushDevice[]>**
- **get(query: PushDeviceQuery): Promise<PushDevice[] | { error: unknown }>**
- Retrieves Push devices based on the specified query.

- **delete(query: DeleteDeviceQuery): Promise\<null>**
- **delete(query: DeleteDeviceQuery): Promise<null | { error: unknown }>**
- Deletes a Push device based on the provided query.

### WebAuthn Management

#### Methods

- **get(query: WebAuthnQuery): Promise<WebAuthnDevice[]>**
- **get(query: WebAuthnQuery): Promise<WebAuthnDevice[] | { error: unknown }>**
- Retrieves WebAuthn devices based on the specified query.

- **update(query: WebAuthnQuery & { device: WebAuthnDevice }): Promise\<UpdatedWebAuthnDevice>**
- **update(query: WebAuthnQuery & { device: WebAuthnDevice }): Promise<UpdatedWebAuthnDevice | { error: unknown }>**
- Updates the name of a WebAuthn device based on the provided query and body.

- **delete(query: WebAuthnQuery & { device: WebAuthnDevice | UpdatedWebAuthnDevice }): Promise\<null>**
- **delete(query: WebAuthnQuery & { device: WebAuthnDevice | UpdatedWebAuthnDevice }): Promise<null | { error: unknown }>**
- Deletes a WebAuthn device based on the provided query and body.

### Bound Devices Management

#### Methods

- **get(query: GetBoundDevicesQuery): Promise\<Device[]>**
- **get(query: GetBoundDevicesQuery): Promise<Device[] | { error: unknown }>**
- Retrieves bound devices based on the specified query.

- **update(query: BoundDeviceQuery): Promise\<Device>**
- **update(query: BoundDeviceQuery): Promise<Device | { error: unknown }>**
- Updates the name of a bound device based on the provided query.

- **delete(query: BoundDeviceQuery): Promise\<null>**
- **delete(query: BoundDeviceQuery): Promise<null | { error: unknown }>**
- Deletes a bound device based on the provided query.

### Device Profiling Management

#### Methods

- **get(query: GetProfileDevices): Promise\<ProfileDevice[]>**
- **get(query: GetProfileDevices): Promise<ProfileDevice[] | { error: unknown }>**
- Retrieves device profiles based on the specified query.

- **update(query: ProfileDevicesQuery): Promise\<ProfileDevice>**
- **update(query: ProfileDevicesQuery): Promise<ProfileDevice | { error: unknown }>**
- Updates the name of a device profile based on the provided query.

- **delete(query: ProfileDevicesQuery): Promise\<null>**
- **delete(query: ProfileDevicesQuery): Promise<null | { error: unknown }>**
- Deletes a device profile based on the provided query.

## Example Usage
Expand Down
20 changes: 12 additions & 8 deletions packages/device-client/src/lib/device.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@
import { type ConfigOptions } from '@forgerock/javascript-sdk';
import { configureStore } from '@reduxjs/toolkit';
import { deviceService } from './services/index.js';
import { OathDevice, RetrieveOathQuery } from './types/oath.types.js';
import { DeleteDeviceQuery, PushDevice, PushDeviceQuery } from './types/push-device.types.js';
import { UpdatedWebAuthnDevice, WebAuthnDevice, WebAuthnQuery } from './types/webauthn.types.js';
import { BoundDeviceQuery, Device, GetBoundDevicesQuery } from './types/bound-device.types.js';
import {
import type { OathDevice, RetrieveOathQuery } from './types/oath.types.js';
import type { DeleteDeviceQuery, PushDevice, PushDeviceQuery } from './types/push-device.types.js';
import type {
UpdatedWebAuthnDevice,
WebAuthnDevice,
WebAuthnQuery,
} from './types/webauthn.types.js';
import type { BoundDeviceQuery, Device, GetBoundDevicesQuery } from './types/bound-device.types.js';
import type {
GetProfileDevices,
ProfileDevice,
ProfileDevicesQuery,
Expand Down Expand Up @@ -290,7 +294,7 @@ export const deviceClient = (config: ConfigOptions) => {
* Get profile devices
*
* @async
* @function update
* @function get
* @param {GetProfileDevices} query - The query used to get profile devices
* @returns {Promise<ProfileDevice[] | { error: unknown }>} - A promise that resolves to the response data or an error object if the response is not valid.
*/
Expand Down Expand Up @@ -336,8 +340,8 @@ export const deviceClient = (config: ConfigOptions) => {
* Get profile devices
*
* @async
* @function update
* @param {ProfileDevicesQuery} query - The query used to update a profile device
* @function delete
* @param {ProfileDevicesQuery} query - The query used to delete a profile device
* @returns {Promise<null | { error: unknown }>} - A promise that resolves to null or an error object if the response is not valid.
*/
delete: async function (query: ProfileDevicesQuery): Promise<null | { error: unknown }> {
Expand Down
6 changes: 4 additions & 2 deletions packages/oidc-client/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# oidc-client
# OIDC Client

A generic OpenID Connect (OIDC) client library for JavaScript and TypeScript, designed to work with any OIDC-compliant identity provider.
A generic OpenID Connect (OIDC) client library for JavaScript and TypeScript, designed to work with PingOne platforms.

The oidc module follows the [OIDC](https://openid.net/specs/openid-connect-core-1_0.html) specification and provides a simple and easy-to-use API to interact with the OIDC server. It allows you to authenticate, retrieve the access token, revoke the token, and sign out from the OIDC server.

```js
// Initialize OIDC Client
Expand Down
16 changes: 8 additions & 8 deletions packages/protect/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ resumeBehavioralData();

## Quickstart with a PingOne AIC or PingAM Authentication Journey

The Ping Protect module is intended to be used along with the ForgeRock JavaScript SDK to provide the Protect feature.
The Ping Protect module is intended to be used along with the Journey client to provide the Protect feature.

### Requirements

1. PingOne Advanced Identity Cloud (aka PingOne AIC) platform or an up-to-date Ping Identity Access Management (aka PingAM)
2. PingOne tenant with Protect enabled
3. A Ping Protect Service configured in AIC or AM
4. A journey/tree with the appropriate Protect Nodes
5. A client application with the `@forgerock/javascript-sdk` and `@forgerock/protect` modules installed
5. A client application with the `@forgerock/journey-client` and `@forgerock/protect` modules installed

### Integrate into a Client Application

Expand All @@ -31,11 +31,11 @@ The Ping Protect module is intended to be used along with the ForgeRock JavaScri
Install both modules and their latest versions:

```sh
npm install @forgerock/javascript-sdk @forgerock/protect
npm install @forgerock/journey-client @forgerock/protect
```

```sh
pnpm install @forgerock/javascript-sdk @forgerock/protect
pnpm install @forgerock/journey-client @forgerock/protect
```

#### Initialization (Recommended)
Expand Down Expand Up @@ -70,10 +70,10 @@ if (step.getCallbacksOfType('PingOneProtectInitializeCallback')) {

#### Data collection

You then call the `FRAuth.next` method after initialization to move the user forward in the journey.
You then call the `next` method after initialization to move the user forward in the journey.

```js
FRAuth.next(step);
journeyClient.next(step);
```

At some point in the journey, and as late as possible in order to collect as much data as you can, you will come across the `PingOneProtectEvaluationCallback`. This is when you call the `getData` method to package what's been collected for the server to evaluate.
Expand All @@ -92,12 +92,12 @@ Now that we have the data, set it on the callback in order to send it to the ser
```js
callback.setData(data);

FRAuth.next(step);
journeyClient.next(step);
```

### Error Handling

The Protect API methods will return an error object if they fail. When you encounter an error during initialization or evaluation, set the error message on the callback using the `setClientError` method. Setting the message on the callback is how it gets sent to the server on the `FRAuth.next` method call.
The Protect API methods will return an error object if they fail. When you encounter an error during initialization or evaluation, set the error message on the callback using the `setClientError` method. Setting the message on the callback is how it gets sent to the server on the `next` method call.

```js
if (step.getCallbacksOfType('PingOneProtectInitializeCallback')) {
Expand Down
26 changes: 13 additions & 13 deletions packages/sdk-effects/iframe-manager/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# IFrame Manager (`@pingidentity/sdk-effects/iframe-manager`)
# IFrame Manager (`@forgerock/iframe-manager`)

## Overview

Expand All @@ -19,10 +19,8 @@ This utility fundamentally relies on the browser's **Same-Origin Policy**. The f

## Installation

This effect is typically part of a larger SDK. Assume it's imported or available within your project structure like so (adjust path as necessary):

```typescript
import iFrameManager from './path/to/iframe-manager.effects'; // Adjust path as needed
import iFrameManager from '@forgerock/iframe-manager';

const iframeMgr = iFrameManager();
```
Expand All @@ -40,18 +38,15 @@ This is the main factory function that initializes the effect.
This method creates a hidden iframe, initiates navigation, and waits for a redirect back to the application's origin containing specific query parameters.

- **`options`**: `GetParamsFromIFrameOptions` - An object containing configuration for the iframe request.

- **`url: string`**: The initial URL to load within the hidden iframe. This URL is expected to eventually redirect back to the application's origin.
- **`timeout: number`**: The maximum time in milliseconds to wait for the entire operation to complete successfully (i.e., for a redirect containing success or error parameters). If the timeout is reached before completion, the promise rejects.

* **`successParams: string[]`**: An array of query parameter _keys_. If the final redirect URL (on the same origin) contains **at least one** of these keys in its query string, the promise will **resolve**.
* **`errorParams: string[]`**: An array of query parameter _keys_. If the final redirect URL (on the same origin) contains **any** of these keys in its query string, the promise will **reject**. Error parameters are checked _before_ success parameters.
* **`errorParams: string[]`**: An array of query parameter _keys_. If the final redirect URL (on the same origin) contains **any** of these keys in its query string, the promise will **resolve** with all parsed parameters (including the error parameters). The caller must check the returned parameters for error keys. Error parameters are checked _before_ success parameters.
- _Note:_ Both `successParams` and `errorParams` must be provided and contain at least one key.

- **Returns**: `Promise<ResolvedParams>`

- **On Success**: Resolves with `ResolvedParams`, an object containing _all_ query parameters parsed from the final redirect URL's query string. This occurs when the iframe redirects back to the same origin and its URL contains at least one key listed in `successParams` (and no keys listed in `errorParams`).
- **On Failure**: Rejects with:
- **On Failure**: Resolves with:
- `ResolvedParams`: An object containing _all_ parsed query parameters if the final redirect URL contains any key listed in `errorParams`.
- An object `{ type: 'internal_error', message: 'iframe timed out' }` if the specified `timeout` is reached before a success or error condition is met.
- An object `{ type: 'internal_error', message: 'unexpected failure' }` if there's an error accessing the iframe's content window (most likely due to a cross-origin redirect that wasn't expected or handled).
Expand All @@ -63,7 +58,7 @@ This method creates a hidden iframe, initiates navigation, and waits for a redir
## Usage Example

```typescript
import iFrameManager from './path/to/iframe-manager.effects'; // Adjust path
import iFrameManager from '@forgerock/iframe-manager';

const iframeMgr = iFrameManager();

Expand All @@ -77,10 +72,15 @@ async function performSilentLogin(authUrl: string) {

try {
console.log('Attempting silent login via iframe...');
// The promise resolves/rejects when the iframe redirects back to *this* app's origin
// with appropriate query parameters.
// The promise resolves with all parsed params regardless of success/error.
const resultParams = await iframeMgr.getParamsByRedirect(options);

// Check if the server returned an error
if (resultParams.error || resultParams.error_description) {
console.error('Silent login failed. Server returned error:', resultParams);
return;
}

// Success case: 'code', 'id_token', or 'session_state' was present
console.log('Silent login successful. Received params:', resultParams);
// Process the received parameters (e.g., exchange code for token)
Expand Down Expand Up @@ -117,4 +117,4 @@ performSilentLogin(authorizationUrl);
1. **Timeout:** Choose a reasonable `timeout` value. If the external service is slow or the redirect chain is long, the operation might time out prematurely. Conversely, too long a timeout might delay feedback to the user if something goes wrong.
1. **Intermediate Redirects:** The code handles intermediate redirects (pages loaded within the iframe that don't contain success or error parameters) by simply waiting for the next `load` event. The process only completes upon detecting success/error parameters or timing out.
1. **Cleanup:** The utility ensures the iframe element is removed from the DOM and the timeout timer is cleared upon completion (resolve, reject, or timeout) to prevent memory leaks.
1. **Error Parameter Precedence:** Error parameters (`errorParams`) are checked before success parameters (`successParams`). If a redirect URL contains both an error parameter and a success parameter, the promise will be **rejected**.
1. **Error Parameter Precedence:** Error parameters (`errorParams`) are checked before success parameters (`successParams`). If a redirect URL contains both an error parameter and a success parameter, the promise will **resolve** with all parameters, and the caller must check for the error parameter keys to determine it's an error state.
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ export function iFrameManager() {
* Accessing contentWindow.location of a cross-origin iframe will fail.
*
* @param options - The options for the iframe request (URL, timeout, success/error params).
* @returns A Promise that resolves with the parsed query parameters on success,
* or rejects on error, timeout, or if unable to access iframe content.
* @returns A Promise that resolves with the parsed query parameters on success or
* when error params are present; rejects on timeout or if unable to access iframe content.
*/
return {
getParamsByRedirect: (options: GetParamsFromIFrameOptions): Promise<ResolvedParams> => {
Expand Down Expand Up @@ -116,7 +116,7 @@ export function iFrameManager() {
// 1. Check for Error Parameters
if (hasErrorParams(searchParams, errorParams)) {
cleanup();
resolve(parsedParams); // Reject with all parsed params for context
resolve(parsedParams); // Resolve with all parsed params for context
return;
}

Expand Down
Loading
Loading