@flink-app/oidc-plugin 2.0.0-alpha.81 → 2.0.0-alpha.82
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 +8 -0
- package/dist/OidcPluginOptions.d.ts +16 -1
- package/dist/OidcPluginOptions.d.ts.map +1 -1
- package/dist/handlers/CallbackOidc.d.ts.map +1 -1
- package/dist/handlers/CallbackOidc.js +9 -2
- package/dist/handlers/InitiateOidc.d.ts.map +1 -1
- package/dist/handlers/InitiateOidc.js +8 -0
- package/dist/schemas/OidcSession.d.ts +5 -0
- package/dist/schemas/OidcSession.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/OidcPluginOptions.ts +17 -1
- package/src/handlers/CallbackOidc.ts +9 -2
- package/src/handlers/InitiateOidc.ts +9 -0
- package/src/schemas/OidcSession.ts +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @flink-app/oidc-plugin
|
|
2
2
|
|
|
3
|
+
## 2.0.0-alpha.82
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Add metadata passthrough support. Any query parameters prefixed with `meta.` on the initiate endpoint (e.g. `?meta.intent=admin&meta.source=mobile`) are collected, stripped of the prefix, stored in the session, and delivered to both `onAuthSuccess` and `onAuthError` callbacks as `metadata: Record<string, string>`. Empty object when no meta params are provided.
|
|
8
|
+
- @flink-app/flink@2.0.0-alpha.82
|
|
9
|
+
- @flink-app/jwt-auth-plugin@2.0.0-alpha.82
|
|
10
|
+
|
|
3
11
|
## 2.0.0-alpha.81
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
|
@@ -110,7 +110,7 @@ export interface OidcPluginOptions<TCtx = any> {
|
|
|
110
110
|
*
|
|
111
111
|
* Example:
|
|
112
112
|
* ```typescript
|
|
113
|
-
* onAuthSuccess: async ({ profile, claims, provider }, ctx) => {
|
|
113
|
+
* onAuthSuccess: async ({ profile, claims, provider, metadata }, ctx) => {
|
|
114
114
|
* // Find user by OIDC subject + issuer
|
|
115
115
|
* let user = await ctx.repos.userRepo.getOne({
|
|
116
116
|
* 'oidcConnections.subject': claims.sub,
|
|
@@ -168,6 +168,16 @@ export interface OidcPluginOptions<TCtx = any> {
|
|
|
168
168
|
* ```
|
|
169
169
|
*/
|
|
170
170
|
redirectUri: string;
|
|
171
|
+
/**
|
|
172
|
+
* Arbitrary metadata collected from meta.* query parameters on the initiate request.
|
|
173
|
+
* Opaque to the plugin — it only transports these values.
|
|
174
|
+
*
|
|
175
|
+
* Example: GET /oidc/acme/initiate?meta.intent=admin&meta.source=mobile
|
|
176
|
+
* Produces: { intent: "admin", source: "mobile" }
|
|
177
|
+
*
|
|
178
|
+
* Empty object ({}) if no meta.* params were provided.
|
|
179
|
+
*/
|
|
180
|
+
metadata: Record<string, string>;
|
|
171
181
|
/**
|
|
172
182
|
* OIDC tokens (only if storeTokens: true)
|
|
173
183
|
* Includes accessToken, idToken, refreshToken
|
|
@@ -210,6 +220,11 @@ export interface OidcPluginOptions<TCtx = any> {
|
|
|
210
220
|
* (e.g., IdP returns an error before we can retrieve the session)
|
|
211
221
|
*/
|
|
212
222
|
redirectUri?: string;
|
|
223
|
+
/**
|
|
224
|
+
* Metadata from the initiate request (meta.* query params, prefix stripped).
|
|
225
|
+
* Empty object ({}) if no meta.* params were provided or session was not found.
|
|
226
|
+
*/
|
|
227
|
+
metadata: Record<string, string>;
|
|
213
228
|
}) => Promise<AuthErrorCallbackResponse>;
|
|
214
229
|
/**
|
|
215
230
|
* Dynamic provider loader callback
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OidcPluginOptions.d.ts","sourceRoot":"","sources":["../src/OidcPluginOptions.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,uBAAuB,CAAC;AAChD,OAAO,YAAY,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,SAAS;IACtB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,OAAO,CAAC,EAAE,GAAG,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,2BAA2B;IACxC;;;OAGG;IACH,IAAI,EAAE,GAAG,CAAC;IAEV;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACtC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB,CAAC,IAAI,GAAG,GAAG;IACzC;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAE/C;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgDG;IACH,aAAa,EAAE,CACX,MAAM,EAAE;QACJ;;WAEG;QACH,OAAO,EAAE,WAAW,CAAC;QAErB;;;WAGG;QACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAE5B;;WAEG;QACH,QAAQ,EAAE,MAAM,CAAC;QAEjB;;;;;;;;WAQG;QACH,WAAW,EAAE,MAAM,CAAC;QAEpB;;;WAGG;QACH,MAAM,CAAC,EAAE,YAAY,CAAC;KACzB,EACD,GAAG,EAAE,IAAI,KACR,OAAO,CAAC,2BAA2B,CAAC,CAAC;IAE1C;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE;QACnB,KAAK,EAAE,SAAS,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB;;;;WAIG;QACH,WAAW,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"OidcPluginOptions.d.ts","sourceRoot":"","sources":["../src/OidcPluginOptions.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,uBAAuB,CAAC;AAChD,OAAO,YAAY,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,SAAS;IACtB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,OAAO,CAAC,EAAE,GAAG,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,2BAA2B;IACxC;;;OAGG;IACH,IAAI,EAAE,GAAG,CAAC;IAEV;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACtC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB,CAAC,IAAI,GAAG,GAAG;IACzC;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAE/C;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgDG;IACH,aAAa,EAAE,CACX,MAAM,EAAE;QACJ;;WAEG;QACH,OAAO,EAAE,WAAW,CAAC;QAErB;;;WAGG;QACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAE5B;;WAEG;QACH,QAAQ,EAAE,MAAM,CAAC;QAEjB;;;;;;;;WAQG;QACH,WAAW,EAAE,MAAM,CAAC;QAEpB;;;;;;;;WAQG;QACH,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAEjC;;;WAGG;QACH,MAAM,CAAC,EAAE,YAAY,CAAC;KACzB,EACD,GAAG,EAAE,IAAI,KACR,OAAO,CAAC,2BAA2B,CAAC,CAAC;IAE1C;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE;QACnB,KAAK,EAAE,SAAS,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB;;;;WAIG;QACH,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB;;;WAGG;QACH,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACpC,KAAK,OAAO,CAAC,yBAAyB,CAAC,CAAC;IAEzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,cAAc,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;IAEzF;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC;;;OAGG;IACH,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAEnC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B"}
|
|
@@ -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,CAqQnE,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
|
@@ -39,8 +39,9 @@ exports.Route = {
|
|
|
39
39
|
const CallbackOidc = async ({ ctx, req }) => {
|
|
40
40
|
const { provider } = req.params;
|
|
41
41
|
const { code, state, error: oidcError, error_description, response_type } = req.query;
|
|
42
|
-
// Track session outside try/catch so redirectUri
|
|
42
|
+
// Track session outside try/catch so redirectUri and metadata are available in error handling
|
|
43
43
|
let sessionRedirectUri;
|
|
44
|
+
let sessionMetadata = {};
|
|
44
45
|
try {
|
|
45
46
|
// Validate provider and response_type
|
|
46
47
|
(0, error_utils_1.validateProvider)(provider);
|
|
@@ -54,6 +55,7 @@ const CallbackOidc = async ({ ctx, req }) => {
|
|
|
54
55
|
if (state) {
|
|
55
56
|
const errorSession = await ctx.repos.oidcSessionRepo.getByState(state);
|
|
56
57
|
sessionRedirectUri = errorSession?.redirectUri;
|
|
58
|
+
sessionMetadata = errorSession?.metadata ?? {};
|
|
57
59
|
if (errorSession) {
|
|
58
60
|
await ctx.repos.oidcSessionRepo.deleteBySessionId(errorSession.sessionId);
|
|
59
61
|
}
|
|
@@ -65,6 +67,7 @@ const CallbackOidc = async ({ ctx, req }) => {
|
|
|
65
67
|
error,
|
|
66
68
|
provider,
|
|
67
69
|
redirectUri: sessionRedirectUri,
|
|
70
|
+
metadata: sessionMetadata,
|
|
68
71
|
});
|
|
69
72
|
if (errorResult.redirectUrl) {
|
|
70
73
|
return {
|
|
@@ -96,8 +99,9 @@ const CallbackOidc = async ({ ctx, req }) => {
|
|
|
96
99
|
providedState: state.substring(0, 10) + "...",
|
|
97
100
|
});
|
|
98
101
|
}
|
|
99
|
-
// Track redirectUri for error handling
|
|
102
|
+
// Track redirectUri and metadata for error handling
|
|
100
103
|
sessionRedirectUri = session.redirectUri;
|
|
104
|
+
sessionMetadata = session.metadata ?? {};
|
|
101
105
|
// Delete session immediately after validation (one-time use)
|
|
102
106
|
await ctx.repos.oidcSessionRepo.deleteBySessionId(session.sessionId);
|
|
103
107
|
log_1.oidcLog.debug(`Callback: state validated, session deleted (one-time use)`);
|
|
@@ -129,6 +133,7 @@ const CallbackOidc = async ({ ctx, req }) => {
|
|
|
129
133
|
claims: tokenSet.claims,
|
|
130
134
|
provider,
|
|
131
135
|
redirectUri: session.redirectUri,
|
|
136
|
+
metadata: sessionMetadata,
|
|
132
137
|
...(options.storeTokens ? { tokens: tokenSet } : {}),
|
|
133
138
|
};
|
|
134
139
|
let authResult;
|
|
@@ -147,6 +152,7 @@ const CallbackOidc = async ({ ctx, req }) => {
|
|
|
147
152
|
error: oidcError,
|
|
148
153
|
provider,
|
|
149
154
|
redirectUri: session.redirectUri,
|
|
155
|
+
metadata: sessionMetadata,
|
|
150
156
|
});
|
|
151
157
|
if (errorResult.redirectUrl) {
|
|
152
158
|
return {
|
|
@@ -228,6 +234,7 @@ const CallbackOidc = async ({ ctx, req }) => {
|
|
|
228
234
|
error,
|
|
229
235
|
provider,
|
|
230
236
|
redirectUri: sessionRedirectUri,
|
|
237
|
+
metadata: sessionMetadata,
|
|
231
238
|
});
|
|
232
239
|
if (errorResult.redirectUrl) {
|
|
233
240
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InitiateOidc.d.ts","sourceRoot":"","sources":["../../src/handlers/InitiateOidc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,UAAU,EAAc,UAAU,EAAmC,MAAM,kBAAkB,CAAC;AACvG,OAAO,eAAe,MAAM,4BAA4B,CAAC;AAMzD;;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;;;;;GAKG;AACH,QAAA,MAAM,YAAY,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"InitiateOidc.d.ts","sourceRoot":"","sources":["../../src/handlers/InitiateOidc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,UAAU,EAAc,UAAU,EAAmC,MAAM,kBAAkB,CAAC;AACvG,OAAO,eAAe,MAAM,4BAA4B,CAAC;AAMzD;;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;;;;;GAKG;AACH,QAAA,MAAM,YAAY,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,eAAe,CAkFnE,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
|
@@ -51,6 +51,13 @@ const InitiateOidc = async ({ ctx, req }) => {
|
|
|
51
51
|
// Determine redirect URI (use provided or default to callback URL)
|
|
52
52
|
const staticProviderConfig = options.providers?.[provider];
|
|
53
53
|
const finalRedirectUri = redirectUri || staticProviderConfig?.callbackUrl || `${req.protocol}://${req.get("host")}/oidc/${provider}/callback`;
|
|
54
|
+
// Collect meta.* query params as opaque metadata (prefix stripped)
|
|
55
|
+
const metadata = {};
|
|
56
|
+
for (const [key, value] of Object.entries(req.query)) {
|
|
57
|
+
if (key.startsWith("meta.") && typeof value === "string") {
|
|
58
|
+
metadata[key.slice(5)] = value;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
54
61
|
log_1.oidcLog.debug(`Initiate: resolved redirectUri="${finalRedirectUri}" (source: ${redirectUri ? "query param" : staticProviderConfig?.callbackUrl ? "provider config" : "auto-detected"})`);
|
|
55
62
|
// Generate cryptographically secure parameters
|
|
56
63
|
const state = (0, state_utils_1.generateState)();
|
|
@@ -65,6 +72,7 @@ const InitiateOidc = async ({ ctx, req }) => {
|
|
|
65
72
|
nonce,
|
|
66
73
|
provider,
|
|
67
74
|
redirectUri: finalRedirectUri,
|
|
75
|
+
metadata,
|
|
68
76
|
createdAt: new Date(),
|
|
69
77
|
});
|
|
70
78
|
log_1.oidcLog.debug(`Initiate: session created sessionId=${sessionId}`);
|
|
@@ -37,6 +37,11 @@ export default interface OidcSession {
|
|
|
37
37
|
* Can be overridden by the client via query parameter
|
|
38
38
|
*/
|
|
39
39
|
redirectUri: string;
|
|
40
|
+
/**
|
|
41
|
+
* Arbitrary metadata collected from meta.* query parameters on initiate
|
|
42
|
+
* Passed through to onAuthSuccess and onAuthError callbacks unchanged
|
|
43
|
+
*/
|
|
44
|
+
metadata?: Record<string, string>;
|
|
40
45
|
/**
|
|
41
46
|
* Session creation timestamp
|
|
42
47
|
* MongoDB TTL index will automatically delete expired sessions
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OidcSession.d.ts","sourceRoot":"","sources":["../../src/schemas/OidcSession.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,OAAO,WAAW,WAAW;IAChC;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,SAAS,EAAE,IAAI,CAAC;CACnB"}
|
|
1
|
+
{"version":3,"file":"OidcSession.d.ts","sourceRoot":"","sources":["../../src/schemas/OidcSession.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,OAAO,WAAW,WAAW;IAChC;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAElC;;;OAGG;IACH,SAAS,EAAE,IAAI,CAAC;CACnB"}
|
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.82",
|
|
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.82"
|
|
15
15
|
},
|
|
16
16
|
"peerDependencies": {
|
|
17
|
-
"@flink-app/flink": ">=2.0.0-alpha.
|
|
17
|
+
"@flink-app/flink": ">=2.0.0-alpha.82",
|
|
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/flink": "2.0.0-alpha.
|
|
31
|
-
"@flink-app/jwt-auth-plugin": "2.0.0-alpha.
|
|
32
|
-
"@flink-app/test-utils": "2.0.0-alpha.
|
|
30
|
+
"@flink-app/flink": "2.0.0-alpha.82",
|
|
31
|
+
"@flink-app/jwt-auth-plugin": "2.0.0-alpha.82",
|
|
32
|
+
"@flink-app/test-utils": "2.0.0-alpha.82"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"test": "jasmine-ts --config=./spec/support/jasmine.json",
|
package/src/OidcPluginOptions.ts
CHANGED
|
@@ -120,7 +120,7 @@ export interface OidcPluginOptions<TCtx = any> {
|
|
|
120
120
|
*
|
|
121
121
|
* Example:
|
|
122
122
|
* ```typescript
|
|
123
|
-
* onAuthSuccess: async ({ profile, claims, provider }, ctx) => {
|
|
123
|
+
* onAuthSuccess: async ({ profile, claims, provider, metadata }, ctx) => {
|
|
124
124
|
* // Find user by OIDC subject + issuer
|
|
125
125
|
* let user = await ctx.repos.userRepo.getOne({
|
|
126
126
|
* 'oidcConnections.subject': claims.sub,
|
|
@@ -183,6 +183,17 @@ export interface OidcPluginOptions<TCtx = any> {
|
|
|
183
183
|
*/
|
|
184
184
|
redirectUri: string;
|
|
185
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Arbitrary metadata collected from meta.* query parameters on the initiate request.
|
|
188
|
+
* Opaque to the plugin — it only transports these values.
|
|
189
|
+
*
|
|
190
|
+
* Example: GET /oidc/acme/initiate?meta.intent=admin&meta.source=mobile
|
|
191
|
+
* Produces: { intent: "admin", source: "mobile" }
|
|
192
|
+
*
|
|
193
|
+
* Empty object ({}) if no meta.* params were provided.
|
|
194
|
+
*/
|
|
195
|
+
metadata: Record<string, string>;
|
|
196
|
+
|
|
186
197
|
/**
|
|
187
198
|
* OIDC tokens (only if storeTokens: true)
|
|
188
199
|
* Includes accessToken, idToken, refreshToken
|
|
@@ -228,6 +239,11 @@ export interface OidcPluginOptions<TCtx = any> {
|
|
|
228
239
|
* (e.g., IdP returns an error before we can retrieve the session)
|
|
229
240
|
*/
|
|
230
241
|
redirectUri?: string;
|
|
242
|
+
/**
|
|
243
|
+
* Metadata from the initiate request (meta.* query params, prefix stripped).
|
|
244
|
+
* Empty object ({}) if no meta.* params were provided or session was not found.
|
|
245
|
+
*/
|
|
246
|
+
metadata: Record<string, string>;
|
|
231
247
|
}) => Promise<AuthErrorCallbackResponse>;
|
|
232
248
|
|
|
233
249
|
/**
|
|
@@ -49,8 +49,9 @@ const CallbackOidc: GetHandler<any, any, PathParams, CallbackRequest> = async ({
|
|
|
49
49
|
const { provider } = req.params;
|
|
50
50
|
const { code, state, error: oidcError, error_description, response_type } = req.query;
|
|
51
51
|
|
|
52
|
-
// Track session outside try/catch so redirectUri
|
|
52
|
+
// Track session outside try/catch so redirectUri and metadata are available in error handling
|
|
53
53
|
let sessionRedirectUri: string | undefined;
|
|
54
|
+
let sessionMetadata: Record<string, string> = {};
|
|
54
55
|
|
|
55
56
|
try {
|
|
56
57
|
// Validate provider and response_type
|
|
@@ -68,6 +69,7 @@ const CallbackOidc: GetHandler<any, any, PathParams, CallbackRequest> = async ({
|
|
|
68
69
|
if (state) {
|
|
69
70
|
const errorSession = await ctx.repos.oidcSessionRepo.getByState(state);
|
|
70
71
|
sessionRedirectUri = errorSession?.redirectUri;
|
|
72
|
+
sessionMetadata = errorSession?.metadata ?? {};
|
|
71
73
|
if (errorSession) {
|
|
72
74
|
await ctx.repos.oidcSessionRepo.deleteBySessionId(errorSession.sessionId);
|
|
73
75
|
}
|
|
@@ -80,6 +82,7 @@ const CallbackOidc: GetHandler<any, any, PathParams, CallbackRequest> = async ({
|
|
|
80
82
|
error,
|
|
81
83
|
provider,
|
|
82
84
|
redirectUri: sessionRedirectUri,
|
|
85
|
+
metadata: sessionMetadata,
|
|
83
86
|
});
|
|
84
87
|
|
|
85
88
|
if (errorResult.redirectUrl) {
|
|
@@ -119,8 +122,9 @@ const CallbackOidc: GetHandler<any, any, PathParams, CallbackRequest> = async ({
|
|
|
119
122
|
});
|
|
120
123
|
}
|
|
121
124
|
|
|
122
|
-
// Track redirectUri for error handling
|
|
125
|
+
// Track redirectUri and metadata for error handling
|
|
123
126
|
sessionRedirectUri = session.redirectUri;
|
|
127
|
+
sessionMetadata = session.metadata ?? {};
|
|
124
128
|
|
|
125
129
|
// Delete session immediately after validation (one-time use)
|
|
126
130
|
await ctx.repos.oidcSessionRepo.deleteBySessionId(session.sessionId);
|
|
@@ -167,6 +171,7 @@ const CallbackOidc: GetHandler<any, any, PathParams, CallbackRequest> = async ({
|
|
|
167
171
|
claims: tokenSet.claims,
|
|
168
172
|
provider,
|
|
169
173
|
redirectUri: session.redirectUri,
|
|
174
|
+
metadata: sessionMetadata,
|
|
170
175
|
...(options.storeTokens ? { tokens: tokenSet } : {}),
|
|
171
176
|
};
|
|
172
177
|
|
|
@@ -187,6 +192,7 @@ const CallbackOidc: GetHandler<any, any, PathParams, CallbackRequest> = async ({
|
|
|
187
192
|
error: oidcError,
|
|
188
193
|
provider,
|
|
189
194
|
redirectUri: session.redirectUri,
|
|
195
|
+
metadata: sessionMetadata,
|
|
190
196
|
});
|
|
191
197
|
|
|
192
198
|
if (errorResult.redirectUrl) {
|
|
@@ -278,6 +284,7 @@ const CallbackOidc: GetHandler<any, any, PathParams, CallbackRequest> = async ({
|
|
|
278
284
|
error,
|
|
279
285
|
provider,
|
|
280
286
|
redirectUri: sessionRedirectUri,
|
|
287
|
+
metadata: sessionMetadata,
|
|
281
288
|
});
|
|
282
289
|
|
|
283
290
|
if (errorResult.redirectUrl) {
|
|
@@ -67,6 +67,14 @@ const InitiateOidc: GetHandler<any, any, PathParams, InitiateRequest> = async ({
|
|
|
67
67
|
const staticProviderConfig = options.providers?.[provider];
|
|
68
68
|
const finalRedirectUri = redirectUri || staticProviderConfig?.callbackUrl || `${req.protocol}://${req.get("host")}/oidc/${provider}/callback`;
|
|
69
69
|
|
|
70
|
+
// Collect meta.* query params as opaque metadata (prefix stripped)
|
|
71
|
+
const metadata: Record<string, string> = {};
|
|
72
|
+
for (const [key, value] of Object.entries(req.query)) {
|
|
73
|
+
if (key.startsWith("meta.") && typeof value === "string") {
|
|
74
|
+
metadata[key.slice(5)] = value;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
70
78
|
oidcLog.debug(`Initiate: resolved redirectUri="${finalRedirectUri}" (source: ${redirectUri ? "query param" : staticProviderConfig?.callbackUrl ? "provider config" : "auto-detected"})`);
|
|
71
79
|
|
|
72
80
|
// Generate cryptographically secure parameters
|
|
@@ -83,6 +91,7 @@ const InitiateOidc: GetHandler<any, any, PathParams, InitiateRequest> = async ({
|
|
|
83
91
|
nonce,
|
|
84
92
|
provider,
|
|
85
93
|
redirectUri: finalRedirectUri,
|
|
94
|
+
metadata,
|
|
86
95
|
createdAt: new Date(),
|
|
87
96
|
});
|
|
88
97
|
|
|
@@ -44,6 +44,12 @@ export default interface OidcSession {
|
|
|
44
44
|
*/
|
|
45
45
|
redirectUri: string;
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Arbitrary metadata collected from meta.* query parameters on initiate
|
|
49
|
+
* Passed through to onAuthSuccess and onAuthError callbacks unchanged
|
|
50
|
+
*/
|
|
51
|
+
metadata?: Record<string, string>;
|
|
52
|
+
|
|
47
53
|
/**
|
|
48
54
|
* Session creation timestamp
|
|
49
55
|
* MongoDB TTL index will automatically delete expired sessions
|