Skip to content

Commit 35c4ad7

Browse files
committed
feat(oidc): refactor-wellknown-architecture
refactor well known after discussion SDKS-4665
1 parent 9a8ca14 commit 35c4ad7

25 files changed

Lines changed: 1112 additions & 1062 deletions

e2e/journey-app/main.ts

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
*/
77
import './style.css';
88

9-
import { journey, isJourneyClient } from '@forgerock/journey-client';
9+
import { journey } from '@forgerock/journey-client';
1010

11-
import type { RequestMiddleware } from '@forgerock/journey-client/types';
11+
import type { JourneyClient, RequestMiddleware } from '@forgerock/journey-client/types';
1212

1313
import { renderCallbacks } from './callback-map.js';
1414
import { renderQRCodeStep } from './components/qr-code.js';
@@ -40,7 +40,7 @@ if (searchParams.get('middleware') === 'true') {
4040
},
4141
(req, action, next) => {
4242
switch (action.type) {
43-
case 'END_SESSION':
43+
case 'JOURNEY_TERMINATE':
4444
req.url.searchParams.set('end-session-middleware', 'end-session');
4545
req.headers.append('x-end-session-middleware', 'end-session');
4646
break;
@@ -55,19 +55,15 @@ if (searchParams.get('middleware') === 'true') {
5555
const formEl = document.getElementById('form') as HTMLFormElement;
5656
const journeyEl = document.getElementById('journey') as HTMLDivElement;
5757

58-
const journeyClientResult = await journey({ config: config, requestMiddleware });
59-
if (!isJourneyClient(journeyClientResult)) {
60-
console.error('Failed to initialize journey client:', journeyClientResult.message);
61-
errorEl.textContent = journeyClientResult.message ?? 'Unknown error';
58+
let journeyClient: JourneyClient;
59+
try {
60+
journeyClient = await journey({ config: config, requestMiddleware });
61+
} catch (error) {
62+
const message = error instanceof Error ? error.message : 'Unknown error';
63+
console.error('Failed to initialize journey client:', message);
64+
errorEl.textContent = message;
6265
return;
6366
}
64-
/**
65-
* Re-assign to a new const after type narrowing.
66-
* TypeScript's type narrowing doesn't persist into closures (event handlers, callbacks)
67-
* because it can't prove the variable wasn't reassigned between the guard and closure execution.
68-
* Creating a new const binding after the guard preserves the narrowed type for nested functions.
69-
*/
70-
const journeyClient = journeyClientResult;
7167
let step = await journeyClient.start({ journey: journeyName });
7268

7369
function renderComplete() {

e2e/journey-app/server-configs.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,19 @@ import type { JourneyClientConfig } from '@forgerock/journey-client/types';
99
/**
1010
* Server configurations for E2E tests.
1111
*
12-
* Both baseUrl and realmPath are automatically inferred from the wellknown URL:
13-
* - baseUrl: extracted from the path before `/oauth2/`
14-
* - realmPath: extracted from the issuer URL in the wellknown response
12+
* All configuration (baseUrl, authenticate/sessions paths) is automatically
13+
* derived from the well-known response via `convertWellknown()`.
1514
*/
1615
export const serverConfigs: Record<string, JourneyClientConfig> = {
1716
basic: {
1817
serverConfig: {
1918
wellknown: 'http://localhost:9443/am/oauth2/realms/root/.well-known/openid-configuration',
20-
// baseUrl inferred: http://localhost:9443/am/
21-
// realmPath inferred from issuer: 'root'
2219
},
2320
},
2421
tenant: {
2522
serverConfig: {
2623
wellknown:
2724
'https://openam-sdks.forgeblocks.com/am/oauth2/realms/root/realms/alpha/.well-known/openid-configuration',
28-
// baseUrl inferred: https://openam-sdks.forgeblocks.com/am/
29-
// realmPath inferred from issuer: 'alpha'
3025
},
3126
},
3227
};

packages/davinci-client/src/lib/client.store.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@
99
*/
1010
import { CustomLogger, logger as loggerFn, LogLevel } from '@forgerock/sdk-logger';
1111
import { createStorage } from '@forgerock/storage';
12-
import { isGenericError } from '@forgerock/sdk-utilities';
12+
import { isGenericError, createWellknownError } from '@forgerock/sdk-utilities';
1313

1414
import { createClientStore, handleUpdateValidateError, RootState } from './client.store.utils.js';
1515
import { nodeSlice } from './node.slice.js';
1616
import { davinciApi } from './davinci.api.js';
1717
import { configSlice } from './config.slice.js';
1818
import { wellknownApi } from './wellknown.api.js';
19-
import { createWellknownError } from './wellknown.utils.js';
2019

2120
import type { ActionTypes, RequestMiddleware } from '@forgerock/sdk-request-middleware';
2221
/**

packages/davinci-client/src/lib/wellknown.api.ts

Lines changed: 20 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -7,83 +7,37 @@
77

88
import { createSelector } from '@reduxjs/toolkit';
99
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query';
10-
import { fetchWellknownConfiguration } from '@forgerock/sdk-oidc';
10+
import { initWellknownQuery } from '@forgerock/sdk-oidc';
1111

12-
import type { WellknownResponse, GenericError } from '@forgerock/sdk-types';
13-
import type { BaseQueryFn, FetchBaseQueryError } from '@reduxjs/toolkit/query';
14-
15-
function isObject(value: unknown): value is Record<string, unknown> {
16-
return typeof value === 'object' && value !== null;
17-
}
18-
19-
/**
20-
* Converts FetchBaseQueryError to GenericError.
21-
*/
22-
function toGenericError(error: FetchBaseQueryError): GenericError {
23-
const status = error.status;
24-
let message = `HTTP error ${String(status)}`;
25-
26-
if ('error' in error) {
27-
message = error.error;
28-
} else if ('data' in error && isObject(error.data)) {
29-
if (typeof error.data['message'] === 'string') message = error.data['message'];
30-
else if (typeof error.data['error'] === 'string') message = error.data['error'];
31-
else if (typeof error.data['error_description'] === 'string')
32-
message = error.data['error_description'];
33-
else message = JSON.stringify(error.data);
34-
}
35-
36-
return {
37-
error: 'Well-known configuration fetch failed',
38-
message,
39-
type: 'wellknown_error',
40-
status,
41-
};
42-
}
43-
44-
/**
45-
* Configured fetchBaseQuery that sets `Accept: application/json` headers.
46-
*/
47-
const innerBaseQuery = fetchBaseQuery({
48-
prepareHeaders: (headers) => {
49-
headers.set('Accept', 'application/json');
50-
return headers;
51-
},
52-
});
53-
54-
/**
55-
* BaseQuery wrapper that normalizes FetchBaseQueryError to GenericError.
56-
*
57-
* This allows the wellknownApi to use GenericError as its error type
58-
* while the actual HTTP transport goes through RTK Query's pipeline.
59-
*/
60-
const wellknownBaseQuery: BaseQueryFn<string, unknown, GenericError> = async (
61-
args,
62-
api,
63-
extraOptions,
64-
) => {
65-
const result = await innerBaseQuery(args, api, extraOptions);
66-
if (result.error) {
67-
return { ...result, error: toGenericError(result.error) };
68-
}
69-
return result;
70-
};
12+
import type { WellknownResponse } from '@forgerock/sdk-types';
13+
import type {
14+
FetchBaseQueryError,
15+
FetchBaseQueryMeta,
16+
QueryReturnValue,
17+
} from '@reduxjs/toolkit/query';
7118

7219
/**
7320
* RTK Query API for well-known endpoint discovery.
7421
*
75-
* Uses `queryFn` to pass the baseQuery into the framework-agnostic
76-
* `fetchWellknownConfiguration` effect, which handles response validation.
77-
* The baseQuery handles HTTP transport.
22+
* Uses the `initWellknownQuery` builder pattern from `@forgerock/sdk-oidc`.
23+
* The builder constructs the request and validates the response;
24+
* `fetchBaseQuery` handles the HTTP transport through RTK Query's pipeline.
7825
*/
7926
export const wellknownApi = createApi({
8027
reducerPath: 'wellknown',
81-
baseQuery: wellknownBaseQuery,
28+
baseQuery: fetchBaseQuery(),
8229
endpoints: (builder) => ({
8330
configuration: builder.query<WellknownResponse, string>({
8431
queryFn: async (url, _api, _extra, baseQuery) => {
85-
const result = await fetchWellknownConfiguration(url, baseQuery);
86-
return result.success ? { data: result.data } : { error: result.error };
32+
const result = await initWellknownQuery(url).applyQuery(async (req) => {
33+
const queryResult = await baseQuery(req);
34+
return queryResult as QueryReturnValue<unknown, FetchBaseQueryError, FetchBaseQueryMeta>;
35+
});
36+
return result as QueryReturnValue<
37+
WellknownResponse,
38+
FetchBaseQueryError,
39+
FetchBaseQueryMeta
40+
>;
8741
},
8842
}),
8943
}),

packages/davinci-client/src/lib/wellknown.utils.ts

Lines changed: 0 additions & 71 deletions
This file was deleted.

0 commit comments

Comments
 (0)