actor-gate 0.1.0

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.
Files changed (155) hide show
  1. package/package.json +25 -0
  2. package/src/config/base-config.d.ts +17 -0
  3. package/src/config/base-config.js +33 -0
  4. package/src/config/index.d.ts +5 -0
  5. package/src/config/index.js +5 -0
  6. package/src/config/nextjs-public-config.d.ts +46 -0
  7. package/src/config/nextjs-public-config.js +89 -0
  8. package/src/config/nextjs-server-config.d.ts +32 -0
  9. package/src/config/nextjs-server-config.js +10 -0
  10. package/src/config/react-client.d.ts +23 -0
  11. package/src/config/react-client.js +69 -0
  12. package/src/config/react-config.d.ts +18 -0
  13. package/src/config/react-config.js +38 -0
  14. package/src/core/adapters/access-token-revocation-adapter.d.ts +8 -0
  15. package/src/core/adapters/access-token-revocation-adapter.js +1 -0
  16. package/src/core/adapters/access-token-transport-adapter.d.ts +15 -0
  17. package/src/core/adapters/access-token-transport-adapter.js +1 -0
  18. package/src/core/adapters/authorization-code-adapter.d.ts +21 -0
  19. package/src/core/adapters/authorization-code-adapter.js +1 -0
  20. package/src/core/adapters/authorization-hooks.d.ts +13 -0
  21. package/src/core/adapters/authorization-hooks.js +1 -0
  22. package/src/core/adapters/index.d.ts +14 -0
  23. package/src/core/adapters/index.js +1 -0
  24. package/src/core/adapters/login-method-adapter.d.ts +7 -0
  25. package/src/core/adapters/login-method-adapter.js +1 -0
  26. package/src/core/adapters/oauth-client-adapter.d.ts +13 -0
  27. package/src/core/adapters/oauth-client-adapter.js +1 -0
  28. package/src/core/adapters/oauth-client-management-adapter.d.ts +23 -0
  29. package/src/core/adapters/oauth-client-management-adapter.js +1 -0
  30. package/src/core/adapters/oauth-grant-type.d.ts +1 -0
  31. package/src/core/adapters/oauth-grant-type.js +1 -0
  32. package/src/core/adapters/oauth-policy.d.ts +9 -0
  33. package/src/core/adapters/oauth-policy.js +1 -0
  34. package/src/core/adapters/observability-hooks.d.ts +31 -0
  35. package/src/core/adapters/observability-hooks.js +1 -0
  36. package/src/core/adapters/pending-auth-request-adapter.d.ts +18 -0
  37. package/src/core/adapters/pending-auth-request-adapter.js +1 -0
  38. package/src/core/adapters/refresh-token-adapter.d.ts +24 -0
  39. package/src/core/adapters/refresh-token-adapter.js +1 -0
  40. package/src/core/adapters/session-adapter.d.ts +14 -0
  41. package/src/core/adapters/session-adapter.js +1 -0
  42. package/src/core/adapters/token-adapter.d.ts +15 -0
  43. package/src/core/adapters/token-adapter.js +1 -0
  44. package/src/core/http/bearer-challenge.d.ts +6 -0
  45. package/src/core/http/bearer-challenge.js +16 -0
  46. package/src/core/ids/id-codec.d.ts +6 -0
  47. package/src/core/ids/id-codec.js +30 -0
  48. package/src/core/index.d.ts +9 -0
  49. package/src/core/index.js +7 -0
  50. package/src/core/oauth/pkce.d.ts +9 -0
  51. package/src/core/oauth/pkce.js +30 -0
  52. package/src/core/services/access-token-service.d.ts +42 -0
  53. package/src/core/services/access-token-service.js +304 -0
  54. package/src/core/services/auth-error.d.ts +14 -0
  55. package/src/core/services/auth-error.js +47 -0
  56. package/src/core/services/contracts.d.ts +23 -0
  57. package/src/core/services/contracts.js +1 -0
  58. package/src/core/services/direct-auth-service.d.ts +50 -0
  59. package/src/core/services/direct-auth-service.js +267 -0
  60. package/src/core/services/index.d.ts +7 -0
  61. package/src/core/services/index.js +5 -0
  62. package/src/core/services/mcp-auth-service.d.ts +39 -0
  63. package/src/core/services/mcp-auth-service.js +170 -0
  64. package/src/core/services/oauth-service.d.ts +91 -0
  65. package/src/core/services/oauth-service.js +571 -0
  66. package/src/core/services/observability.d.ts +22 -0
  67. package/src/core/services/observability.js +71 -0
  68. package/src/core/services/revocation-policy.d.ts +21 -0
  69. package/src/core/services/revocation-policy.js +51 -0
  70. package/src/core/sessions/client-session.d.ts +7 -0
  71. package/src/core/sessions/client-session.js +18 -0
  72. package/src/core/tokens/access-claims.d.ts +21 -0
  73. package/src/core/tokens/access-claims.js +128 -0
  74. package/src/core/tokens/id-claims.d.ts +20 -0
  75. package/src/core/tokens/id-claims.js +25 -0
  76. package/src/core/types/auth-contract.d.ts +33 -0
  77. package/src/core/types/auth-contract.js +1 -0
  78. package/src/express/index.d.ts +1 -0
  79. package/src/express/index.js +1 -0
  80. package/src/express/protected-route.d.ts +44 -0
  81. package/src/express/protected-route.js +119 -0
  82. package/src/index.d.ts +8 -0
  83. package/src/index.js +8 -0
  84. package/src/mcp/index.d.ts +1 -0
  85. package/src/mcp/index.js +1 -0
  86. package/src/mcp/json-rpc-auth.d.ts +5 -0
  87. package/src/mcp/json-rpc-auth.js +41 -0
  88. package/src/next/app/catch-all.d.ts +32 -0
  89. package/src/next/app/catch-all.js +82 -0
  90. package/src/next/app/cookies.d.ts +22 -0
  91. package/src/next/app/cookies.js +36 -0
  92. package/src/next/app/direct-auth-handlers.d.ts +55 -0
  93. package/src/next/app/direct-auth-handlers.js +419 -0
  94. package/src/next/app/index.d.ts +8 -0
  95. package/src/next/app/index.js +8 -0
  96. package/src/next/app/mcp-oauth-handlers.d.ts +74 -0
  97. package/src/next/app/mcp-oauth-handlers.js +365 -0
  98. package/src/next/app/protected-route.d.ts +27 -0
  99. package/src/next/app/protected-route.js +59 -0
  100. package/src/next/app/request.d.ts +12 -0
  101. package/src/next/app/request.js +30 -0
  102. package/src/next/app/response.d.ts +16 -0
  103. package/src/next/app/response.js +48 -0
  104. package/src/next/app/wrapper.d.ts +28 -0
  105. package/src/next/app/wrapper.js +78 -0
  106. package/src/next/index.d.ts +6 -0
  107. package/src/next/index.js +5 -0
  108. package/src/next/pages/catch-all.d.ts +19 -0
  109. package/src/next/pages/catch-all.js +60 -0
  110. package/src/next/pages/cookies.d.ts +41 -0
  111. package/src/next/pages/cookies.js +87 -0
  112. package/src/next/pages/direct-auth-handlers.d.ts +58 -0
  113. package/src/next/pages/direct-auth-handlers.js +425 -0
  114. package/src/next/pages/index.d.ts +8 -0
  115. package/src/next/pages/index.js +8 -0
  116. package/src/next/pages/mcp-oauth-handlers.d.ts +77 -0
  117. package/src/next/pages/mcp-oauth-handlers.js +341 -0
  118. package/src/next/pages/protected-route.d.ts +28 -0
  119. package/src/next/pages/protected-route.js +59 -0
  120. package/src/next/pages/request.d.ts +14 -0
  121. package/src/next/pages/request.js +66 -0
  122. package/src/next/pages/response.d.ts +28 -0
  123. package/src/next/pages/response.js +29 -0
  124. package/src/next/pages/wrapper.d.ts +29 -0
  125. package/src/next/pages/wrapper.js +74 -0
  126. package/src/next/rewrites.d.ts +12 -0
  127. package/src/next/rewrites.js +74 -0
  128. package/src/next/shared/auth-http.d.ts +24 -0
  129. package/src/next/shared/auth-http.js +42 -0
  130. package/src/next/shared/auth-routes.d.ts +17 -0
  131. package/src/next/shared/auth-routes.js +153 -0
  132. package/src/next/shared/direct-auth-utils.d.ts +71 -0
  133. package/src/next/shared/direct-auth-utils.js +275 -0
  134. package/src/next/shared/oauth-utils.d.ts +45 -0
  135. package/src/next/shared/oauth-utils.js +308 -0
  136. package/src/next/shared/well-known-utils.d.ts +46 -0
  137. package/src/next/shared/well-known-utils.js +108 -0
  138. package/src/testing/in-memory/in-memory-access-token-revocation-adapter.d.ts +2 -0
  139. package/src/testing/in-memory/in-memory-access-token-revocation-adapter.js +14 -0
  140. package/src/testing/in-memory/in-memory-authorization-code-adapter.d.ts +2 -0
  141. package/src/testing/in-memory/in-memory-authorization-code-adapter.js +36 -0
  142. package/src/testing/in-memory/in-memory-oauth-client-adapter.d.ts +14 -0
  143. package/src/testing/in-memory/in-memory-oauth-client-adapter.js +26 -0
  144. package/src/testing/in-memory/in-memory-pending-auth-request-adapter.d.ts +2 -0
  145. package/src/testing/in-memory/in-memory-pending-auth-request-adapter.js +43 -0
  146. package/src/testing/in-memory/in-memory-refresh-token-adapter.d.ts +2 -0
  147. package/src/testing/in-memory/in-memory-refresh-token-adapter.js +67 -0
  148. package/src/testing/in-memory/in-memory-session-adapter.d.ts +6 -0
  149. package/src/testing/in-memory/in-memory-session-adapter.js +43 -0
  150. package/src/testing/in-memory/index.d.ts +7 -0
  151. package/src/testing/in-memory/index.js +7 -0
  152. package/src/testing/in-memory/test-fixtures.d.ts +5 -0
  153. package/src/testing/in-memory/test-fixtures.js +18 -0
  154. package/src/testing/index.d.ts +2 -0
  155. package/src/testing/index.js +4 -0
@@ -0,0 +1,425 @@
1
+ import { AuthServiceError } from '../../core/services/auth-error';
2
+ import { clearPagesAuthCookie, setPagesAuthCookie, } from './cookies';
3
+ import { sendPagesRedirect } from './response';
4
+ import { withPagesAuthRoute } from './wrapper';
5
+ import { assertBearerOnlyActorPolicy, assertCsrfForCookieMutation, DEFAULT_ACCESS_TOKEN_COOKIE_NAME, DEFAULT_REFRESH_TOKEN_COOKIE_NAME, extractRefreshToken, parseNonNegativeSafeInteger, resolveAccessTokenTransportAdapter, resolveCookieSecureFlag, resolveLoginMethod, resolvePathnameFromUrl, } from '../shared/direct-auth-utils';
6
+ import { parseBodyToRecord, toSingleString } from '../shared/oauth-utils';
7
+ function sendMethodNotAllowed(res, allowedMethods) {
8
+ res.setHeader('Allow', allowedMethods.join(', '));
9
+ res.status(405).json({
10
+ error: 'method_not_allowed',
11
+ error_description: 'HTTP method not allowed for auth route.',
12
+ });
13
+ }
14
+ function buildTokenOutput(input) {
15
+ const expiresIn = Math.max(0, input.accessClaims.exp - input.accessClaims.iat);
16
+ return {
17
+ access_token: input.accessToken,
18
+ token_type: 'Bearer',
19
+ expires_in: expiresIn,
20
+ ...(input.refreshToken === undefined
21
+ ? {}
22
+ : { refresh_token: input.refreshToken }),
23
+ ...(input.clientSession === undefined
24
+ ? {}
25
+ : { clientSession: input.clientSession }),
26
+ };
27
+ }
28
+ function resolveCookiesConfig(config) {
29
+ return {
30
+ enabled: config?.enabled ?? true,
31
+ accessTokenCookieName: toSingleString(config?.accessTokenCookieName) ??
32
+ DEFAULT_ACCESS_TOKEN_COOKIE_NAME,
33
+ refreshTokenCookieName: toSingleString(config?.refreshTokenCookieName) ??
34
+ DEFAULT_REFRESH_TOKEN_COOKIE_NAME,
35
+ path: toSingleString(config?.path) ?? '/',
36
+ sameSite: config?.sameSite ?? 'lax',
37
+ secure: resolveCookieSecureFlag({
38
+ ...(config?.secure === undefined ? {} : { secure: config.secure }),
39
+ ...(config?.secureInProduction === undefined
40
+ ? {}
41
+ : { secureInProduction: config.secureInProduction }),
42
+ }),
43
+ refreshTokenMaxAgeSeconds: parseNonNegativeSafeInteger(config?.refreshTokenMaxAgeSeconds ?? 60 * 60 * 24 * 30, 'cookies.refreshTokenMaxAgeSeconds'),
44
+ };
45
+ }
46
+ function clearAuthCookies(res, cookiesConfig) {
47
+ if (!cookiesConfig.enabled) {
48
+ return;
49
+ }
50
+ clearPagesAuthCookie(res, {
51
+ name: cookiesConfig.accessTokenCookieName,
52
+ path: cookiesConfig.path,
53
+ sameSite: cookiesConfig.sameSite,
54
+ secure: cookiesConfig.secure,
55
+ });
56
+ clearPagesAuthCookie(res, {
57
+ name: cookiesConfig.refreshTokenCookieName,
58
+ path: cookiesConfig.path,
59
+ sameSite: cookiesConfig.sameSite,
60
+ secure: cookiesConfig.secure,
61
+ });
62
+ }
63
+ function applyAuthCookies(input) {
64
+ if (!input.cookiesConfig.enabled) {
65
+ return;
66
+ }
67
+ setPagesAuthCookie(input.res, {
68
+ name: input.cookiesConfig.accessTokenCookieName,
69
+ value: input.accessToken,
70
+ maxAgeSeconds: input.accessTokenMaxAgeSeconds,
71
+ path: input.cookiesConfig.path,
72
+ sameSite: input.cookiesConfig.sameSite,
73
+ secure: input.cookiesConfig.secure,
74
+ });
75
+ if (input.refreshToken !== undefined) {
76
+ setPagesAuthCookie(input.res, {
77
+ name: input.cookiesConfig.refreshTokenCookieName,
78
+ value: input.refreshToken,
79
+ maxAgeSeconds: input.cookiesConfig.refreshTokenMaxAgeSeconds,
80
+ path: input.cookiesConfig.path,
81
+ sameSite: input.cookiesConfig.sameSite,
82
+ secure: input.cookiesConfig.secure,
83
+ });
84
+ }
85
+ }
86
+ function parseQuery(req) {
87
+ return req.query;
88
+ }
89
+ function requireLoginMethods(loginMethods) {
90
+ if (!loginMethods || Object.keys(loginMethods).length === 0) {
91
+ throw new AuthServiceError({
92
+ code: 'invalid_request',
93
+ message: 'No login methods are configured.',
94
+ });
95
+ }
96
+ return loginMethods;
97
+ }
98
+ function requireLoginMethod(input) {
99
+ const adapter = input.loginMethods[input.method];
100
+ if (!adapter) {
101
+ throw new AuthServiceError({
102
+ code: 'invalid_request',
103
+ message: `Unsupported login method "${input.method}".`,
104
+ });
105
+ }
106
+ return adapter;
107
+ }
108
+ export function createPagesDirectAuthHandlers(options) {
109
+ const cookiesConfig = resolveCookiesConfig(options.cookies);
110
+ const refreshTokenFieldName = toSingleString(options.refreshTokenFieldName) ?? 'refresh_token';
111
+ const accessTokenTransport = resolveAccessTokenTransportAdapter(options.accessTokenTransport);
112
+ const logoutAllowMissingCredentials = options.logoutAllowMissingCredentials ?? true;
113
+ const refresh = withPagesAuthRoute({
114
+ ...(options.requestIdHeaderName === undefined
115
+ ? {}
116
+ : { requestIdHeaderName: options.requestIdHeaderName }),
117
+ ...(options.authorizationHeaderName === undefined
118
+ ? {}
119
+ : { authorizationHeaderName: options.authorizationHeaderName }),
120
+ handler: async ({ req, res, requestId, transport }) => {
121
+ if (req.method !== 'POST') {
122
+ sendMethodNotAllowed(res, ['POST']);
123
+ return;
124
+ }
125
+ const body = parseBodyToRecord(req.body);
126
+ const pathname = resolvePathnameFromUrl(req.url);
127
+ const cookies = transport.cookies ?? {};
128
+ const extractedRefreshToken = extractRefreshToken({
129
+ body,
130
+ cookies,
131
+ bodyFieldName: refreshTokenFieldName,
132
+ cookieName: cookiesConfig.refreshTokenCookieName,
133
+ ...(options.refreshTokenPriority === undefined
134
+ ? {}
135
+ : { priority: options.refreshTokenPriority }),
136
+ });
137
+ assertCsrfForCookieMutation({
138
+ method: req.method,
139
+ pathname,
140
+ cookies,
141
+ headers: req.headers,
142
+ body,
143
+ cookieAuthenticated: extractedRefreshToken.source === 'cookie',
144
+ ...(options.csrf === undefined ? {} : { config: options.csrf }),
145
+ });
146
+ if (extractedRefreshToken.token === undefined) {
147
+ throw new AuthServiceError({
148
+ code: 'unauthorized',
149
+ message: 'Refresh token is required.',
150
+ });
151
+ }
152
+ const refreshed = await options.directAuthService.refresh({
153
+ refreshToken: extractedRefreshToken.token,
154
+ ...(requestId === undefined ? {} : { requestId }),
155
+ });
156
+ const output = buildTokenOutput({
157
+ accessToken: refreshed.accessToken,
158
+ accessClaims: refreshed.accessClaims,
159
+ ...(refreshed.refreshToken === undefined
160
+ ? {}
161
+ : { refreshToken: refreshed.refreshToken }),
162
+ ...(refreshed.clientSession === undefined
163
+ ? {}
164
+ : { clientSession: refreshed.clientSession }),
165
+ });
166
+ if (extractedRefreshToken.source === 'cookie') {
167
+ applyAuthCookies({
168
+ res,
169
+ cookiesConfig,
170
+ accessToken: refreshed.accessToken,
171
+ accessTokenMaxAgeSeconds: output.expires_in,
172
+ refreshToken: refreshed.refreshToken ?? extractedRefreshToken.token,
173
+ });
174
+ }
175
+ return output;
176
+ },
177
+ });
178
+ const logout = withPagesAuthRoute({
179
+ ...(options.requestIdHeaderName === undefined
180
+ ? {}
181
+ : { requestIdHeaderName: options.requestIdHeaderName }),
182
+ ...(options.authorizationHeaderName === undefined
183
+ ? {}
184
+ : { authorizationHeaderName: options.authorizationHeaderName }),
185
+ handler: async ({ req, res, requestId, transport }) => {
186
+ if (req.method !== 'POST') {
187
+ sendMethodNotAllowed(res, ['POST']);
188
+ return;
189
+ }
190
+ const body = parseBodyToRecord(req.body);
191
+ const pathname = resolvePathnameFromUrl(req.url);
192
+ const cookies = transport.cookies ?? {};
193
+ const extractedRefreshToken = extractRefreshToken({
194
+ body,
195
+ cookies,
196
+ bodyFieldName: refreshTokenFieldName,
197
+ cookieName: cookiesConfig.refreshTokenCookieName,
198
+ ...(options.refreshTokenPriority === undefined
199
+ ? {}
200
+ : { priority: options.refreshTokenPriority }),
201
+ });
202
+ const extractedAccessToken = accessTokenTransport.extractAccessToken({
203
+ transport,
204
+ });
205
+ assertCsrfForCookieMutation({
206
+ method: req.method,
207
+ pathname,
208
+ cookies,
209
+ headers: req.headers,
210
+ body,
211
+ cookieAuthenticated: extractedRefreshToken.source === 'cookie' ||
212
+ extractedAccessToken.source === 'cookie',
213
+ ...(options.csrf === undefined ? {} : { config: options.csrf }),
214
+ });
215
+ let validatedAccess;
216
+ if (extractedAccessToken.token) {
217
+ validatedAccess = await options.directAuthService.validateAccessToken({
218
+ accessToken: extractedAccessToken.token,
219
+ ...(requestId === undefined ? {} : { requestId }),
220
+ ...(options.expectedAudience === undefined
221
+ ? {}
222
+ : { expectedAudience: options.expectedAudience }),
223
+ ...(options.allowedActors === undefined
224
+ ? {}
225
+ : { allowedActors: options.allowedActors }),
226
+ });
227
+ assertBearerOnlyActorPolicy({
228
+ actor: validatedAccess.claims.actor,
229
+ ...(extractedAccessToken.source === undefined
230
+ ? {}
231
+ : { source: extractedAccessToken.source }),
232
+ ...(options.accessTokenTransport?.requireBearerForActors === undefined
233
+ ? {}
234
+ : {
235
+ requireBearerForActors: options.accessTokenTransport.requireBearerForActors,
236
+ }),
237
+ });
238
+ }
239
+ const sessionId = options.parseSessionId?.(body.session_id);
240
+ const resolvedSessionId = sessionId ?? validatedAccess?.authContext.sessionId;
241
+ if (resolvedSessionId === undefined &&
242
+ extractedRefreshToken.token === undefined &&
243
+ validatedAccess === undefined) {
244
+ clearAuthCookies(res, cookiesConfig);
245
+ if (logoutAllowMissingCredentials) {
246
+ return {
247
+ ok: true,
248
+ session_revoked: false,
249
+ refresh_token_revoked: false,
250
+ access_token_revoked: false,
251
+ };
252
+ }
253
+ throw new AuthServiceError({
254
+ code: 'invalid_request',
255
+ message: 'At least one logout credential (session_id, refresh_token, or access token) is required.',
256
+ });
257
+ }
258
+ const result = await options.directAuthService.logout({
259
+ ...(requestId === undefined ? {} : { requestId }),
260
+ ...(resolvedSessionId === undefined
261
+ ? {}
262
+ : { sessionId: resolvedSessionId }),
263
+ ...(extractedRefreshToken.token === undefined
264
+ ? {}
265
+ : { refreshToken: extractedRefreshToken.token }),
266
+ ...(validatedAccess === undefined
267
+ ? {}
268
+ : {
269
+ accessTokenJti: validatedAccess.claims.jti,
270
+ accessTokenExpiresAtUnix: validatedAccess.claims.exp,
271
+ }),
272
+ });
273
+ clearAuthCookies(res, cookiesConfig);
274
+ return {
275
+ ok: true,
276
+ session_revoked: result.sessionRevoked,
277
+ refresh_token_revoked: result.refreshTokenRevoked,
278
+ access_token_revoked: result.accessTokenRevoked,
279
+ };
280
+ },
281
+ });
282
+ const loginStart = withPagesAuthRoute({
283
+ ...(options.requestIdHeaderName === undefined
284
+ ? {}
285
+ : { requestIdHeaderName: options.requestIdHeaderName }),
286
+ handler: async ({ req, res }) => {
287
+ if (req.method !== 'POST') {
288
+ sendMethodNotAllowed(res, ['POST']);
289
+ return;
290
+ }
291
+ const loginMethods = requireLoginMethods(options.loginMethods);
292
+ const body = parseBodyToRecord(req.body);
293
+ const query = parseQuery(req);
294
+ const method = resolveLoginMethod({
295
+ pathname: resolvePathnameFromUrl(req.url),
296
+ phase: 'start',
297
+ body,
298
+ queryMethod: query.method,
299
+ ...(options.defaultLoginMethod === undefined
300
+ ? {}
301
+ : { defaultMethod: options.defaultLoginMethod }),
302
+ });
303
+ const adapter = requireLoginMethod({
304
+ loginMethods,
305
+ method,
306
+ });
307
+ if (!adapter.start) {
308
+ throw new AuthServiceError({
309
+ code: 'invalid_request',
310
+ message: `Login method "${method}" does not implement start.`,
311
+ });
312
+ }
313
+ await adapter.start(body);
314
+ return { ok: true };
315
+ },
316
+ });
317
+ const loginFinish = withPagesAuthRoute({
318
+ ...(options.requestIdHeaderName === undefined
319
+ ? {}
320
+ : { requestIdHeaderName: options.requestIdHeaderName }),
321
+ handler: async ({ req, res, requestId }) => {
322
+ if (req.method !== 'GET') {
323
+ sendMethodNotAllowed(res, ['GET']);
324
+ return;
325
+ }
326
+ const loginMethods = requireLoginMethods(options.loginMethods);
327
+ const query = parseQuery(req);
328
+ const method = resolveLoginMethod({
329
+ pathname: resolvePathnameFromUrl(req.url),
330
+ phase: 'finish',
331
+ queryMethod: query.method,
332
+ ...(options.defaultLoginMethod === undefined
333
+ ? {}
334
+ : { defaultMethod: options.defaultLoginMethod }),
335
+ });
336
+ const adapter = requireLoginMethod({
337
+ loginMethods,
338
+ method,
339
+ });
340
+ const finishResult = await adapter.finish(query);
341
+ if (finishResult === null) {
342
+ throw new AuthServiceError({
343
+ code: 'unauthorized',
344
+ message: 'Login method did not authenticate a user.',
345
+ });
346
+ }
347
+ if (!options.issueLogin) {
348
+ return { ok: true };
349
+ }
350
+ const issued = await options.issueLogin({
351
+ req,
352
+ res,
353
+ method,
354
+ userId: finishResult.userId,
355
+ ...(requestId === undefined ? {} : { requestId }),
356
+ });
357
+ if (!issued) {
358
+ return { ok: true };
359
+ }
360
+ if (issued.accessToken !== undefined) {
361
+ if (issued.accessClaims === undefined) {
362
+ throw new AuthServiceError({
363
+ code: 'invalid_request',
364
+ message: 'issueLogin must include accessClaims when accessToken is provided.',
365
+ });
366
+ }
367
+ const tokenOutput = buildTokenOutput({
368
+ accessToken: issued.accessToken,
369
+ accessClaims: issued.accessClaims,
370
+ ...(issued.refreshToken === undefined
371
+ ? {}
372
+ : { refreshToken: issued.refreshToken }),
373
+ ...(issued.clientSession === undefined
374
+ ? {}
375
+ : { clientSession: issued.clientSession }),
376
+ });
377
+ applyAuthCookies({
378
+ res,
379
+ cookiesConfig,
380
+ accessToken: issued.accessToken,
381
+ accessTokenMaxAgeSeconds: tokenOutput.expires_in,
382
+ ...(issued.refreshToken === undefined
383
+ ? {}
384
+ : { refreshToken: issued.refreshToken }),
385
+ });
386
+ if (issued.redirectTo) {
387
+ return {
388
+ location: issued.redirectTo,
389
+ ...(issued.statusCode === undefined
390
+ ? {}
391
+ : { statusCode: issued.statusCode }),
392
+ };
393
+ }
394
+ return issued.body ?? tokenOutput;
395
+ }
396
+ if (issued.redirectTo) {
397
+ return {
398
+ location: issued.redirectTo,
399
+ ...(issued.statusCode === undefined
400
+ ? {}
401
+ : { statusCode: issued.statusCode }),
402
+ };
403
+ }
404
+ return issued.body ?? { ok: true };
405
+ },
406
+ send: ({ res, output }) => {
407
+ const location = output.location;
408
+ if (typeof location === 'string') {
409
+ const statusCode = output.statusCode;
410
+ sendPagesRedirect(res, {
411
+ location,
412
+ ...(typeof statusCode === 'number' ? { statusCode } : {}),
413
+ });
414
+ return;
415
+ }
416
+ res.status(200).json(output);
417
+ },
418
+ });
419
+ return {
420
+ refresh,
421
+ logout,
422
+ loginStart,
423
+ loginFinish,
424
+ };
425
+ }
@@ -0,0 +1,8 @@
1
+ export { buildPagesAccessTokenTransportInput, getPagesAuthorizationHeader, getPagesCookies, getPagesRequestId, parseCookieHeader, } from './request';
2
+ export { appendSetCookieHeader, clearPagesAuthCookie, serializeSetCookie, setPagesAuthCookie, setPagesCookie, type CookieSameSite, type PagesCookieOptions, } from './cookies';
3
+ export { sendPagesAuthError, sendPagesRedirect, sendPagesSystemError, type PagesAuthErrorResponseBody, } from './response';
4
+ export { withPagesAuthRoute, type PagesAuthRouteContext, type WithPagesAuthRouteOptions, } from './wrapper';
5
+ export { withPagesProtectedRoute, type PagesProtectedAuth, type PagesProtectedRouteContext, type WithPagesProtectedRouteOptions, } from './protected-route';
6
+ export { createPagesAuthCatchAllHandler, type CreatePagesAuthCatchAllHandlerOptions, type PagesAuthCatchAllHandler, } from './catch-all';
7
+ export { createPagesMcpHandler, createPagesOAuthHandlers, createPagesWellKnownHandlers, type CreatePagesMcpHandlerOptions, type CreatePagesOAuthHandlersOptions, type CreatePagesWellKnownHandlersOptions, type PagesOAuthHandlers, type PagesWellKnownHandlers, } from './mcp-oauth-handlers';
8
+ export { createPagesDirectAuthHandlers, type CreatePagesDirectAuthHandlersOptions, type DirectCookieConfig as PagesDirectCookieConfig, type PagesDirectAuthHandlers, type PagesDirectLoginIssueResult, } from './direct-auth-handlers';
@@ -0,0 +1,8 @@
1
+ export { buildPagesAccessTokenTransportInput, getPagesAuthorizationHeader, getPagesCookies, getPagesRequestId, parseCookieHeader, } from './request';
2
+ export { appendSetCookieHeader, clearPagesAuthCookie, serializeSetCookie, setPagesAuthCookie, setPagesCookie, } from './cookies';
3
+ export { sendPagesAuthError, sendPagesRedirect, sendPagesSystemError, } from './response';
4
+ export { withPagesAuthRoute, } from './wrapper';
5
+ export { withPagesProtectedRoute, } from './protected-route';
6
+ export { createPagesAuthCatchAllHandler, } from './catch-all';
7
+ export { createPagesMcpHandler, createPagesOAuthHandlers, createPagesWellKnownHandlers, } from './mcp-oauth-handlers';
8
+ export { createPagesDirectAuthHandlers, } from './direct-auth-handlers';
@@ -0,0 +1,77 @@
1
+ import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next';
2
+ import type { OAuthGrantType } from '../../core/adapters/oauth-grant-type';
3
+ import type { McpAuthService, McpAuthenticationResult, OAuthService } from '../../core/services/index';
4
+ import type { AuthActor } from '../../core/types/auth-contract';
5
+ export type CreatePagesMcpHandlerOptions<TSessionId, TUserId, TActor extends AuthActor = AuthActor, TServerSessionData extends Record<string, unknown> = Record<string, never>, TClientSessionData extends Record<string, unknown> = Record<string, never>, TExtClaims extends Record<string, unknown> = Record<string, never>> = {
6
+ mcpAuthService: McpAuthService<TSessionId, TUserId, TActor, TServerSessionData, TClientSessionData, TExtClaims>;
7
+ handleRequest: (input: {
8
+ req: NextApiRequest;
9
+ res: NextApiResponse;
10
+ body: unknown;
11
+ authentication: McpAuthenticationResult<TSessionId, TUserId, TActor, TServerSessionData, TClientSessionData, TExtClaims>;
12
+ requestId?: string;
13
+ }) => Promise<unknown | void> | unknown | void;
14
+ parseBody?: (req: NextApiRequest) => unknown;
15
+ expectedAudience?: string;
16
+ allowedActors?: readonly TActor[] | ReadonlySet<TActor>;
17
+ challengeScope?: string;
18
+ resourceMetadataUrl?: string;
19
+ requestIdHeaderName?: string;
20
+ authorizationHeaderName?: string;
21
+ };
22
+ export declare function createPagesMcpHandler<TSessionId, TUserId, TActor extends AuthActor = AuthActor, TServerSessionData extends Record<string, unknown> = Record<string, never>, TClientSessionData extends Record<string, unknown> = Record<string, never>, TExtClaims extends Record<string, unknown> = Record<string, never>>(options: CreatePagesMcpHandlerOptions<TSessionId, TUserId, TActor, TServerSessionData, TClientSessionData, TExtClaims>): NextApiHandler;
23
+ export type CreatePagesOAuthHandlersOptions<TUserId, TActor extends AuthActor = AuthActor, TExtClaims extends Record<string, unknown> = Record<string, never>> = {
24
+ oauthService: OAuthService<TUserId, TActor, TExtClaims>;
25
+ resolveUserId: (input: {
26
+ req: NextApiRequest;
27
+ res: NextApiResponse;
28
+ requestId: string;
29
+ approved: boolean;
30
+ }) => Promise<TUserId | null> | TUserId | null;
31
+ requiredScope?: string;
32
+ requireState?: boolean;
33
+ defaultCodeMethod?: 'S256' | 'plain';
34
+ generateRequestId?: () => string;
35
+ getAuthorizeLoginLocation?: (input: {
36
+ req: NextApiRequest;
37
+ requestId: string;
38
+ expiresAtUnix: number;
39
+ }) => string;
40
+ token?: {
41
+ authorizationCodeSessionTtlSeconds?: number;
42
+ issueRefreshToken?: boolean;
43
+ allowGrantTypeInference?: boolean;
44
+ clientCredentials?: {
45
+ enabled?: boolean;
46
+ sessionTtlSeconds?: number;
47
+ };
48
+ };
49
+ requestIdHeaderName?: string;
50
+ authorizationHeaderName?: string;
51
+ };
52
+ export type PagesOAuthHandlers = {
53
+ authorize: NextApiHandler;
54
+ authorizeConfirm: NextApiHandler;
55
+ token: NextApiHandler;
56
+ };
57
+ export declare function createPagesOAuthHandlers<TUserId, TActor extends AuthActor = AuthActor, TExtClaims extends Record<string, unknown> = Record<string, never>>(options: CreatePagesOAuthHandlersOptions<TUserId, TActor, TExtClaims>): PagesOAuthHandlers;
58
+ export type CreatePagesWellKnownHandlersOptions = {
59
+ issuer?: string | ((req: NextApiRequest) => string);
60
+ authorizationEndpoint?: string;
61
+ tokenEndpoint?: string;
62
+ protectedResource?: string | ((req: NextApiRequest) => string);
63
+ authorizationServers?: ReadonlyArray<string> | ((req: NextApiRequest) => ReadonlyArray<string>);
64
+ responseTypesSupported?: readonly string[];
65
+ grantTypesSupported?: readonly OAuthGrantType[];
66
+ codeChallengeMethodsSupported?: ReadonlyArray<'S256' | 'plain'>;
67
+ tokenEndpointAuthMethodsSupported?: readonly string[];
68
+ scopesSupported?: readonly string[];
69
+ cacheControlHeader?: string;
70
+ fallbackOrigin?: string;
71
+ };
72
+ export type PagesWellKnownHandlers = {
73
+ oauthProtectedResource: NextApiHandler;
74
+ oauthAuthorizationServer: NextApiHandler;
75
+ openidConfiguration: NextApiHandler;
76
+ };
77
+ export declare function createPagesWellKnownHandlers(options?: CreatePagesWellKnownHandlersOptions): PagesWellKnownHandlers;