@flink-app/oidc-plugin 2.0.0-alpha.78 → 2.0.0-alpha.79

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # @flink-app/oidc-plugin
2
2
 
3
+ ## 2.0.0-alpha.79
4
+
5
+ ### Patch Changes
6
+
7
+ - Expose redirectUri from session in onAuthSuccess and onAuthError callbacks, enabling multi-client redirect decisions
8
+ - @flink-app/flink@2.0.0-alpha.79
9
+ - @flink-app/jwt-auth-plugin@2.0.0-alpha.79
10
+
3
11
  ## 2.0.0-alpha.78
4
12
 
5
13
  ### Patch Changes
@@ -158,6 +158,16 @@ export interface OidcPluginOptions<TCtx = any> {
158
158
  * Provider name (e.g., "acme", "contoso")
159
159
  */
160
160
  provider: string;
161
+ /**
162
+ * The redirectUri passed on the initiate request (stored in session)
163
+ * Use this to redirect back to the originating client application.
164
+ *
165
+ * Example:
166
+ * ```typescript
167
+ * return { user, token, redirectUrl: redirectUri || DEFAULT_REDIRECT_URL };
168
+ * ```
169
+ */
170
+ redirectUri: string;
161
171
  /**
162
172
  * OIDC tokens (only if storeTokens: true)
163
173
  * Includes accessToken, idToken, refreshToken
@@ -194,6 +204,12 @@ export interface OidcPluginOptions<TCtx = any> {
194
204
  onAuthError?: (params: {
195
205
  error: OidcError;
196
206
  provider: string;
207
+ /**
208
+ * The redirectUri from the initiate request (if available)
209
+ * May be undefined for errors that occur before session lookup
210
+ * (e.g., IdP returns an error before we can retrieve the session)
211
+ */
212
+ redirectUri?: string;
197
213
  }) => Promise<AuthErrorCallbackResponse>;
198
214
  /**
199
215
  * 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;;;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;QAAE,KAAK,EAAE,SAAS,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,yBAAyB,CAAC,CAAC;IAErG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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
+ {"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;KACxB,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;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;;;;;;GAMG;AACH,QAAA,MAAM,YAAY,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,eAAe,CAgNnE,CAAC;AAEF,eAAe,YAAY,CAAC"}
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;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;;;;;;GAMG;AACH,QAAA,MAAM,YAAY,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,eAAe,CAmOnE,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -38,6 +38,8 @@ exports.Route = {
38
38
  const CallbackOidc = async ({ ctx, req }) => {
39
39
  const { provider } = req.params;
40
40
  const { code, state, error: oidcError, error_description, response_type } = req.query;
41
+ // Track session outside try/catch so redirectUri is available in error handling
42
+ let sessionRedirectUri;
41
43
  try {
42
44
  // Validate provider and response_type
43
45
  (0, error_utils_1.validateProvider)(provider);
@@ -45,12 +47,21 @@ const CallbackOidc = async ({ ctx, req }) => {
45
47
  // Check for OIDC provider errors (e.g., user denied access)
46
48
  if (oidcError) {
47
49
  const error = (0, error_utils_1.handleProviderError)({ error: oidcError, error_description });
50
+ // Try to retrieve session redirectUri (state may be available even on IdP errors)
51
+ if (state) {
52
+ const errorSession = await ctx.repos.oidcSessionRepo.getByState(state);
53
+ sessionRedirectUri = errorSession?.redirectUri;
54
+ if (errorSession) {
55
+ await ctx.repos.oidcSessionRepo.deleteBySessionId(errorSession.sessionId);
56
+ }
57
+ }
48
58
  // Call onAuthError callback if provided
49
59
  const { options } = ctx.plugins.oidc;
50
60
  if (options.onAuthError) {
51
61
  const errorResult = await options.onAuthError({
52
62
  error,
53
63
  provider,
64
+ redirectUri: sessionRedirectUri,
54
65
  });
55
66
  if (errorResult.redirectUrl) {
56
67
  return {
@@ -80,6 +91,8 @@ const CallbackOidc = async ({ ctx, req }) => {
80
91
  providedState: state.substring(0, 10) + "...",
81
92
  });
82
93
  }
94
+ // Track redirectUri for error handling
95
+ sessionRedirectUri = session.redirectUri;
83
96
  // Delete session immediately after validation (one-time use)
84
97
  await ctx.repos.oidcSessionRepo.deleteBySessionId(session.sessionId);
85
98
  // Get plugin options
@@ -104,6 +117,7 @@ const CallbackOidc = async ({ ctx, req }) => {
104
117
  profile,
105
118
  claims: tokenSet.claims,
106
119
  provider,
120
+ redirectUri: session.redirectUri,
107
121
  ...(options.storeTokens ? { tokens: tokenSet } : {}),
108
122
  };
109
123
  let authResult;
@@ -121,6 +135,7 @@ const CallbackOidc = async ({ ctx, req }) => {
121
135
  const errorResult = await options.onAuthError({
122
136
  error: oidcError,
123
137
  provider,
138
+ redirectUri: session.redirectUri,
124
139
  });
125
140
  if (errorResult.redirectUrl) {
126
141
  return {
@@ -196,6 +211,7 @@ const CallbackOidc = async ({ ctx, req }) => {
196
211
  const errorResult = await options.onAuthError({
197
212
  error,
198
213
  provider,
214
+ redirectUri: sessionRedirectUri,
199
215
  });
200
216
  if (errorResult.redirectUrl) {
201
217
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flink-app/oidc-plugin",
3
- "version": "2.0.0-alpha.78",
3
+ "version": "2.0.0-alpha.79",
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.78"
14
+ "@flink-app/jwt-auth-plugin": "2.0.0-alpha.79"
15
15
  },
16
16
  "peerDependencies": {
17
- "@flink-app/flink": ">=2.0.0-alpha.78",
17
+ "@flink-app/flink": ">=2.0.0-alpha.79",
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/jwt-auth-plugin": "2.0.0-alpha.78",
31
- "@flink-app/test-utils": "2.0.0-alpha.78",
32
- "@flink-app/flink": "2.0.0-alpha.78"
30
+ "@flink-app/jwt-auth-plugin": "2.0.0-alpha.79",
31
+ "@flink-app/test-utils": "2.0.0-alpha.79",
32
+ "@flink-app/flink": "2.0.0-alpha.79"
33
33
  },
34
34
  "scripts": {
35
35
  "test": "jasmine-ts --config=./spec/support/jasmine.json",
@@ -172,6 +172,17 @@ export interface OidcPluginOptions<TCtx = any> {
172
172
  */
173
173
  provider: string;
174
174
 
175
+ /**
176
+ * The redirectUri passed on the initiate request (stored in session)
177
+ * Use this to redirect back to the originating client application.
178
+ *
179
+ * Example:
180
+ * ```typescript
181
+ * return { user, token, redirectUrl: redirectUri || DEFAULT_REDIRECT_URL };
182
+ * ```
183
+ */
184
+ redirectUri: string;
185
+
175
186
  /**
176
187
  * OIDC tokens (only if storeTokens: true)
177
188
  * Includes accessToken, idToken, refreshToken
@@ -208,7 +219,16 @@ export interface OidcPluginOptions<TCtx = any> {
208
219
  * }
209
220
  * ```
210
221
  */
211
- onAuthError?: (params: { error: OidcError; provider: string }) => Promise<AuthErrorCallbackResponse>;
222
+ onAuthError?: (params: {
223
+ error: OidcError;
224
+ provider: string;
225
+ /**
226
+ * The redirectUri from the initiate request (if available)
227
+ * May be undefined for errors that occur before session lookup
228
+ * (e.g., IdP returns an error before we can retrieve the session)
229
+ */
230
+ redirectUri?: string;
231
+ }) => Promise<AuthErrorCallbackResponse>;
212
232
 
213
233
  /**
214
234
  * Dynamic provider loader callback
@@ -48,6 +48,9 @@ const CallbackOidc: GetHandler<any, any, PathParams, CallbackRequest> = async ({
48
48
  const { provider } = req.params;
49
49
  const { code, state, error: oidcError, error_description, response_type } = req.query;
50
50
 
51
+ // Track session outside try/catch so redirectUri is available in error handling
52
+ let sessionRedirectUri: string | undefined;
53
+
51
54
  try {
52
55
  // Validate provider and response_type
53
56
  validateProvider(provider);
@@ -57,12 +60,22 @@ const CallbackOidc: GetHandler<any, any, PathParams, CallbackRequest> = async ({
57
60
  if (oidcError) {
58
61
  const error = handleProviderError({ error: oidcError, error_description });
59
62
 
63
+ // Try to retrieve session redirectUri (state may be available even on IdP errors)
64
+ if (state) {
65
+ const errorSession = await ctx.repos.oidcSessionRepo.getByState(state);
66
+ sessionRedirectUri = errorSession?.redirectUri;
67
+ if (errorSession) {
68
+ await ctx.repos.oidcSessionRepo.deleteBySessionId(errorSession.sessionId);
69
+ }
70
+ }
71
+
60
72
  // Call onAuthError callback if provided
61
73
  const { options } = ctx.plugins.oidc;
62
74
  if (options.onAuthError) {
63
75
  const errorResult = await options.onAuthError({
64
76
  error,
65
77
  provider,
78
+ redirectUri: sessionRedirectUri,
66
79
  });
67
80
 
68
81
  if (errorResult.redirectUrl) {
@@ -99,6 +112,9 @@ const CallbackOidc: GetHandler<any, any, PathParams, CallbackRequest> = async ({
99
112
  });
100
113
  }
101
114
 
115
+ // Track redirectUri for error handling
116
+ sessionRedirectUri = session.redirectUri;
117
+
102
118
  // Delete session immediately after validation (one-time use)
103
119
  await ctx.repos.oidcSessionRepo.deleteBySessionId(session.sessionId);
104
120
 
@@ -129,6 +145,7 @@ const CallbackOidc: GetHandler<any, any, PathParams, CallbackRequest> = async ({
129
145
  profile,
130
146
  claims: tokenSet.claims,
131
147
  provider,
148
+ redirectUri: session.redirectUri,
132
149
  ...(options.storeTokens ? { tokens: tokenSet } : {}),
133
150
  };
134
151
 
@@ -148,6 +165,7 @@ const CallbackOidc: GetHandler<any, any, PathParams, CallbackRequest> = async ({
148
165
  const errorResult = await options.onAuthError({
149
166
  error: oidcError,
150
167
  provider,
168
+ redirectUri: session.redirectUri,
151
169
  });
152
170
 
153
171
  if (errorResult.redirectUrl) {
@@ -231,6 +249,7 @@ const CallbackOidc: GetHandler<any, any, PathParams, CallbackRequest> = async ({
231
249
  const errorResult = await options.onAuthError({
232
250
  error,
233
251
  provider,
252
+ redirectUri: sessionRedirectUri,
234
253
  });
235
254
 
236
255
  if (errorResult.redirectUrl) {