@cedarjs/auth-dbauth-oauth 7.0.0-canary.3075

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.
@@ -0,0 +1,57 @@
1
+ import type { APIGatewayProxyEvent, Context as LambdaContext } from 'aws-lambda';
2
+ import type { CorsContext } from '@cedarjs/api';
3
+ import { getDbAuthResponseBuilder } from '@cedarjs/auth-dbauth-api';
4
+ import { IdentityModel } from './identity.js';
5
+ import type { NormalizedOAuthRequest } from './request.js';
6
+ import type { OAuthHandlerOptions } from './types.js';
7
+ /**
8
+ * Handles the redirect-based OAuth flow (`login`/`signup`/`link`/`unlink`)
9
+ * for dbAuth, under its own base path (`/auth/oauth` by default) rather
10
+ * than `DbAuthHandler`'s `METHODS`/`VERBS` dispatch table — see the
11
+ * "Endpoint shape" decision in the implementation plan.
12
+ *
13
+ * Constructed per-request, the same way `DbAuthHandler` is, and accepts
14
+ * both a Lambda-style event and a Fetch `Request` so it works from a
15
+ * function handler and from `@cedarjs/auth-dbauth-middleware`.
16
+ */
17
+ export declare class OAuthHandler<TDb extends object = Record<string, unknown>> {
18
+ event: Request | APIGatewayProxyEvent;
19
+ options: OAuthHandlerOptions<TDb>;
20
+ basePath: string;
21
+ corsContext: CorsContext | undefined;
22
+ createResponse: ReturnType<typeof getDbAuthResponseBuilder>;
23
+ identities: IdentityModel;
24
+ userAccessor: any;
25
+ _normalizedRequest: NormalizedOAuthRequest | undefined;
26
+ constructor(event: APIGatewayProxyEvent | Request, _context: LambdaContext, options: OAuthHandlerOptions<TDb>);
27
+ get normalizedRequest(): NormalizedOAuthRequest;
28
+ init(): Promise<void>;
29
+ invoke(): Promise<{
30
+ statusCode: number;
31
+ headers: Record<string, string | string[]>;
32
+ multiValueHeaders?: Record<string, string[]>;
33
+ body?: string;
34
+ }>;
35
+ /**
36
+ * Cookie config for the transaction cookie: `transactionCookie` when set,
37
+ * otherwise falls back to `cookie` (the same config the session cookie
38
+ * uses).
39
+ */
40
+ private get _transactionCookieConfig();
41
+ private _authorize;
42
+ private _callback;
43
+ private _handleLogin;
44
+ private _handleSignup;
45
+ private _handleLink;
46
+ private _loginRedirect;
47
+ private _findUserById;
48
+ private _unlink;
49
+ private _statusForError;
50
+ private _requestHost;
51
+ private _requestProtocol;
52
+ private _json;
53
+ private _notFound;
54
+ private _redirectWithError;
55
+ private _handleUnexpectedError;
56
+ }
57
+ //# sourceMappingURL=OAuthHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OAuthHandler.d.ts","sourceRoot":"","sources":["../src/OAuthHandler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,YAAY,CAAA;AAEhF,OAAO,KAAK,EAAE,WAAW,EAAkB,MAAM,cAAc,CAAA;AAE/D,OAAO,EAKL,wBAAwB,EAKzB,MAAM,0BAA0B,CAAA;AAcjC,OAAO,EAAyB,aAAa,EAAE,MAAM,eAAe,CAAA;AAMpE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAS1D,OAAO,KAAK,EAAE,mBAAmB,EAA2B,MAAM,YAAY,CAAA;AAgB9E;;;;;;;;;GASG;AACH,qBAAa,YAAY,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACpE,KAAK,EAAE,OAAO,GAAG,oBAAoB,CAAA;IACrC,OAAO,EAAE,mBAAmB,CAAC,GAAG,CAAC,CAAA;IACjC,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,WAAW,GAAG,SAAS,CAAA;IACpC,cAAc,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAA;IAC3D,UAAU,EAAE,aAAa,CAAA;IACzB,YAAY,EAAE,GAAG,CAAA;IACjB,kBAAkB,EAAE,sBAAsB,GAAG,SAAS,CAAA;gBAGpD,KAAK,EAAE,oBAAoB,GAAG,OAAO,EACrC,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,mBAAmB,CAAC,GAAG,CAAC;IAkBnC,IAAI,iBAAiB,IAAI,sBAAsB,CAQ9C;IAEK,IAAI;IAIJ,MAAM;;;;;;IA4DZ;;;;OAIG;IACH,OAAO,KAAK,wBAAwB,GAEnC;YAIa,UAAU;YAmEV,SAAS;YAgGT,YAAY;YAoBZ,aAAa;YA+Cb,WAAW;IAiCzB,OAAO,CAAC,cAAc;YAYR,aAAa;YASb,OAAO;IAsFrB,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,KAAK;IAWb,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,sBAAsB;CAY/B"}
@@ -0,0 +1,481 @@
1
+ import { createCorsContext, isFetchApiRequest } from "@cedarjs/api";
2
+ import {
3
+ createExpiresAtDate,
4
+ createLoginResponse,
5
+ dbAuthSession,
6
+ extractCookie,
7
+ getDbAuthResponseBuilder,
8
+ isProxiedRequest,
9
+ isRequestOriginTrusted,
10
+ resolveRequestHost,
11
+ resolveRequestProtocol
12
+ } from "@cedarjs/auth-dbauth-api";
13
+ import {
14
+ CannotUnlinkLastIdentityError,
15
+ EmailInUseError,
16
+ FlowNotEnabledError,
17
+ IdentityInUseError,
18
+ NotAuthenticatedError,
19
+ OAuthError,
20
+ UnknownIdentityError,
21
+ UnknownProviderError,
22
+ UntrustedOriginError
23
+ } from "./errors.js";
24
+ import { resolveIdentityFields, IdentityModel } from "./identity.js";
25
+ import {
26
+ normalizeOAuthRequest,
27
+ parseAuthorizeFlow,
28
+ parseOAuthRoute
29
+ } from "./request.js";
30
+ import {
31
+ clearTransactionCookieString,
32
+ createTransactionCookieString,
33
+ decodeTransactionCookie,
34
+ DEFAULT_TRANSACTION_EXPIRES_SECONDS,
35
+ getTransactionCookieValue,
36
+ isTransactionExpired
37
+ } from "./transactionCookie.js";
38
+ function appendQueryParams(path, params) {
39
+ const separator = path.includes("?") ? "&" : "?";
40
+ return `${path}${separator}${new URLSearchParams(params).toString()}`;
41
+ }
42
+ class OAuthHandler {
43
+ event;
44
+ options;
45
+ basePath;
46
+ corsContext;
47
+ createResponse;
48
+ identities;
49
+ userAccessor;
50
+ _normalizedRequest;
51
+ constructor(event, _context, options) {
52
+ this.event = event;
53
+ this.options = options;
54
+ this.basePath = options.basePath ?? "/auth/oauth";
55
+ this.createResponse = getDbAuthResponseBuilder(event);
56
+ if (options.cors) {
57
+ this.corsContext = createCorsContext(options.cors);
58
+ }
59
+ this.userAccessor = options.db[options.authModelAccessor];
60
+ this.identities = new IdentityModel(
61
+ options.db[options.oauthModelAccessor],
62
+ resolveIdentityFields(options.oauthFields)
63
+ );
64
+ }
65
+ get normalizedRequest() {
66
+ if (!this._normalizedRequest) {
67
+ throw new Error(
68
+ "OAuthHandler has not been initialized. Either await oauthHandler.invoke() or call await oauthHandler.init()."
69
+ );
70
+ }
71
+ return this._normalizedRequest;
72
+ }
73
+ async init() {
74
+ this._normalizedRequest ??= await normalizeOAuthRequest(this.event);
75
+ }
76
+ async invoke() {
77
+ let corsHeaders = {};
78
+ await this.init();
79
+ if (this.corsContext) {
80
+ const corsRequest = {
81
+ ...this.normalizedRequest,
82
+ jsonBody: {}
83
+ };
84
+ corsHeaders = this.corsContext.getRequestHeaders(corsRequest);
85
+ if (this.corsContext.shouldHandleCors(corsRequest)) {
86
+ return this.createResponse({ body: "", statusCode: 200 }, corsHeaders);
87
+ }
88
+ }
89
+ const route = parseOAuthRoute(this.normalizedRequest.path, this.basePath);
90
+ if (!route) {
91
+ return this.createResponse(this._notFound(), corsHeaders);
92
+ }
93
+ const { method } = this.normalizedRequest;
94
+ if (route.action === "authorize") {
95
+ if (method !== "GET") {
96
+ return this.createResponse(this._notFound(), corsHeaders);
97
+ }
98
+ return this.createResponse(
99
+ await this._authorize(route.provider),
100
+ corsHeaders
101
+ );
102
+ }
103
+ if (route.action === "callback") {
104
+ if (method !== "GET" && method !== "POST") {
105
+ return this.createResponse(this._notFound(), corsHeaders);
106
+ }
107
+ return this.createResponse(
108
+ await this._callback(route.provider),
109
+ corsHeaders
110
+ );
111
+ }
112
+ if (method !== "POST") {
113
+ return this.createResponse(this._notFound(), corsHeaders);
114
+ }
115
+ return this.createResponse(await this._unlink(route.provider), corsHeaders);
116
+ }
117
+ /**
118
+ * Cookie config for the transaction cookie: `transactionCookie` when set,
119
+ * otherwise falls back to `cookie` (the same config the session cookie
120
+ * uses).
121
+ */
122
+ get _transactionCookieConfig() {
123
+ return this.options.transactionCookie ?? this.options.cookie;
124
+ }
125
+ // -- authorize --------------------------------------------------------
126
+ async _authorize(providerKey) {
127
+ const strategy = this.options.providers[providerKey];
128
+ if (!strategy) {
129
+ return this._redirectWithError("unknown_provider", providerKey);
130
+ }
131
+ const flow = parseAuthorizeFlow(this.normalizedRequest.query);
132
+ if (flow === "signup" && this.options.signup.enabled === false) {
133
+ return this._redirectWithError("flow_not_enabled", providerKey);
134
+ }
135
+ if (flow === "link") {
136
+ const session = dbAuthSession(this.event, this.options.cookie?.name);
137
+ if (!session) {
138
+ return this._redirectWithError("not_authenticated", providerKey);
139
+ }
140
+ }
141
+ try {
142
+ const oauth = await import("oauth4webapi");
143
+ const state = oauth.generateRandomState();
144
+ const codeVerifier = oauth.generateRandomCodeVerifier();
145
+ const codeChallenge = await oauth.calculatePKCECodeChallenge(codeVerifier);
146
+ const nonce = strategy.usesOidc ? oauth.generateRandomNonce() : void 0;
147
+ const authorizationUrl = await strategy.getAuthorizationUrl({
148
+ provider: providerKey,
149
+ redirectUri: strategy.redirectUri,
150
+ flow,
151
+ state,
152
+ codeVerifier,
153
+ codeChallenge,
154
+ nonce
155
+ });
156
+ const transactionExpires = this.options.transactionExpires ?? DEFAULT_TRANSACTION_EXPIRES_SECONDS;
157
+ const headers = new Headers();
158
+ headers.set("Location", authorizationUrl.toString());
159
+ headers.append(
160
+ "set-cookie",
161
+ createTransactionCookieString({
162
+ data: {
163
+ provider: providerKey,
164
+ flow,
165
+ state,
166
+ codeVerifier,
167
+ nonce,
168
+ createdAt: Date.now()
169
+ },
170
+ cookieConfig: this._transactionCookieConfig,
171
+ expiresSeconds: transactionExpires
172
+ })
173
+ );
174
+ return { body: "", statusCode: 302, headers };
175
+ } catch (e) {
176
+ return this._handleUnexpectedError(e, providerKey);
177
+ }
178
+ }
179
+ // -- callback -----------------------------------------------------------
180
+ async _callback(providerKey) {
181
+ const clearCookieHeaders = new Headers();
182
+ clearCookieHeaders.append(
183
+ "set-cookie",
184
+ clearTransactionCookieString(this._transactionCookieConfig)
185
+ );
186
+ const cookieHeader = extractCookie(this.event);
187
+ const txn = decodeTransactionCookie(getTransactionCookieValue(cookieHeader));
188
+ const transactionExpires = this.options.transactionExpires ?? DEFAULT_TRANSACTION_EXPIRES_SECONDS;
189
+ if (!txn || txn?.provider !== providerKey) {
190
+ return this._redirectWithError(
191
+ "invalid_state",
192
+ providerKey,
193
+ clearCookieHeaders
194
+ );
195
+ }
196
+ if (isTransactionExpired(txn, transactionExpires)) {
197
+ return this._redirectWithError(
198
+ "invalid_state",
199
+ providerKey,
200
+ clearCookieHeaders
201
+ );
202
+ }
203
+ const strategy = this.options.providers[providerKey];
204
+ if (!strategy) {
205
+ return this._redirectWithError(
206
+ "unknown_provider",
207
+ providerKey,
208
+ clearCookieHeaders
209
+ );
210
+ }
211
+ const { query, form } = this.normalizedRequest;
212
+ if (query.error ?? form.error) {
213
+ return this._redirectWithError(
214
+ "provider_error",
215
+ providerKey,
216
+ clearCookieHeaders
217
+ );
218
+ }
219
+ const state = query.state ?? form.state;
220
+ if (!state || state !== txn.state) {
221
+ return this._redirectWithError(
222
+ "invalid_state",
223
+ providerKey,
224
+ clearCookieHeaders
225
+ );
226
+ }
227
+ let profile;
228
+ try {
229
+ profile = await strategy.handleCallback({
230
+ provider: providerKey,
231
+ redirectUri: strategy.redirectUri,
232
+ flow: txn.flow,
233
+ state: txn.state,
234
+ codeVerifier: txn.codeVerifier,
235
+ nonce: txn.nonce,
236
+ query,
237
+ form
238
+ });
239
+ } catch (e) {
240
+ return this._handleUnexpectedError(e, providerKey, clearCookieHeaders);
241
+ }
242
+ try {
243
+ let result;
244
+ if (txn.flow === "login") {
245
+ result = await this._handleLogin(providerKey, profile);
246
+ } else if (txn.flow === "signup") {
247
+ result = await this._handleSignup(providerKey, profile);
248
+ } else {
249
+ result = await this._handleLink(providerKey, profile);
250
+ }
251
+ result.headers.append(
252
+ "set-cookie",
253
+ clearTransactionCookieString(this._transactionCookieConfig)
254
+ );
255
+ return result;
256
+ } catch (e) {
257
+ if (e instanceof OAuthError) {
258
+ return this._redirectWithError(e.code, providerKey, clearCookieHeaders);
259
+ }
260
+ return this._handleUnexpectedError(e, providerKey, clearCookieHeaders);
261
+ }
262
+ }
263
+ async _handleLogin(providerKey, profile) {
264
+ const identity = await this.identities.findByProviderUserId(
265
+ providerKey,
266
+ profile.providerUserId
267
+ );
268
+ if (!identity) {
269
+ throw new UnknownIdentityError(providerKey);
270
+ }
271
+ const user = await this._findUserById(this.identities.userIdOf(identity));
272
+ if (!user) {
273
+ throw new UnknownIdentityError(providerKey);
274
+ }
275
+ return this._loginRedirect(user, this.options.redirects.afterLogin);
276
+ }
277
+ async _handleSignup(providerKey, profile) {
278
+ if (this.options.signup.enabled === false) {
279
+ throw new FlowNotEnabledError();
280
+ }
281
+ const existingIdentity = await this.identities.findByProviderUserId(
282
+ providerKey,
283
+ profile.providerUserId
284
+ );
285
+ if (existingIdentity) {
286
+ const user2 = await this._findUserById(
287
+ this.identities.userIdOf(existingIdentity)
288
+ );
289
+ if (user2) {
290
+ return this._loginRedirect(user2, this.options.redirects.afterLogin);
291
+ }
292
+ }
293
+ if (profile.email) {
294
+ const existingUser = await this.userAccessor.findFirst({
295
+ where: { [this.options.authFields.username]: profile.email }
296
+ });
297
+ if (existingUser) {
298
+ throw new EmailInUseError();
299
+ }
300
+ }
301
+ const signup = this.options.signup;
302
+ if (!("handler" in signup)) {
303
+ throw new FlowNotEnabledError();
304
+ }
305
+ const user = await signup.handler({ provider: providerKey, profile });
306
+ const userId = user[this.options.authFields.id];
307
+ await this.identities.create(userId, providerKey, profile);
308
+ return this._loginRedirect(
309
+ user,
310
+ this.options.redirects.afterSignup ?? this.options.redirects.afterLogin
311
+ );
312
+ }
313
+ async _handleLink(providerKey, profile) {
314
+ const session = dbAuthSession(this.event, this.options.cookie?.name);
315
+ if (!session) {
316
+ throw new NotAuthenticatedError();
317
+ }
318
+ const userId = session[this.options.authFields.id];
319
+ const existingIdentity = await this.identities.findByProviderUserId(
320
+ providerKey,
321
+ profile.providerUserId
322
+ );
323
+ if (existingIdentity) {
324
+ const existingUserId = this.identities.userIdOf(existingIdentity);
325
+ if (existingUserId !== userId) {
326
+ throw new IdentityInUseError();
327
+ }
328
+ } else {
329
+ await this.identities.create(userId, providerKey, profile);
330
+ }
331
+ const headers = new Headers();
332
+ headers.set(
333
+ "Location",
334
+ this.options.redirects.afterLink ?? this.options.redirects.afterLogin
335
+ );
336
+ return { body: "", statusCode: 302, headers };
337
+ }
338
+ _loginRedirect(user, redirectTo) {
339
+ const expiresAt = createExpiresAtDate(this.options.sessionExpires);
340
+ const [, headers] = createLoginResponse(user, {
341
+ cookie: this.options.cookie,
342
+ allowedUserFields: this.options.allowedUserFields,
343
+ expiresAt
344
+ });
345
+ headers.set("Location", redirectTo);
346
+ return { body: "", statusCode: 302, headers };
347
+ }
348
+ async _findUserById(userId) {
349
+ const user = await this.userAccessor.findFirst({
350
+ where: { [this.options.authFields.id]: userId }
351
+ });
352
+ return user ?? null;
353
+ }
354
+ // -- unlink ---------------------------------------------------------------
355
+ async _unlink(providerKey) {
356
+ try {
357
+ if (!isRequestOriginTrusted(
358
+ {
359
+ method: this.normalizedRequest.method,
360
+ headers: this.normalizedRequest.headers,
361
+ host: this._requestHost(),
362
+ protocol: this._requestProtocol(),
363
+ proxied: isProxiedRequest(this.normalizedRequest.headers)
364
+ },
365
+ {
366
+ trustedOrigins: this.options.trustedOrigins,
367
+ corsOrigin: this.options.cors?.origin
368
+ }
369
+ )) {
370
+ throw new UntrustedOriginError();
371
+ }
372
+ const session = dbAuthSession(this.event, this.options.cookie?.name);
373
+ if (!session) {
374
+ throw new NotAuthenticatedError();
375
+ }
376
+ const userId = session[this.options.authFields.id];
377
+ if (!this.options.providers[providerKey]) {
378
+ throw new UnknownProviderError(providerKey);
379
+ }
380
+ const identity = await this.identities.findByUserAndProvider(
381
+ userId,
382
+ providerKey
383
+ );
384
+ if (!identity) {
385
+ return this._json({ error: "unknown_identity" }, 404);
386
+ }
387
+ const user = await this._findUserById(userId);
388
+ const hasPassword = Boolean(
389
+ user?.[this.options.authFields.hashedPassword]
390
+ );
391
+ if (!hasPassword) {
392
+ const allIdentities = await this.identities.findAllForUser(userId);
393
+ if (allIdentities.length <= 1) {
394
+ throw new CannotUnlinkLastIdentityError();
395
+ }
396
+ }
397
+ await this.identities.delete(userId, providerKey);
398
+ if (!hasPassword) {
399
+ const remaining = await this.identities.findAllForUser(userId);
400
+ if (remaining.length === 0) {
401
+ await this.identities.create(
402
+ userId,
403
+ providerKey,
404
+ this.identities.profileOf(identity)
405
+ );
406
+ throw new CannotUnlinkLastIdentityError();
407
+ }
408
+ }
409
+ return this._json({ ok: true }, 200);
410
+ } catch (e) {
411
+ if (e instanceof OAuthError) {
412
+ return this._json({ error: e.code }, this._statusForError(e.code));
413
+ }
414
+ const message = e instanceof Error ? e.message : String(e);
415
+ console.error("[@cedarjs/auth-dbauth-oauth] unlink failed:", message);
416
+ return this._json({ error: "server_error" }, 500);
417
+ }
418
+ }
419
+ _statusForError(code) {
420
+ switch (code) {
421
+ case "not_authenticated":
422
+ return 401;
423
+ case "untrusted_origin":
424
+ return 403;
425
+ case "unknown_provider":
426
+ return 404;
427
+ default:
428
+ return 400;
429
+ }
430
+ }
431
+ // best-effort host the request was sent to, used by origin validation to
432
+ // allow same-origin `unlink` requests through with no extra
433
+ // configuration. Prefers `X-Forwarded-Host` over the connection-level
434
+ // host/URL -- see `resolveRequestHost` for why that's safe behind a proxy
435
+ _requestHost() {
436
+ return resolveRequestHost(
437
+ this.normalizedRequest.headers,
438
+ isFetchApiRequest(this.event) ? this.event.url : void 0
439
+ );
440
+ }
441
+ // best-effort scheme the request arrived over, used alongside
442
+ // `_requestHost` for origin validation. See `resolveRequestProtocol` for
443
+ // when it can and can't be determined reliably
444
+ _requestProtocol() {
445
+ return resolveRequestProtocol(
446
+ this.normalizedRequest.headers,
447
+ isFetchApiRequest(this.event) ? this.event.url : void 0
448
+ );
449
+ }
450
+ // -- response helpers -------------------------------------------------
451
+ _json(data, statusCode) {
452
+ return {
453
+ body: JSON.stringify(data),
454
+ statusCode,
455
+ headers: new Headers({ "content-type": "application/json" })
456
+ };
457
+ }
458
+ _notFound() {
459
+ return this._json({ error: "not_found" }, 404);
460
+ }
461
+ _redirectWithError(code, providerKey, extraHeaders) {
462
+ const headers = extraHeaders ? new Headers(extraHeaders) : new Headers();
463
+ headers.set(
464
+ "Location",
465
+ appendQueryParams(this.options.redirects.error, {
466
+ error: code,
467
+ provider: providerKey
468
+ })
469
+ );
470
+ return { body: "", statusCode: 302, headers };
471
+ }
472
+ _handleUnexpectedError(e, providerKey, extraHeaders) {
473
+ const message = e instanceof Error ? e.message : String(e);
474
+ console.error("[@cedarjs/auth-dbauth-oauth]", message);
475
+ const code = e instanceof OAuthError ? e.code : "server_error";
476
+ return this._redirectWithError(code, providerKey, extraHeaders);
477
+ }
478
+ }
479
+ export {
480
+ OAuthHandler
481
+ };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Stable, documented error codes surfaced to the browser via the `error`
3
+ * query param on the configured error-redirect (or in the JSON body for the
4
+ * `unlink` flow, which never redirects). Exception text is never leaked to
5
+ * the client — only one of these codes is.
6
+ */
7
+ export type OAuthErrorCode = 'unknown_provider' | 'invalid_state' | 'provider_error' | 'unknown_identity' | 'email_in_use' | 'identity_in_use' | 'not_authenticated' | 'flow_not_enabled' | 'cannot_unlink_last_identity' | 'untrusted_origin' | 'server_error';
8
+ /**
9
+ * Base class for every error the OAuth handler throws. `code` is the stable
10
+ * identifier written to the error-redirect/JSON response; `message` is for
11
+ * server-side logs only and is never sent to the client.
12
+ */
13
+ export declare class OAuthError extends Error {
14
+ code: OAuthErrorCode;
15
+ constructor(code: OAuthErrorCode, message: string);
16
+ }
17
+ export declare class UnknownProviderError extends OAuthError {
18
+ constructor(provider: string);
19
+ }
20
+ export declare class InvalidStateError extends OAuthError {
21
+ constructor(message?: string);
22
+ }
23
+ export declare class ProviderError extends OAuthError {
24
+ constructor(message: string);
25
+ }
26
+ export declare class UnknownIdentityError extends OAuthError {
27
+ constructor(provider: string);
28
+ }
29
+ export declare class EmailInUseError extends OAuthError {
30
+ constructor(message?: string);
31
+ }
32
+ export declare class IdentityInUseError extends OAuthError {
33
+ constructor(message?: string);
34
+ }
35
+ export declare class NotAuthenticatedError extends OAuthError {
36
+ constructor(message?: string);
37
+ }
38
+ export declare class FlowNotEnabledError extends OAuthError {
39
+ constructor(message?: string);
40
+ }
41
+ export declare class CannotUnlinkLastIdentityError extends OAuthError {
42
+ constructor(message?: string);
43
+ }
44
+ export declare class UntrustedOriginError extends OAuthError {
45
+ constructor(message?: string);
46
+ }
47
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,eAAe,GACf,gBAAgB,GAChB,kBAAkB,GAClB,cAAc,GACd,iBAAiB,GACjB,mBAAmB,GACnB,kBAAkB,GAClB,6BAA6B,GAC7B,kBAAkB,GAClB,cAAc,CAAA;AAElB;;;;GAIG;AACH,qBAAa,UAAW,SAAQ,KAAK;IACnC,IAAI,EAAE,cAAc,CAAA;gBAER,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM;CAKlD;AAED,qBAAa,oBAAqB,SAAQ,UAAU;gBACtC,QAAQ,EAAE,MAAM;CAI7B;AAED,qBAAa,iBAAkB,SAAQ,UAAU;gBAE7C,OAAO,SAA0E;CAKpF;AAED,qBAAa,aAAc,SAAQ,UAAU;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,oBAAqB,SAAQ,UAAU;gBACtC,QAAQ,EAAE,MAAM;CAO7B;AAED,qBAAa,eAAgB,SAAQ,UAAU;gBACjC,OAAO,SAAsD;CAI1E;AAED,qBAAa,kBAAmB,SAAQ,UAAU;gBACpC,OAAO,SAAuD;CAI3E;AAED,qBAAa,qBAAsB,SAAQ,UAAU;gBACvC,OAAO,SAAuD;CAI3E;AAED,qBAAa,mBAAoB,SAAQ,UAAU;gBACrC,OAAO,SAAmC;CAIvD;AAED,qBAAa,6BAA8B,SAAQ,UAAU;gBAEzD,OAAO,SAAyE;CAKnF;AAED,qBAAa,oBAAqB,SAAQ,UAAU;gBACtC,OAAO,SAAkC;CAItD"}
package/dist/errors.js ADDED
@@ -0,0 +1,84 @@
1
+ class OAuthError extends Error {
2
+ code;
3
+ constructor(code, message) {
4
+ super(message);
5
+ this.name = "OAuthError";
6
+ this.code = code;
7
+ }
8
+ }
9
+ class UnknownProviderError extends OAuthError {
10
+ constructor(provider) {
11
+ super("unknown_provider", `No OAuth provider configured for '${provider}'`);
12
+ this.name = "UnknownProviderError";
13
+ }
14
+ }
15
+ class InvalidStateError extends OAuthError {
16
+ constructor(message = "OAuth transaction cookie is missing, expired, or state does not match") {
17
+ super("invalid_state", message);
18
+ this.name = "InvalidStateError";
19
+ }
20
+ }
21
+ class ProviderError extends OAuthError {
22
+ constructor(message) {
23
+ super("provider_error", message);
24
+ this.name = "ProviderError";
25
+ }
26
+ }
27
+ class UnknownIdentityError extends OAuthError {
28
+ constructor(provider) {
29
+ super(
30
+ "unknown_identity",
31
+ `No account is linked to this ${provider} identity`
32
+ );
33
+ this.name = "UnknownIdentityError";
34
+ }
35
+ }
36
+ class EmailInUseError extends OAuthError {
37
+ constructor(message = "An account already exists with this email address") {
38
+ super("email_in_use", message);
39
+ this.name = "EmailInUseError";
40
+ }
41
+ }
42
+ class IdentityInUseError extends OAuthError {
43
+ constructor(message = "This identity is already linked to another account") {
44
+ super("identity_in_use", message);
45
+ this.name = "IdentityInUseError";
46
+ }
47
+ }
48
+ class NotAuthenticatedError extends OAuthError {
49
+ constructor(message = "You must be logged in to link or unlink an account") {
50
+ super("not_authenticated", message);
51
+ this.name = "NotAuthenticatedError";
52
+ }
53
+ }
54
+ class FlowNotEnabledError extends OAuthError {
55
+ constructor(message = "This OAuth flow is not enabled") {
56
+ super("flow_not_enabled", message);
57
+ this.name = "FlowNotEnabledError";
58
+ }
59
+ }
60
+ class CannotUnlinkLastIdentityError extends OAuthError {
61
+ constructor(message = "Cannot unlink the last identity from an account with no password set") {
62
+ super("cannot_unlink_last_identity", message);
63
+ this.name = "CannotUnlinkLastIdentityError";
64
+ }
65
+ }
66
+ class UntrustedOriginError extends OAuthError {
67
+ constructor(message = "Request origin is not trusted") {
68
+ super("untrusted_origin", message);
69
+ this.name = "UntrustedOriginError";
70
+ }
71
+ }
72
+ export {
73
+ CannotUnlinkLastIdentityError,
74
+ EmailInUseError,
75
+ FlowNotEnabledError,
76
+ IdentityInUseError,
77
+ InvalidStateError,
78
+ NotAuthenticatedError,
79
+ OAuthError,
80
+ ProviderError,
81
+ UnknownIdentityError,
82
+ UnknownProviderError,
83
+ UntrustedOriginError
84
+ };