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.
- package/package.json +25 -0
- package/src/config/base-config.d.ts +17 -0
- package/src/config/base-config.js +33 -0
- package/src/config/index.d.ts +5 -0
- package/src/config/index.js +5 -0
- package/src/config/nextjs-public-config.d.ts +46 -0
- package/src/config/nextjs-public-config.js +89 -0
- package/src/config/nextjs-server-config.d.ts +32 -0
- package/src/config/nextjs-server-config.js +10 -0
- package/src/config/react-client.d.ts +23 -0
- package/src/config/react-client.js +69 -0
- package/src/config/react-config.d.ts +18 -0
- package/src/config/react-config.js +38 -0
- package/src/core/adapters/access-token-revocation-adapter.d.ts +8 -0
- package/src/core/adapters/access-token-revocation-adapter.js +1 -0
- package/src/core/adapters/access-token-transport-adapter.d.ts +15 -0
- package/src/core/adapters/access-token-transport-adapter.js +1 -0
- package/src/core/adapters/authorization-code-adapter.d.ts +21 -0
- package/src/core/adapters/authorization-code-adapter.js +1 -0
- package/src/core/adapters/authorization-hooks.d.ts +13 -0
- package/src/core/adapters/authorization-hooks.js +1 -0
- package/src/core/adapters/index.d.ts +14 -0
- package/src/core/adapters/index.js +1 -0
- package/src/core/adapters/login-method-adapter.d.ts +7 -0
- package/src/core/adapters/login-method-adapter.js +1 -0
- package/src/core/adapters/oauth-client-adapter.d.ts +13 -0
- package/src/core/adapters/oauth-client-adapter.js +1 -0
- package/src/core/adapters/oauth-client-management-adapter.d.ts +23 -0
- package/src/core/adapters/oauth-client-management-adapter.js +1 -0
- package/src/core/adapters/oauth-grant-type.d.ts +1 -0
- package/src/core/adapters/oauth-grant-type.js +1 -0
- package/src/core/adapters/oauth-policy.d.ts +9 -0
- package/src/core/adapters/oauth-policy.js +1 -0
- package/src/core/adapters/observability-hooks.d.ts +31 -0
- package/src/core/adapters/observability-hooks.js +1 -0
- package/src/core/adapters/pending-auth-request-adapter.d.ts +18 -0
- package/src/core/adapters/pending-auth-request-adapter.js +1 -0
- package/src/core/adapters/refresh-token-adapter.d.ts +24 -0
- package/src/core/adapters/refresh-token-adapter.js +1 -0
- package/src/core/adapters/session-adapter.d.ts +14 -0
- package/src/core/adapters/session-adapter.js +1 -0
- package/src/core/adapters/token-adapter.d.ts +15 -0
- package/src/core/adapters/token-adapter.js +1 -0
- package/src/core/http/bearer-challenge.d.ts +6 -0
- package/src/core/http/bearer-challenge.js +16 -0
- package/src/core/ids/id-codec.d.ts +6 -0
- package/src/core/ids/id-codec.js +30 -0
- package/src/core/index.d.ts +9 -0
- package/src/core/index.js +7 -0
- package/src/core/oauth/pkce.d.ts +9 -0
- package/src/core/oauth/pkce.js +30 -0
- package/src/core/services/access-token-service.d.ts +42 -0
- package/src/core/services/access-token-service.js +304 -0
- package/src/core/services/auth-error.d.ts +14 -0
- package/src/core/services/auth-error.js +47 -0
- package/src/core/services/contracts.d.ts +23 -0
- package/src/core/services/contracts.js +1 -0
- package/src/core/services/direct-auth-service.d.ts +50 -0
- package/src/core/services/direct-auth-service.js +267 -0
- package/src/core/services/index.d.ts +7 -0
- package/src/core/services/index.js +5 -0
- package/src/core/services/mcp-auth-service.d.ts +39 -0
- package/src/core/services/mcp-auth-service.js +170 -0
- package/src/core/services/oauth-service.d.ts +91 -0
- package/src/core/services/oauth-service.js +571 -0
- package/src/core/services/observability.d.ts +22 -0
- package/src/core/services/observability.js +71 -0
- package/src/core/services/revocation-policy.d.ts +21 -0
- package/src/core/services/revocation-policy.js +51 -0
- package/src/core/sessions/client-session.d.ts +7 -0
- package/src/core/sessions/client-session.js +18 -0
- package/src/core/tokens/access-claims.d.ts +21 -0
- package/src/core/tokens/access-claims.js +128 -0
- package/src/core/tokens/id-claims.d.ts +20 -0
- package/src/core/tokens/id-claims.js +25 -0
- package/src/core/types/auth-contract.d.ts +33 -0
- package/src/core/types/auth-contract.js +1 -0
- package/src/express/index.d.ts +1 -0
- package/src/express/index.js +1 -0
- package/src/express/protected-route.d.ts +44 -0
- package/src/express/protected-route.js +119 -0
- package/src/index.d.ts +8 -0
- package/src/index.js +8 -0
- package/src/mcp/index.d.ts +1 -0
- package/src/mcp/index.js +1 -0
- package/src/mcp/json-rpc-auth.d.ts +5 -0
- package/src/mcp/json-rpc-auth.js +41 -0
- package/src/next/app/catch-all.d.ts +32 -0
- package/src/next/app/catch-all.js +82 -0
- package/src/next/app/cookies.d.ts +22 -0
- package/src/next/app/cookies.js +36 -0
- package/src/next/app/direct-auth-handlers.d.ts +55 -0
- package/src/next/app/direct-auth-handlers.js +419 -0
- package/src/next/app/index.d.ts +8 -0
- package/src/next/app/index.js +8 -0
- package/src/next/app/mcp-oauth-handlers.d.ts +74 -0
- package/src/next/app/mcp-oauth-handlers.js +365 -0
- package/src/next/app/protected-route.d.ts +27 -0
- package/src/next/app/protected-route.js +59 -0
- package/src/next/app/request.d.ts +12 -0
- package/src/next/app/request.js +30 -0
- package/src/next/app/response.d.ts +16 -0
- package/src/next/app/response.js +48 -0
- package/src/next/app/wrapper.d.ts +28 -0
- package/src/next/app/wrapper.js +78 -0
- package/src/next/index.d.ts +6 -0
- package/src/next/index.js +5 -0
- package/src/next/pages/catch-all.d.ts +19 -0
- package/src/next/pages/catch-all.js +60 -0
- package/src/next/pages/cookies.d.ts +41 -0
- package/src/next/pages/cookies.js +87 -0
- package/src/next/pages/direct-auth-handlers.d.ts +58 -0
- package/src/next/pages/direct-auth-handlers.js +425 -0
- package/src/next/pages/index.d.ts +8 -0
- package/src/next/pages/index.js +8 -0
- package/src/next/pages/mcp-oauth-handlers.d.ts +77 -0
- package/src/next/pages/mcp-oauth-handlers.js +341 -0
- package/src/next/pages/protected-route.d.ts +28 -0
- package/src/next/pages/protected-route.js +59 -0
- package/src/next/pages/request.d.ts +14 -0
- package/src/next/pages/request.js +66 -0
- package/src/next/pages/response.d.ts +28 -0
- package/src/next/pages/response.js +29 -0
- package/src/next/pages/wrapper.d.ts +29 -0
- package/src/next/pages/wrapper.js +74 -0
- package/src/next/rewrites.d.ts +12 -0
- package/src/next/rewrites.js +74 -0
- package/src/next/shared/auth-http.d.ts +24 -0
- package/src/next/shared/auth-http.js +42 -0
- package/src/next/shared/auth-routes.d.ts +17 -0
- package/src/next/shared/auth-routes.js +153 -0
- package/src/next/shared/direct-auth-utils.d.ts +71 -0
- package/src/next/shared/direct-auth-utils.js +275 -0
- package/src/next/shared/oauth-utils.d.ts +45 -0
- package/src/next/shared/oauth-utils.js +308 -0
- package/src/next/shared/well-known-utils.d.ts +46 -0
- package/src/next/shared/well-known-utils.js +108 -0
- package/src/testing/in-memory/in-memory-access-token-revocation-adapter.d.ts +2 -0
- package/src/testing/in-memory/in-memory-access-token-revocation-adapter.js +14 -0
- package/src/testing/in-memory/in-memory-authorization-code-adapter.d.ts +2 -0
- package/src/testing/in-memory/in-memory-authorization-code-adapter.js +36 -0
- package/src/testing/in-memory/in-memory-oauth-client-adapter.d.ts +14 -0
- package/src/testing/in-memory/in-memory-oauth-client-adapter.js +26 -0
- package/src/testing/in-memory/in-memory-pending-auth-request-adapter.d.ts +2 -0
- package/src/testing/in-memory/in-memory-pending-auth-request-adapter.js +43 -0
- package/src/testing/in-memory/in-memory-refresh-token-adapter.d.ts +2 -0
- package/src/testing/in-memory/in-memory-refresh-token-adapter.js +67 -0
- package/src/testing/in-memory/in-memory-session-adapter.d.ts +6 -0
- package/src/testing/in-memory/in-memory-session-adapter.js +43 -0
- package/src/testing/in-memory/index.d.ts +7 -0
- package/src/testing/in-memory/index.js +7 -0
- package/src/testing/in-memory/test-fixtures.d.ts +5 -0
- package/src/testing/in-memory/test-fixtures.js +18 -0
- package/src/testing/index.d.ts +2 -0
- 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;
|