44
55> ** Note** : This client is designed specifically for ForgeRock AM servers. For PingOne DaVinci flows, use ` @forgerock/davinci-client ` . For standard OIDC operations, use ` @forgerock/oidc-client ` .
66
7+ ## Table of Contents
8+
9+ - [ Features] ( #features )
10+ - [ Installation] ( #installation )
11+ - [ Quick Start] ( #quick-start )
12+ - [ Configuration] ( #configuration )
13+ - [ API Reference] ( #api-reference )
14+ - [ Working with Callbacks] ( #working-with-callbacks )
15+ - [ Request Middleware] ( #request-middleware )
16+ - [ Error Handling] ( #error-handling )
17+ - [ Building] ( #building )
18+ - [ Testing] ( #testing )
19+
720## Features
821
922- ** Wellknown Discovery** : Automatically discovers server configuration from the OIDC wellknown endpoint
23+ - ** Automatic Path Derivation** : Derives ` baseUrl ` , ` authenticate ` , and ` sessions ` paths directly from the well-known response
1024- ** Stateful Client** : Manages authentication journey state internally
11- - ** Error-as-Value Pattern** : Returns errors as values instead of throwing, enabling type-safe error handling
1225- ** Callback Handling** : Provides a structured way to interact with various authentication callbacks
1326- ** Redux Toolkit & RTK Query** : Built on modern state management for predictable state and efficient API interactions
1427
@@ -25,115 +38,98 @@ yarn add @forgerock/journey-client
2538## Quick Start
2639
2740``` typescript
28- import { journey , isJourneyClient } from ' @forgerock/journey-client' ;
41+ import { journey } from ' @forgerock/journey-client' ;
2942import { callbackType } from ' @forgerock/sdk-types' ;
3043
3144async function authenticateUser() {
3245 // Initialize the client with wellknown discovery
33- const result = await journey ({
34- config: {
35- serverConfig: {
36- wellknown: ' https://am.example.com/am/oauth2/alpha/.well-known/openid-configuration' ,
46+ try {
47+ const client = await journey ({
48+ config: {
49+ serverConfig: {
50+ wellknown: ' https://am.example.com/am/oauth2/alpha/.well-known/openid-configuration' ,
51+ },
3752 },
38- // realmPath is optional - inferred from wellknown issuer
39- },
40- });
41-
42- // Handle initialization errors using the type guard
43- if (! isJourneyClient (result )) {
44- console .error (' Failed to initialize:' , result .message );
45- return ;
46- }
53+ });
4754
48- const client = result ;
55+ // Start the authentication journey
56+ let step = await client .start ({ journey: ' Login' });
4957
50- // Start the authentication journey
51- let step = await client .start ({ journey: ' Login' });
58+ // Handle callbacks in a loop until success or failure
59+ while (step ?.type === ' Step' ) {
60+ const nameCallbacks = step .getCallbacksOfType (callbackType .NameCallback );
61+ for (const cb of nameCallbacks ) {
62+ cb .setName (' demo' );
63+ }
5264
53- // Handle callbacks in a loop until success or failure
54- while (step ?.type === ' Step' ) {
55- // Handle NameCallback
56- const nameCallbacks = step .getCallbacksOfType (callbackType .NameCallback );
57- for (const cb of nameCallbacks ) {
58- cb .setName (' demo' );
59- }
65+ const passwordCallbacks = step .getCallbacksOfType (callbackType .PasswordCallback );
66+ for (const cb of passwordCallbacks ) {
67+ cb .setPassword (' password' );
68+ }
6069
61- // Handle PasswordCallback
62- const passwordCallbacks = step .getCallbacksOfType (callbackType .PasswordCallback );
63- for (const cb of passwordCallbacks ) {
64- cb .setPassword (' password' );
70+ step = await client .next (step );
6571 }
6672
67- // Submit and get next step
68- step = await client .next (step );
69- }
70-
71- // Check the final result
72- if (step ?.type === ' LoginSuccess' ) {
73- console .log (' Login successful!' , step .getSessionToken ());
74- } else if (step ?.type === ' LoginFailure' ) {
75- console .error (' Login failed:' , step .payload .message );
73+ // Check the final result
74+ if (step ?.type === ' LoginSuccess' ) {
75+ console .log (' Login successful!' , step .getSessionToken ());
76+ } else if (step ?.type === ' LoginFailure' ) {
77+ console .error (' Login failed:' , step .payload .message );
78+ }
79+ } catch (error ) {
80+ console .error (' Failed to initialize client:' , error );
7681 }
7782}
7883```
7984
8085## Configuration
8186
82- The client uses OIDC wellknown discovery to automatically configure itself :
87+ The client requires only the OIDC wellknown endpoint URL. All other configuration is derived automatically from the well-known response :
8388
8489``` typescript
8590import type { JourneyClientConfig } from ' @forgerock/journey-client/types' ;
8691
8792const config: JourneyClientConfig = {
8893 serverConfig: {
89- // Required: OIDC discovery endpoint
9094 wellknown: ' https://am.example.com/am/oauth2/alpha/.well-known/openid-configuration' ,
91- // Optional: Custom path overrides
92- paths: {
93- authenticate: ' /custom/authenticate' ,
94- },
95- // Optional: Request timeout in milliseconds
96- timeout: 30000 ,
9795 },
98- // Optional: Realm path (inferred from wellknown issuer if not provided)
99- realmPath: ' alpha' ,
10096};
10197```
10298
103- ### Automatic Inference
99+ ### Automatic Derivation
104100
105- The client automatically infers configuration from the wellknown URL :
101+ The client automatically derives all needed configuration from the well-known response :
106102
107- | Property | Inferred From |
108- | ----------- | ----------------------------------------------------- |
109- | ` baseUrl ` | Extracted from wellknown URL path (before ` /oauth2/ ` ) |
110- | ` realmPath ` | Extracted from the ` issuer ` in the wellknown response |
103+ | Property | Derived From |
104+ | -------------- | -------------------------------------------------------------------- |
105+ | ` baseUrl ` | ` authorization_endpoint ` origin |
106+ | ` authenticate ` | Issuer path with ` /oauth2 ` replaced by ` /json ` , plus ` /authenticate ` |
107+ | ` sessions ` | Issuer path with ` /oauth2 ` replaced by ` /json ` , plus ` /sessions/ ` |
111108
112109## API Reference
113110
114111### ` journey(options) `
115112
116- Factory function that creates a journey client instance.
113+ Factory function that creates a journey client instance. Throws on initialization failure (invalid URL, fetch error, non-AM server).
117114
118115``` typescript
119- const result = await journey ({
116+ const client = await journey ({
120117 config: JourneyClientConfig ,
121118 requestMiddleware?: RequestMiddleware [],
122119 logger?: { level: LogLevel ; custom ?: CustomLogger },
123120});
124121```
125122
126- ** Returns** : ` Promise<JourneyClient | GenericError > `
123+ ** Returns** : ` Promise<JourneyClient> `
127124
128- Use the ` isJourneyClient() ` type guard to narrow the result:
125+ ** Throws ** : ` Error ` if the wellknown URL is invalid, the fetch fails, or the server is not a ForgeRock AM instance.
129126
130127``` typescript
131- if ( ! isJourneyClient ( result )) {
132- // result is GenericError
133- console . error ( result . error , result . message );
134- return ;
128+ try {
129+ const client = await journey ({ config });
130+ } catch ( error ) {
131+ console . error ( ' Initialization failed: ' , error . message ) ;
135132}
136- // result is JourneyClient
137133```
138134
139135### Client Methods
@@ -223,37 +219,31 @@ const loggingMiddleware: RequestMiddleware = (req, action, next) => {
223219 next ();
224220};
225221
226- const result = await journey ({
222+ const client = await journey ({
227223 config ,
228224 requestMiddleware: [loggingMiddleware ],
229225});
230226```
231227
232228### Middleware Actions
233229
234- | Action Type | Description |
235- | --------------- | ----------------------- |
236- | ` JOURNEY_START ` | Starting a new journey |
237- | ` JOURNEY_NEXT ` | Submitting a step |
238- | ` END_SESSION ` | Terminating the session |
230+ | Action Type | Description |
231+ | ------------------- | ----------------------- |
232+ | ` JOURNEY_START ` | Starting a new journey |
233+ | ` JOURNEY_NEXT ` | Submitting a step |
234+ | ` JOURNEY_TERMINATE ` | Terminating the session |
239235
240236## Error Handling
241237
242- The client uses an error-as-value pattern instead of throwing exceptions :
238+ The ` journey() ` factory throws on initialization failure. Use try/catch :
243239
244240``` typescript
245- const result = await journey ({ config });
246-
247- if (! isJourneyClient (result )) {
248- // Handle initialization error
249- switch (result .type ) {
250- case ' wellknown_error' :
251- console .error (' Configuration error:' , result .message );
252- break ;
253- default :
254- console .error (' Unknown error:' , result .error );
255- }
256- return ;
241+ try {
242+ const client = await journey ({ config });
243+ // client is guaranteed to be a JourneyClient
244+ } catch (error ) {
245+ // Handle initialization errors (invalid URL, fetch failure, non-AM server)
246+ console .error (' Failed to initialize:' , error .message );
257247}
258248```
259249
0 commit comments