Skip to content

Commit e644572

Browse files
authored
Merge pull request #525 from ForgeRock/well-known
feat(journey-client): wellknown-endpoint-config-support
2 parents 8dd47e2 + 35c4ad7 commit e644572

66 files changed

Lines changed: 3511 additions & 1415 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/rich-cows-try.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
'@forgerock/journey-client': minor
3+
'@forgerock/sdk-oidc': minor
4+
'@forgerock/sdk-utilities': minor
5+
'@forgerock/davinci-client': patch
6+
'@forgerock/oidc-client': patch
7+
---
8+
9+
### @forgerock/journey-client
10+
11+
Add well-known OIDC endpoint discovery support. The journey client can now fetch configuration from the `.well-known/openid-configuration` endpoint:
12+
13+
```typescript
14+
const client = await journey({
15+
serverConfig: {
16+
baseUrl: 'https://am.example.com/am/',
17+
wellknown:
18+
'https://am.example.com/am/oauth2/realms/root/realms/alpha/.well-known/openid-configuration',
19+
},
20+
});
21+
```
22+
23+
The realm path can be automatically inferred from the well-known issuer URL.
24+
25+
### @forgerock/sdk-oidc
26+
27+
Add shared well-known module with RTK Query API for OIDC endpoint discovery:
28+
29+
- `wellknownApi` - RTK Query API for fetching well-known configuration
30+
- `createWellknownSelector` - Selector factory for cached well-known data
31+
- `createWellknownError` - Typed error creation from fetch failures
32+
- Re-exports pure utilities from `@forgerock/sdk-utilities`
33+
34+
### @forgerock/sdk-utilities
35+
36+
Add pure well-known utilities:
37+
38+
- `inferRealmFromIssuer` - Extract realm path from AM issuer URLs
39+
- `isValidWellknownUrl` - Validate well-known URLs (HTTPS required, HTTP allowed for localhost)
40+
41+
### @forgerock/davinci-client
42+
43+
Refactored to use shared well-known module from `@forgerock/sdk-oidc`.
44+
45+
### @forgerock/oidc-client
46+
47+
Refactored to use shared well-known module from `@forgerock/sdk-oidc`.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
'@forgerock/journey-client': major
3+
---
4+
5+
BREAKING: Unify journey-client around wellknown-only configuration
6+
7+
This release simplifies the configuration API by requiring the `wellknown` URL and automatically inferring `baseUrl` and `realmPath`.
8+
9+
## Breaking Changes
10+
11+
- **Removed `baseUrl` from `JourneyServerConfig`**: The `baseUrl` is now always inferred from the wellknown URL. If inference fails (non-AM server), an error is returned.
12+
- **Removed `hasWellknownConfig` export**: This type guard is no longer needed since all configs use wellknown.
13+
14+
## Migration
15+
16+
**Before:**
17+
18+
```typescript
19+
journey({
20+
config: {
21+
serverConfig: { baseUrl: 'https://am.example.com/am/' },
22+
realmPath: 'alpha',
23+
},
24+
});
25+
```
26+
27+
**After:**
28+
29+
```typescript
30+
journey({
31+
config: {
32+
serverConfig: {
33+
wellknown: 'https://am.example.com/am/oauth2/alpha/.well-known/openid-configuration',
34+
},
35+
// realmPath is now optional - inferred from wellknown issuer
36+
},
37+
});
38+
```
39+
40+
## Features
41+
42+
- Automatic `baseUrl` inference from wellknown URL (extracts path before `/oauth2/`)
43+
- Automatic `realmPath` inference from wellknown issuer
44+
- Improved error messages for non-AM servers, guiding users to appropriate clients
45+
- Updated README with comprehensive API documentation

e2e/am-mock-api/package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
"author": "",
99
"main": "./index.js",
1010
"dependencies": {
11-
"@types/express": "^4.17.17",
12-
"body-parser": "^2.2.0",
11+
"body-parser": "^2.2.2",
1312
"cookie-parser": "^1.4.7",
1413
"cors": "^2.8.5",
15-
"express": "^4.21.2",
14+
"express": "^5.2.1",
1615
"superagent": "^10.2.3",
1716
"uuid": "^13.0.0"
17+
},
18+
"devDependencies": {
19+
"@types/express": "^5.0.0"
1820
}
1921
}

e2e/am-mock-api/src/app/routes.auth.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,11 @@ export default function (app) {
668668
res.send(wellKnownForgeRock);
669669
});
670670

671+
// Standard AM wellknown endpoint path (used by journey-client wellknown discovery)
672+
app.get('/am/oauth2/realms/root/.well-known/openid-configuration', (req, res) => {
673+
res.send(wellKnownForgeRock);
674+
});
675+
671676
app.get('/as/.well-known/new-oidc-configuration', (req, res) => {
672677
res.send(newPiWellKnown);
673678
});

e2e/am-mock-api/src/app/routes.resource.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ async function authorization(req, res, next) {
6363

6464
export default function (app) {
6565
// Passthrough route that enforces authentication
66-
app.all('/resource/*', async (req, res, next) => {
66+
app.all('/resource/{*splat}', async (req, res, next) => {
6767
if (env.NODE_ENV === 'LIVE' && req.hostname === FORGEOPS) {
6868
// Only enforce authentication if IG is not used
6969
// In other words, the call comes directly from app
@@ -156,7 +156,7 @@ export default function (app) {
156156
}
157157
});
158158

159-
app.get('/resource/rest/*', wait, authorization, async (req, res) => {
159+
app.get('/resource/rest/{*splat}', wait, authorization, async (req, res) => {
160160
if (env.NODE_ENV === 'live') {
161161
if (req.access.actions && req.access.actions.GET) {
162162
res.json({ message: 'Successfully retrieved resource!' });

e2e/davinci-app/package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
{
22
"name": "@forgerock/davinci-app",
33
"version": "0.0.0",
4+
"private": true,
45
"description": "Ping DaVinci Client Test App",
56
"type": "module",
6-
"private": true,
7-
"nx": {
8-
"tags": ["scope:e2e"]
9-
},
107
"scripts": {
118
"build": "pnpm nx nxBuild",
129
"lint": "pnpm nx nxLint",
@@ -16,8 +13,11 @@
1613
"dependencies": {
1714
"@forgerock/davinci-client": "workspace:*",
1815
"@forgerock/javascript-sdk": "4.7.0",
19-
"@forgerock/sdk-logger": "workspace:*",
20-
"@forgerock/protect": "workspace:*"
16+
"@forgerock/protect": "workspace:*",
17+
"@forgerock/sdk-logger": "workspace:*"
2118
},
22-
"devDependencies": {}
19+
"devDependencies": {},
20+
"nx": {
21+
"tags": ["scope:e2e"]
22+
}
2323
}

e2e/davinci-app/tsconfig.json

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,6 @@
1414
"skipLibCheck": true
1515
},
1616
"references": [
17-
{
18-
"path": "../../packages/sdk-effects/logger"
19-
},
20-
{
21-
"path": "../../packages/protect"
22-
},
23-
{
24-
"path": "../../packages/davinci-client"
25-
},
2617
{
2718
"path": "./tsconfig.app.json"
2819
},

e2e/device-client-app/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
"@forgerock/javascript-sdk": "4.7.0",
1414
"effect": "^3.12.7"
1515
},
16-
"nx": {
17-
"tags": ["scope:e2e"]
18-
},
1916
"devDependencies": {
2017
"@effect/language-service": "^0.20.0"
18+
},
19+
"nx": {
20+
"tags": ["scope:e2e"]
2121
}
2222
}

e2e/device-client-app/tsconfig.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
"files": [],
44
"include": [],
55
"references": [
6-
{
7-
"path": "../../packages/device-client"
8-
},
96
{
107
"path": "./tsconfig.app.json"
118
}

e2e/journey-app/main.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import './style.css';
88

99
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;
@@ -51,12 +51,19 @@ if (searchParams.get('middleware') === 'true') {
5151
}
5252

5353
(async () => {
54-
const journeyClient = await journey({ config: config, requestMiddleware });
55-
5654
const errorEl = document.getElementById('error') as HTMLDivElement;
5755
const formEl = document.getElementById('form') as HTMLFormElement;
5856
const journeyEl = document.getElementById('journey') as HTMLDivElement;
5957

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;
65+
return;
66+
}
6067
let step = await journeyClient.start({ journey: journeyName });
6168

6269
function renderComplete() {

0 commit comments

Comments
 (0)