@flink-app/oidc-plugin 2.0.0-alpha.114 → 2.0.0-alpha.116
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/CHANGELOG.md +37 -0
- package/dist/handlers/CallbackOidc.d.ts.map +1 -1
- package/dist/handlers/CallbackOidc.js +2 -1
- package/dist/providers/OidcProvider.d.ts +1 -0
- package/dist/providers/OidcProvider.d.ts.map +1 -1
- package/dist/providers/OidcProvider.js +8 -0
- package/dist/schemas/CallbackRequest.d.ts +6 -0
- package/dist/schemas/CallbackRequest.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/handlers/CallbackOidc.ts +2 -1
- package/src/providers/OidcProvider.ts +9 -1
- package/src/schemas/CallbackRequest.ts +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# @flink-app/oidc-plugin
|
|
2
2
|
|
|
3
|
+
## 2.0.0-alpha.116
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [7760b9f]
|
|
8
|
+
- @flink-app/flink@2.0.0-alpha.116
|
|
9
|
+
- @flink-app/jwt-auth-plugin@2.0.0-alpha.116
|
|
10
|
+
|
|
11
|
+
## 2.0.0-alpha.115
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- 12fe6f5: fix: forward the RFC 9207 `iss` authorization response parameter to openid-client
|
|
16
|
+
|
|
17
|
+
IdPs that advertise `authorization_response_iss_parameter_supported` in their
|
|
18
|
+
discovery document (Google does) return an `iss` parameter on the authorization
|
|
19
|
+
redirect. The callback handler destructured only `code`, `state`, `error`,
|
|
20
|
+
`error_description` and `response_type` from the query, and `exchangeCodeForToken`
|
|
21
|
+
rebuilt the params object as `{ code, state }` — so `iss` was dropped before it
|
|
22
|
+
reached `client.callback()`.
|
|
23
|
+
|
|
24
|
+
openid-client then rejected every callback from such an IdP with
|
|
25
|
+
`RPError: iss missing from the response`, failing the login client-side before the
|
|
26
|
+
token endpoint was ever contacted. This made Google unusable as an OIDC provider.
|
|
27
|
+
|
|
28
|
+
`iss` is now read from the callback query and threaded through to openid-client,
|
|
29
|
+
which validates it against the discovered issuer (the mix-up attack defence RFC 9207
|
|
30
|
+
is there to provide).
|
|
31
|
+
|
|
32
|
+
The parameter is spread conditionally rather than always set, because openid-client
|
|
33
|
+
keys its check on `'iss' in params` — an explicit `iss: undefined` is read as a
|
|
34
|
+
mismatch and would break IdPs that do not send the parameter at all (Entra ID,
|
|
35
|
+
Okta, Auth0 and Keycloak do not advertise it by default).
|
|
36
|
+
|
|
37
|
+
- @flink-app/flink@2.0.0-alpha.115
|
|
38
|
+
- @flink-app/jwt-auth-plugin@2.0.0-alpha.115
|
|
39
|
+
|
|
3
40
|
## 2.0.0-alpha.114
|
|
4
41
|
|
|
5
42
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CallbackOidc.d.ts","sourceRoot":"","sources":["../../src/handlers/CallbackOidc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAc,UAAU,EAAwC,MAAM,kBAAkB,CAAC;AAC5G,OAAO,eAAe,MAAM,4BAA4B,CAAC;AAOzD;;GAEG;AACH,UAAU,UAAU;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,UAGnB,CAAC;AAEF;;;;;;GAMG;AACH,QAAA,MAAM,YAAY,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"CallbackOidc.d.ts","sourceRoot":"","sources":["../../src/handlers/CallbackOidc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAc,UAAU,EAAwC,MAAM,kBAAkB,CAAC;AAC5G,OAAO,eAAe,MAAM,4BAA4B,CAAC;AAOzD;;GAEG;AACH,UAAU,UAAU;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,UAGnB,CAAC;AAEF;;;;;;GAMG;AACH,QAAA,MAAM,YAAY,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,eAAe,CA6QnE,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
|
@@ -38,7 +38,7 @@ exports.Route = {
|
|
|
38
38
|
*/
|
|
39
39
|
const CallbackOidc = async ({ ctx, req }) => {
|
|
40
40
|
const { provider } = req.params;
|
|
41
|
-
const { code, state, error: oidcError, error_description, response_type } = req.query;
|
|
41
|
+
const { code, state, iss, error: oidcError, error_description, response_type } = req.query;
|
|
42
42
|
// Track session outside try/catch so redirectUri and metadata are available in error handling
|
|
43
43
|
let sessionRedirectUri;
|
|
44
44
|
let sessionMetadata = {};
|
|
@@ -119,6 +119,7 @@ const CallbackOidc = async ({ ctx, req }) => {
|
|
|
119
119
|
codeVerifier: session.codeVerifier,
|
|
120
120
|
state: session.state,
|
|
121
121
|
nonce: session.nonce,
|
|
122
|
+
iss,
|
|
122
123
|
});
|
|
123
124
|
log_1.oidcLog.debug(`Callback: token exchange successful`, `sub="${tokenSet.claims.sub}"`, `iss="${tokenSet.claims.iss}"`, `email="${tokenSet.claims.email || "(none)"}"`, `hasRefreshToken=${!!tokenSet.refreshToken}`, `expiresIn=${tokenSet.expiresIn ?? "(none)"}s`);
|
|
124
125
|
// Build user profile from ID token and (optionally) UserInfo
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OidcProvider.d.ts","sourceRoot":"","sources":["../../src/providers/OidcProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwD,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACvG,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,WAAW,MAAM,wBAAwB,CAAC;AACjD,OAAO,YAAY,MAAM,yBAAyB,CAAC;AAKnD;;;;;;;;;;GAUG;AACH,qBAAa,YAAY;IACrB,SAAgB,MAAM,EAAE,kBAAkB,CAAC;IAC3C,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,WAAW,CAAkB;gBAEzB,MAAM,EAAE,kBAAkB;IAItC;;;;;;;OAOG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAsEjC;;;;;OAKG;IACG,mBAAmB,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAiB1G;;;;;;;OAOG;IACG,oBAAoB,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"OidcProvider.d.ts","sourceRoot":"","sources":["../../src/providers/OidcProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwD,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACvG,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,WAAW,MAAM,wBAAwB,CAAC;AACjD,OAAO,YAAY,MAAM,yBAAyB,CAAC;AAKnD;;;;;;;;;;GAUG;AACH,qBAAa,YAAY;IACrB,SAAgB,MAAM,EAAE,kBAAkB,CAAC;IAC3C,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,WAAW,CAAkB;gBAEzB,MAAM,EAAE,kBAAkB;IAItC;;;;;;;OAOG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAsEjC;;;;;OAKG;IACG,mBAAmB,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAiB1G;;;;;;;OAOG;IACG,oBAAoB,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,YAAY,CAAC;IA4D7I;;;;;;;;OAQG;IACG,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAsBjE;;;;;;;;;OASG;IACG,YAAY,CAAC,QAAQ,EAAE,YAAY,EAAE,eAAe,GAAE,OAAc,GAAG,OAAO,CAAC,WAAW,CAAC;IAgCjG;;;;OAIG;YACW,iBAAiB;IAU/B;;;;OAIG;IACH,iBAAiB,IAAI,GAAG;CAM3B"}
|
|
@@ -116,6 +116,14 @@ class OidcProvider {
|
|
|
116
116
|
const tokenSet = await this.client.callback(this.config.callbackUrl, {
|
|
117
117
|
code: params.code,
|
|
118
118
|
state: params.state,
|
|
119
|
+
// RFC 9207: IdPs that advertise
|
|
120
|
+
// `authorization_response_iss_parameter_supported` (Google does)
|
|
121
|
+
// return `iss` on the authorization redirect, and openid-client
|
|
122
|
+
// rejects the callback with "iss missing from the response" unless
|
|
123
|
+
// it is forwarded here. Spread conditionally — openid-client keys
|
|
124
|
+
// its check on `'iss' in params`, so an explicit `iss: undefined`
|
|
125
|
+
// would be read as a mismatch and break IdPs that never send it.
|
|
126
|
+
...(params.iss ? { iss: params.iss } : {}),
|
|
119
127
|
}, {
|
|
120
128
|
code_verifier: params.codeVerifier,
|
|
121
129
|
state: params.state,
|
|
@@ -14,6 +14,12 @@ type CallbackRequest = Record<string, string | string[]> & {
|
|
|
14
14
|
* Must match the state stored in the session
|
|
15
15
|
*/
|
|
16
16
|
state?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Issuer identifier returned on the authorization response (RFC 9207)
|
|
19
|
+
* Sent by IdPs advertising `authorization_response_iss_parameter_supported`
|
|
20
|
+
* Forwarded to openid-client, which validates it against the discovered issuer
|
|
21
|
+
*/
|
|
22
|
+
iss?: string;
|
|
17
23
|
/**
|
|
18
24
|
* Error code from the IdP (if authorization failed)
|
|
19
25
|
* e.g., "access_denied" if user cancelled
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CallbackRequest.d.ts","sourceRoot":"","sources":["../../src/schemas/CallbackRequest.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG;IACvD;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CAE1B,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"CallbackRequest.d.ts","sourceRoot":"","sources":["../../src/schemas/CallbackRequest.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG;IACvD;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CAE1B,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flink-app/oidc-plugin",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.116",
|
|
4
4
|
"description": "Flink plugin for OIDC authentication with generic IdP support",
|
|
5
5
|
"author": "joel@frost.se",
|
|
6
6
|
"license": "MIT",
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"openid-client": "^5.7.0",
|
|
14
|
-
"@flink-app/jwt-auth-plugin": "2.0.0-alpha.
|
|
14
|
+
"@flink-app/jwt-auth-plugin": "2.0.0-alpha.116"
|
|
15
15
|
},
|
|
16
16
|
"peerDependencies": {
|
|
17
|
-
"@flink-app/flink": ">=2.0.0-alpha.
|
|
17
|
+
"@flink-app/flink": ">=2.0.0-alpha.116",
|
|
18
18
|
"mongodb": "^6.15.0"
|
|
19
19
|
},
|
|
20
20
|
"peerDependenciesMeta": {
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"@types/node": "22.13.10",
|
|
28
28
|
"ts-node": "^10.9.2",
|
|
29
29
|
"tsc-watch": "^4.2.9",
|
|
30
|
-
"@flink-app/
|
|
31
|
-
"@flink-app/
|
|
32
|
-
"@flink-app/test-utils": "2.0.0-alpha.
|
|
30
|
+
"@flink-app/jwt-auth-plugin": "2.0.0-alpha.116",
|
|
31
|
+
"@flink-app/flink": "2.0.0-alpha.116",
|
|
32
|
+
"@flink-app/test-utils": "2.0.0-alpha.116"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"test": "jasmine-ts --config=./spec/support/jasmine.json",
|
|
@@ -47,7 +47,7 @@ export const Route: RouteProps = {
|
|
|
47
47
|
*/
|
|
48
48
|
const CallbackOidc: GetHandler<any, any, PathParams, CallbackRequest> = async ({ ctx, req }) => {
|
|
49
49
|
const { provider } = req.params;
|
|
50
|
-
const { code, state, error: oidcError, error_description, response_type } = req.query;
|
|
50
|
+
const { code, state, iss, error: oidcError, error_description, response_type } = req.query;
|
|
51
51
|
|
|
52
52
|
// Track session outside try/catch so redirectUri and metadata are available in error handling
|
|
53
53
|
let sessionRedirectUri: string | undefined;
|
|
@@ -147,6 +147,7 @@ const CallbackOidc: GetHandler<any, any, PathParams, CallbackRequest> = async ({
|
|
|
147
147
|
codeVerifier: session.codeVerifier,
|
|
148
148
|
state: session.state,
|
|
149
149
|
nonce: session.nonce,
|
|
150
|
+
iss,
|
|
150
151
|
});
|
|
151
152
|
|
|
152
153
|
oidcLog.debug(
|
|
@@ -136,7 +136,7 @@ export class OidcProvider {
|
|
|
136
136
|
* @param params - Token exchange parameters
|
|
137
137
|
* @returns Token set with access token, ID token, and claims
|
|
138
138
|
*/
|
|
139
|
-
async exchangeCodeForToken(params: { code: string; codeVerifier: string; state: string; nonce: string }): Promise<OidcTokenSet> {
|
|
139
|
+
async exchangeCodeForToken(params: { code: string; codeVerifier: string; state: string; nonce: string; iss?: string }): Promise<OidcTokenSet> {
|
|
140
140
|
await this.ensureInitialized();
|
|
141
141
|
|
|
142
142
|
try {
|
|
@@ -145,6 +145,14 @@ export class OidcProvider {
|
|
|
145
145
|
{
|
|
146
146
|
code: params.code,
|
|
147
147
|
state: params.state,
|
|
148
|
+
// RFC 9207: IdPs that advertise
|
|
149
|
+
// `authorization_response_iss_parameter_supported` (Google does)
|
|
150
|
+
// return `iss` on the authorization redirect, and openid-client
|
|
151
|
+
// rejects the callback with "iss missing from the response" unless
|
|
152
|
+
// it is forwarded here. Spread conditionally — openid-client keys
|
|
153
|
+
// its check on `'iss' in params`, so an explicit `iss: undefined`
|
|
154
|
+
// would be read as a mismatch and break IdPs that never send it.
|
|
155
|
+
...(params.iss ? { iss: params.iss } : {}),
|
|
148
156
|
},
|
|
149
157
|
{
|
|
150
158
|
code_verifier: params.codeVerifier,
|
|
@@ -16,6 +16,13 @@ type CallbackRequest = Record<string, string | string[]> & {
|
|
|
16
16
|
*/
|
|
17
17
|
state?: string;
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Issuer identifier returned on the authorization response (RFC 9207)
|
|
21
|
+
* Sent by IdPs advertising `authorization_response_iss_parameter_supported`
|
|
22
|
+
* Forwarded to openid-client, which validates it against the discovered issuer
|
|
23
|
+
*/
|
|
24
|
+
iss?: string;
|
|
25
|
+
|
|
19
26
|
/**
|
|
20
27
|
* Error code from the IdP (if authorization failed)
|
|
21
28
|
* e.g., "access_denied" if user cancelled
|