@shware/security 7.1.1 → 8.0.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/README.md +108 -30
- package/dist/auth/attributes.cjs +4 -3
- package/dist/auth/attributes.cjs.map +1 -1
- package/dist/auth/attributes.d.cts +3 -3
- package/dist/auth/attributes.d.cts.map +1 -1
- package/dist/auth/attributes.d.mts +3 -3
- package/dist/auth/attributes.d.mts.map +1 -1
- package/dist/auth/attributes.mjs +4 -3
- package/dist/auth/attributes.mjs.map +1 -1
- package/dist/auth/desktop-codes.cjs +57 -0
- package/dist/auth/desktop-codes.cjs.map +1 -0
- package/dist/auth/desktop-codes.d.cts +24 -0
- package/dist/auth/desktop-codes.d.cts.map +1 -0
- package/dist/auth/desktop-codes.d.mts +24 -0
- package/dist/auth/desktop-codes.d.mts.map +1 -0
- package/dist/auth/desktop-codes.mjs +55 -0
- package/dist/auth/desktop-codes.mjs.map +1 -0
- package/dist/auth/email-codes.cjs +77 -0
- package/dist/auth/email-codes.cjs.map +1 -0
- package/dist/auth/email-codes.d.cts +39 -0
- package/dist/auth/email-codes.d.cts.map +1 -0
- package/dist/auth/email-codes.d.mts +39 -0
- package/dist/auth/email-codes.d.mts.map +1 -0
- package/dist/auth/email-codes.mjs +75 -0
- package/dist/auth/email-codes.mjs.map +1 -0
- package/dist/auth/error.cjs +47 -0
- package/dist/auth/error.cjs.map +1 -0
- package/dist/auth/error.d.cts +88 -0
- package/dist/auth/error.d.cts.map +1 -0
- package/dist/auth/error.d.mts +88 -0
- package/dist/auth/error.d.mts.map +1 -0
- package/dist/auth/error.mjs +45 -0
- package/dist/auth/error.mjs.map +1 -0
- package/dist/auth/index.cjs +244 -383
- package/dist/auth/index.cjs.map +1 -1
- package/dist/auth/index.d.cts +66 -55
- package/dist/auth/index.d.cts.map +1 -1
- package/dist/auth/index.d.mts +66 -55
- package/dist/auth/index.d.mts.map +1 -1
- package/dist/auth/index.mjs +247 -386
- package/dist/auth/index.mjs.map +1 -1
- package/dist/auth/respond.cjs +99 -0
- package/dist/auth/respond.cjs.map +1 -0
- package/dist/auth/respond.d.cts +39 -0
- package/dist/auth/respond.d.cts.map +1 -0
- package/dist/auth/respond.d.mts +39 -0
- package/dist/auth/respond.d.mts.map +1 -0
- package/dist/auth/respond.mjs +92 -0
- package/dist/auth/respond.mjs.map +1 -0
- package/dist/auth/types.d.cts +27 -11
- package/dist/auth/types.d.cts.map +1 -1
- package/dist/auth/types.d.mts +27 -11
- package/dist/auth/types.d.mts.map +1 -1
- package/dist/index.cjs +5 -0
- package/dist/index.d.cts +4 -1
- package/dist/index.d.mts +4 -1
- package/dist/index.mjs +3 -1
- package/dist/oauth2/error.cjs +0 -7
- package/dist/oauth2/error.cjs.map +1 -1
- package/dist/oauth2/error.d.cts +0 -3
- package/dist/oauth2/error.d.cts.map +1 -1
- package/dist/oauth2/error.d.mts +0 -3
- package/dist/oauth2/error.d.mts.map +1 -1
- package/dist/oauth2/error.mjs +1 -7
- package/dist/oauth2/error.mjs.map +1 -1
- package/dist/oauth2/provider/common.cjs +1 -1
- package/dist/oauth2/provider/common.cjs.map +1 -1
- package/dist/oauth2/provider/common.mjs +1 -1
- package/dist/oauth2/provider/common.mjs.map +1 -1
- package/dist/oauth2/provider/facebook.cjs +2 -4
- package/dist/oauth2/provider/facebook.cjs.map +1 -1
- package/dist/oauth2/provider/facebook.d.cts +0 -1
- package/dist/oauth2/provider/facebook.d.cts.map +1 -1
- package/dist/oauth2/provider/facebook.d.mts +0 -1
- package/dist/oauth2/provider/facebook.d.mts.map +1 -1
- package/dist/oauth2/provider/facebook.mjs +2 -4
- package/dist/oauth2/provider/facebook.mjs.map +1 -1
- package/dist/oauth2/provider/x.cjs +6 -4
- package/dist/oauth2/provider/x.cjs.map +1 -1
- package/dist/oauth2/provider/x.d.cts +3 -2
- package/dist/oauth2/provider/x.d.cts.map +1 -1
- package/dist/oauth2/provider/x.d.mts +3 -2
- package/dist/oauth2/provider/x.d.mts.map +1 -1
- package/dist/oauth2/provider/x.mjs +6 -4
- package/dist/oauth2/provider/x.mjs.map +1 -1
- package/dist/oauth2/types.cjs.map +1 -1
- package/dist/oauth2/types.d.cts +5 -0
- package/dist/oauth2/types.d.cts.map +1 -1
- package/dist/oauth2/types.d.mts +5 -0
- package/dist/oauth2/types.d.mts.map +1 -1
- package/dist/oauth2/types.mjs.map +1 -1
- package/dist/session/common.cjs +10 -13
- package/dist/session/common.cjs.map +1 -1
- package/dist/session/common.d.cts +7 -3
- package/dist/session/common.d.cts.map +1 -1
- package/dist/session/common.d.mts +7 -3
- package/dist/session/common.d.mts.map +1 -1
- package/dist/session/common.mjs +10 -12
- package/dist/session/common.mjs.map +1 -1
- package/dist/session/db.cjs +2 -2
- package/dist/session/db.cjs.map +1 -1
- package/dist/session/db.mjs +3 -3
- package/dist/session/db.mjs.map +1 -1
- package/dist/session/redis.cjs +6 -8
- package/dist/session/redis.cjs.map +1 -1
- package/dist/session/redis.d.cts.map +1 -1
- package/dist/session/redis.d.mts.map +1 -1
- package/dist/session/redis.mjs +7 -9
- package/dist/session/redis.mjs.map +1 -1
- package/dist/session/types.d.cts +0 -12
- package/dist/session/types.d.cts.map +1 -1
- package/dist/session/types.d.mts +0 -12
- package/dist/session/types.d.mts.map +1 -1
- package/dist/utils/valid.cjs +34 -52
- package/dist/utils/valid.cjs.map +1 -1
- package/dist/utils/valid.d.cts +8 -23
- package/dist/utils/valid.d.cts.map +1 -1
- package/dist/utils/valid.d.mts +8 -23
- package/dist/utils/valid.d.mts.map +1 -1
- package/dist/utils/valid.mjs +32 -50
- package/dist/utils/valid.mjs.map +1 -1
- package/package.json +2 -2
package/dist/auth/types.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Principal } from "../core/index.cjs";
|
|
2
2
|
import { KVRepository, Session, SessionRepository } from "../session/types.cjs";
|
|
3
|
+
import { EmailSender } from "./email-codes.cjs";
|
|
3
4
|
import { OAuth2ClientConfig, OAuth2Token, UserInfo } from "../oauth2/types.cjs";
|
|
4
5
|
import { CookieOptions } from "../utils/http.cjs";
|
|
5
6
|
export type LoggedHandler = (principal: Principal) => Promise<void>;
|
|
@@ -24,7 +25,11 @@ export interface Authorization {
|
|
|
24
25
|
token?: OAuth2Token;
|
|
25
26
|
attributes: Record<string, string>;
|
|
26
27
|
}
|
|
27
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Creates or updates the user and returns the principal whose `name` the session stores. Throw an
|
|
30
|
+
* `AuthenticationError` to refuse the sign-in with a reason the client can act on; any other error
|
|
31
|
+
* is treated as a server fault.
|
|
32
|
+
*/
|
|
28
33
|
export type AuthorizedHandler = (request: Request, authorization: Authorization) => Promise<Principal>;
|
|
29
34
|
export interface OAuth2AuthorizationOptions {
|
|
30
35
|
/**
|
|
@@ -56,13 +61,24 @@ export interface AuthConfig {
|
|
|
56
61
|
* 3600 (an hour); `0` writes on every authenticated request.
|
|
57
62
|
*/
|
|
58
63
|
touchInterval?: number;
|
|
64
|
+
/**
|
|
65
|
+
* Also carry the session id in the `X-Auth-Token` header, for clients without a cookie jar
|
|
66
|
+
* (a CLI, a desktop shell), as Spring Session's `HeaderHttpSessionIdResolver` does: every
|
|
67
|
+
* response that sets the session cookie (a login, `logged`) also answers `X-Auth-Token: <id>`,
|
|
68
|
+
* and a request without the cookie is read from the same header. Default `false`. Turning it
|
|
69
|
+
* on exposes the id to same-origin scripts, which the cookie's `HttpOnly` kept it from, and a
|
|
70
|
+
* cross-origin client needs the header in `Access-Control-Expose-Headers`. The header is for
|
|
71
|
+
* the session id only: an API token for programmatic access is a different credential and
|
|
72
|
+
* is not read here.
|
|
73
|
+
*/
|
|
74
|
+
header?: boolean;
|
|
59
75
|
};
|
|
60
76
|
turnstileSecretKey?: string;
|
|
61
77
|
/** Limits of email verification codes (a code itself is valid for 10 minutes). */
|
|
62
78
|
verificationCode?: {
|
|
63
79
|
/**
|
|
64
80
|
* Wrong guesses a code survives: once a login exceeds this many attempts the code is discarded
|
|
65
|
-
* and `loginEmail` answers `VERIFICATION_CODE_ATTEMPTS_EXCEEDED`. Default 5.
|
|
81
|
+
* and `loginEmail` answers `403 VERIFICATION_CODE_ATTEMPTS_EXCEEDED`. Default 5.
|
|
66
82
|
*/
|
|
67
83
|
maxAttempts?: number;
|
|
68
84
|
/**
|
|
@@ -96,16 +112,16 @@ export interface AuthService {
|
|
|
96
112
|
loginOAuth2Code: (request: Request, onAuthorized: AuthorizedHandler) => Promise<Response>;
|
|
97
113
|
loginOAuth2Native: (request: Request, onAuthorized: AuthorizedHandler) => Promise<Response>;
|
|
98
114
|
loginOAuth2Onetap: (request: Request, onAuthorized: AuthorizedHandler, provider?: string) => Promise<Response>;
|
|
99
|
-
sendEmailVerificationCode: (request: Request, sender:
|
|
100
|
-
email: string;
|
|
101
|
-
verificationCode: string;
|
|
102
|
-
}) => Promise<void>) => Promise<Response>;
|
|
103
|
-
sendPhoneVerificationCode: (request: Request, sender: (data: {
|
|
104
|
-
phone: string;
|
|
105
|
-
verificationCode: string;
|
|
106
|
-
}) => Promise<void>) => Promise<Response>;
|
|
115
|
+
sendEmailVerificationCode: (request: Request, sender: EmailSender) => Promise<Response>;
|
|
107
116
|
loginEmail: (request: Request, onAuthorized: AuthorizedHandler) => Promise<Response>;
|
|
108
|
-
|
|
117
|
+
/**
|
|
118
|
+
* Spends a mailed code without signing in, for a host flow that only needs proof of the address
|
|
119
|
+
* (verifying or changing a signed-in user's email). Throws `AuthenticationError`
|
|
120
|
+
* (`INVALID_VERIFICATION_CODE`, `VERIFICATION_CODE_ATTEMPTS_EXCEEDED`) as `loginEmail` answers.
|
|
121
|
+
*/
|
|
122
|
+
verifyEmailCode: (email: string, verificationCode: string) => Promise<void>;
|
|
123
|
+
desktopAuthorize: (request: Request) => Promise<Response>;
|
|
124
|
+
desktopExchange: (request: Request) => Promise<Response>;
|
|
109
125
|
kick: (principal: Principal) => Promise<void>;
|
|
110
126
|
isAuthenticated: (request: Request) => Promise<boolean>;
|
|
111
127
|
getSession: <T extends boolean>(request: Request, create: T) => Promise<T extends true ? Session : Session | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.cts","names":[],"sources":["../../src/auth/types.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.cts","names":[],"sources":["../../src/auth/types.ts"],"mappings":";;;;;YAMY,iBAAiB,WAAW,cAAc;;;;;;;;;;;;;;;iBAerC;EACf;EACA;EACA,UAAU;EACV,QAAQ;EACR,YAAY;;;;;;;YAQF,qBACV,SAAS,SACT,eAAe,kBACZ,QAAQ;iBAEI;;;;;;;;;;;EAWf,aAAa;;iBAGE;EACf;EACA,cAAc;EACd,mBAAmB;EACnB,SAAS;IAAkB;;EAC3B;;;;;;;;;IASE;;;;;;;;;;;IAWA;;EAEF;;EAEA;;;;;IAKE;;;;;IAKA;;EAEF;IACE,SAAS;;IAET;;;;;;;;MAQE;;;;iBAKW;EAEf,OAAO,SAAS,YAAY,QAAQ;EACpC,SAAS,SAAS,YAAY,QAAQ;EACtC,SAAS,SAAS,SAAS,WAAW,kBAAkB,QAAQ;EAGhE,cAAc,SAAS,YAAY,QAAQ;EAC3C,cAAc,SAAS,YAAY,QAAQ;EAC3C,sBACE,SAAS,SACT,UAAU,+BACP,QAAQ;EACb,kBAAkB,SAAS,SAAS,cAAc,sBAAsB,QAAQ;EAChF,oBAAoB,SAAS,SAAS,cAAc,sBAAsB,QAAQ;EAClF,oBACE,SAAS,SACT,cAAc,mBACd,sBACG,QAAQ;EAGb,4BAA4B,SAAS,SAAS,QAAQ,gBAAgB,QAAQ;EAC9E,aAAa,SAAS,SAAS,cAAc,sBAAsB,QAAQ;;;;;;EAM3E,kBAAkB,eAAe,6BAA6B;EAG9D,mBAAmB,SAAS,YAAY,QAAQ;EAChD,kBAAkB,SAAS,YAAY,QAAQ;EAG/C,OAAO,WAAW,cAAc;EAChC,kBAAkB,SAAS,YAAY;EACvC,aAAa,mBACX,SAAS,SACT,QAAQ,MACL,QAAQ,iBAAiB,UAAU;EACxC,gBAAgB,sBAAsB;EACtC,eAAe,SAAS,YAAY,QAAQ;EAC5C,eAAe,WAAW,cAAc,QAAQ;EAChD,yBAAyB,0BAA0B,QAAQ"}
|
package/dist/auth/types.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Principal } from "../core/index.mjs";
|
|
2
2
|
import { KVRepository, Session, SessionRepository } from "../session/types.mjs";
|
|
3
|
+
import { EmailSender } from "./email-codes.mjs";
|
|
3
4
|
import { OAuth2ClientConfig, OAuth2Token, UserInfo } from "../oauth2/types.mjs";
|
|
4
5
|
import { CookieOptions } from "../utils/http.mjs";
|
|
5
6
|
export type LoggedHandler = (principal: Principal) => Promise<void>;
|
|
@@ -24,7 +25,11 @@ export interface Authorization {
|
|
|
24
25
|
token?: OAuth2Token;
|
|
25
26
|
attributes: Record<string, string>;
|
|
26
27
|
}
|
|
27
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Creates or updates the user and returns the principal whose `name` the session stores. Throw an
|
|
30
|
+
* `AuthenticationError` to refuse the sign-in with a reason the client can act on; any other error
|
|
31
|
+
* is treated as a server fault.
|
|
32
|
+
*/
|
|
28
33
|
export type AuthorizedHandler = (request: Request, authorization: Authorization) => Promise<Principal>;
|
|
29
34
|
export interface OAuth2AuthorizationOptions {
|
|
30
35
|
/**
|
|
@@ -56,13 +61,24 @@ export interface AuthConfig {
|
|
|
56
61
|
* 3600 (an hour); `0` writes on every authenticated request.
|
|
57
62
|
*/
|
|
58
63
|
touchInterval?: number;
|
|
64
|
+
/**
|
|
65
|
+
* Also carry the session id in the `X-Auth-Token` header, for clients without a cookie jar
|
|
66
|
+
* (a CLI, a desktop shell), as Spring Session's `HeaderHttpSessionIdResolver` does: every
|
|
67
|
+
* response that sets the session cookie (a login, `logged`) also answers `X-Auth-Token: <id>`,
|
|
68
|
+
* and a request without the cookie is read from the same header. Default `false`. Turning it
|
|
69
|
+
* on exposes the id to same-origin scripts, which the cookie's `HttpOnly` kept it from, and a
|
|
70
|
+
* cross-origin client needs the header in `Access-Control-Expose-Headers`. The header is for
|
|
71
|
+
* the session id only: an API token for programmatic access is a different credential and
|
|
72
|
+
* is not read here.
|
|
73
|
+
*/
|
|
74
|
+
header?: boolean;
|
|
59
75
|
};
|
|
60
76
|
turnstileSecretKey?: string;
|
|
61
77
|
/** Limits of email verification codes (a code itself is valid for 10 minutes). */
|
|
62
78
|
verificationCode?: {
|
|
63
79
|
/**
|
|
64
80
|
* Wrong guesses a code survives: once a login exceeds this many attempts the code is discarded
|
|
65
|
-
* and `loginEmail` answers `VERIFICATION_CODE_ATTEMPTS_EXCEEDED`. Default 5.
|
|
81
|
+
* and `loginEmail` answers `403 VERIFICATION_CODE_ATTEMPTS_EXCEEDED`. Default 5.
|
|
66
82
|
*/
|
|
67
83
|
maxAttempts?: number;
|
|
68
84
|
/**
|
|
@@ -96,16 +112,16 @@ export interface AuthService {
|
|
|
96
112
|
loginOAuth2Code: (request: Request, onAuthorized: AuthorizedHandler) => Promise<Response>;
|
|
97
113
|
loginOAuth2Native: (request: Request, onAuthorized: AuthorizedHandler) => Promise<Response>;
|
|
98
114
|
loginOAuth2Onetap: (request: Request, onAuthorized: AuthorizedHandler, provider?: string) => Promise<Response>;
|
|
99
|
-
sendEmailVerificationCode: (request: Request, sender:
|
|
100
|
-
email: string;
|
|
101
|
-
verificationCode: string;
|
|
102
|
-
}) => Promise<void>) => Promise<Response>;
|
|
103
|
-
sendPhoneVerificationCode: (request: Request, sender: (data: {
|
|
104
|
-
phone: string;
|
|
105
|
-
verificationCode: string;
|
|
106
|
-
}) => Promise<void>) => Promise<Response>;
|
|
115
|
+
sendEmailVerificationCode: (request: Request, sender: EmailSender) => Promise<Response>;
|
|
107
116
|
loginEmail: (request: Request, onAuthorized: AuthorizedHandler) => Promise<Response>;
|
|
108
|
-
|
|
117
|
+
/**
|
|
118
|
+
* Spends a mailed code without signing in, for a host flow that only needs proof of the address
|
|
119
|
+
* (verifying or changing a signed-in user's email). Throws `AuthenticationError`
|
|
120
|
+
* (`INVALID_VERIFICATION_CODE`, `VERIFICATION_CODE_ATTEMPTS_EXCEEDED`) as `loginEmail` answers.
|
|
121
|
+
*/
|
|
122
|
+
verifyEmailCode: (email: string, verificationCode: string) => Promise<void>;
|
|
123
|
+
desktopAuthorize: (request: Request) => Promise<Response>;
|
|
124
|
+
desktopExchange: (request: Request) => Promise<Response>;
|
|
109
125
|
kick: (principal: Principal) => Promise<void>;
|
|
110
126
|
isAuthenticated: (request: Request) => Promise<boolean>;
|
|
111
127
|
getSession: <T extends boolean>(request: Request, create: T) => Promise<T extends true ? Session : Session | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../../src/auth/types.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.mts","names":[],"sources":["../../src/auth/types.ts"],"mappings":";;;;;YAMY,iBAAiB,WAAW,cAAc;;;;;;;;;;;;;;;iBAerC;EACf;EACA;EACA,UAAU;EACV,QAAQ;EACR,YAAY;;;;;;;YAQF,qBACV,SAAS,SACT,eAAe,kBACZ,QAAQ;iBAEI;;;;;;;;;;;EAWf,aAAa;;iBAGE;EACf;EACA,cAAc;EACd,mBAAmB;EACnB,SAAS;IAAkB;;EAC3B;;;;;;;;;IASE;;;;;;;;;;;IAWA;;EAEF;;EAEA;;;;;IAKE;;;;;IAKA;;EAEF;IACE,SAAS;;IAET;;;;;;;;MAQE;;;;iBAKW;EAEf,OAAO,SAAS,YAAY,QAAQ;EACpC,SAAS,SAAS,YAAY,QAAQ;EACtC,SAAS,SAAS,SAAS,WAAW,kBAAkB,QAAQ;EAGhE,cAAc,SAAS,YAAY,QAAQ;EAC3C,cAAc,SAAS,YAAY,QAAQ;EAC3C,sBACE,SAAS,SACT,UAAU,+BACP,QAAQ;EACb,kBAAkB,SAAS,SAAS,cAAc,sBAAsB,QAAQ;EAChF,oBAAoB,SAAS,SAAS,cAAc,sBAAsB,QAAQ;EAClF,oBACE,SAAS,SACT,cAAc,mBACd,sBACG,QAAQ;EAGb,4BAA4B,SAAS,SAAS,QAAQ,gBAAgB,QAAQ;EAC9E,aAAa,SAAS,SAAS,cAAc,sBAAsB,QAAQ;;;;;;EAM3E,kBAAkB,eAAe,6BAA6B;EAG9D,mBAAmB,SAAS,YAAY,QAAQ;EAChD,kBAAkB,SAAS,YAAY,QAAQ;EAG/C,OAAO,WAAW,cAAc;EAChC,kBAAkB,SAAS,YAAY;EACvC,aAAa,mBACX,SAAS,SACT,QAAQ,MACL,QAAQ,iBAAiB,UAAU;EACxC,gBAAgB,sBAAsB;EACtC,eAAe,SAAS,YAAY,QAAQ;EAC5C,eAAe,WAAW,cAAc,QAAQ;EAChD,yBAAyB,0BAA0B,QAAQ"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_auth_error = require("./auth/error.cjs");
|
|
2
3
|
const require_oauth2_error = require("./oauth2/error.cjs");
|
|
4
|
+
const require_auth_respond = require("./auth/respond.cjs");
|
|
3
5
|
const require_oauth2_client = require("./oauth2/client.cjs");
|
|
4
6
|
const require_session_common = require("./session/common.cjs");
|
|
5
7
|
const require_auth_index = require("./auth/index.cjs");
|
|
@@ -8,7 +10,9 @@ const require_session_db = require("./session/db.cjs");
|
|
|
8
10
|
const require_core_index = require("./core/index.cjs");
|
|
9
11
|
const require_oauth2_types = require("./oauth2/types.cjs");
|
|
10
12
|
const require_oauth2_name = require("./oauth2/name.cjs");
|
|
13
|
+
exports.AUTHENTICATION_REASONS = require_auth_error.AUTHENTICATION_REASONS;
|
|
11
14
|
exports.Auth = require_auth_index.Auth;
|
|
15
|
+
exports.AuthenticationError = require_auth_error.AuthenticationError;
|
|
12
16
|
exports.DBIndexedSessionRepository = require_session_db.DBIndexedSessionRepository;
|
|
13
17
|
exports.MAX_NAME_PART_LENGTH = require_oauth2_name.MAX_NAME_PART_LENGTH;
|
|
14
18
|
exports.OAuth2Client = require_oauth2_client.OAuth2Client;
|
|
@@ -20,4 +24,5 @@ exports.PROVIDERS = require_core_index.PROVIDERS;
|
|
|
20
24
|
exports.RedisIndexedSessionRepository = require_session_redis.RedisIndexedSessionRepository;
|
|
21
25
|
exports.RedisKVRepository = require_session_redis.RedisKVRepository;
|
|
22
26
|
exports.composeName = require_oauth2_name.composeName;
|
|
27
|
+
exports.errorResponse = require_auth_respond.errorResponse;
|
|
23
28
|
exports.oauth2RedirectQuerySchema = require_oauth2_client.oauth2RedirectQuerySchema;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { CsrfToken, PROVIDERS, Principal, Provider } from "./core/index.cjs";
|
|
2
2
|
import { KVRepository, Namespace, Session, SessionRepository } from "./session/types.cjs";
|
|
3
|
+
import { EmailSender } from "./auth/email-codes.cjs";
|
|
4
|
+
import { AUTHENTICATION_REASONS, AuthenticationError, AuthenticationReason, AuthenticationReasons } from "./auth/error.cjs";
|
|
3
5
|
import { AuthorizationResponse, ClientSecret, NativeClient, NativeCredential, OAuth2ClientConfig, OAuth2Token, OIDC_SCOPES, OidcScope, OidcToken, PersonNameComponents, ProviderConfig, StandardClaims, UserInfo, WebClient } from "./oauth2/types.cjs";
|
|
4
6
|
import { AuthConfig, AuthService, Authorization, AuthorizedHandler, OAuth2AuthorizationOptions } from "./auth/types.cjs";
|
|
5
7
|
import { Auth, PATH } from "./auth/index.cjs";
|
|
8
|
+
import { ErrorBody, errorResponse } from "./auth/respond.cjs";
|
|
6
9
|
import { OAuth2Client, oauth2RedirectQuerySchema } from "./oauth2/client.cjs";
|
|
7
10
|
import { OAuth2Error, OAuth2ErrorType } from "./oauth2/error.cjs";
|
|
8
11
|
import { RedisIndexedSessionRepository, RedisKVRepository } from "./session/redis.cjs";
|
|
9
12
|
import { DBAdapter, DBIndexedSessionRepository } from "./session/db.cjs";
|
|
10
13
|
import { PRINCIPAL_NAME_INDEX_NAME } from "./session/common.cjs";
|
|
11
14
|
import { MAX_NAME_PART_LENGTH, composeName } from "./oauth2/name.cjs";
|
|
12
|
-
export { Auth, type AuthConfig, type AuthService, type Authorization, type AuthorizationResponse, type AuthorizedHandler, type ClientSecret, type CsrfToken, type DBAdapter, DBIndexedSessionRepository, type KVRepository, MAX_NAME_PART_LENGTH, type Namespace, type NativeClient, type NativeCredential, type OAuth2AuthorizationOptions, OAuth2Client, type OAuth2ClientConfig, OAuth2Error, type OAuth2ErrorType, type OAuth2Token, OIDC_SCOPES, type OidcScope, type OidcToken, PATH, PRINCIPAL_NAME_INDEX_NAME, PROVIDERS, type PersonNameComponents, type Principal, type Provider, type ProviderConfig, RedisIndexedSessionRepository, RedisKVRepository, type Session, type SessionRepository, type StandardClaims, type UserInfo, type WebClient, composeName, oauth2RedirectQuerySchema };
|
|
15
|
+
export { AUTHENTICATION_REASONS, Auth, type AuthConfig, type AuthService, AuthenticationError, type AuthenticationReason, type AuthenticationReasons, type Authorization, type AuthorizationResponse, type AuthorizedHandler, type ClientSecret, type CsrfToken, type DBAdapter, DBIndexedSessionRepository, type EmailSender, type ErrorBody, type KVRepository, MAX_NAME_PART_LENGTH, type Namespace, type NativeClient, type NativeCredential, type OAuth2AuthorizationOptions, OAuth2Client, type OAuth2ClientConfig, OAuth2Error, type OAuth2ErrorType, type OAuth2Token, OIDC_SCOPES, type OidcScope, type OidcToken, PATH, PRINCIPAL_NAME_INDEX_NAME, PROVIDERS, type PersonNameComponents, type Principal, type Provider, type ProviderConfig, RedisIndexedSessionRepository, RedisKVRepository, type Session, type SessionRepository, type StandardClaims, type UserInfo, type WebClient, composeName, errorResponse, oauth2RedirectQuerySchema };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { CsrfToken, PROVIDERS, Principal, Provider } from "./core/index.mjs";
|
|
2
2
|
import { KVRepository, Namespace, Session, SessionRepository } from "./session/types.mjs";
|
|
3
|
+
import { EmailSender } from "./auth/email-codes.mjs";
|
|
4
|
+
import { AUTHENTICATION_REASONS, AuthenticationError, AuthenticationReason, AuthenticationReasons } from "./auth/error.mjs";
|
|
3
5
|
import { AuthorizationResponse, ClientSecret, NativeClient, NativeCredential, OAuth2ClientConfig, OAuth2Token, OIDC_SCOPES, OidcScope, OidcToken, PersonNameComponents, ProviderConfig, StandardClaims, UserInfo, WebClient } from "./oauth2/types.mjs";
|
|
4
6
|
import { AuthConfig, AuthService, Authorization, AuthorizedHandler, OAuth2AuthorizationOptions } from "./auth/types.mjs";
|
|
5
7
|
import { Auth, PATH } from "./auth/index.mjs";
|
|
8
|
+
import { ErrorBody, errorResponse } from "./auth/respond.mjs";
|
|
6
9
|
import { OAuth2Client, oauth2RedirectQuerySchema } from "./oauth2/client.mjs";
|
|
7
10
|
import { OAuth2Error, OAuth2ErrorType } from "./oauth2/error.mjs";
|
|
8
11
|
import { RedisIndexedSessionRepository, RedisKVRepository } from "./session/redis.mjs";
|
|
9
12
|
import { DBAdapter, DBIndexedSessionRepository } from "./session/db.mjs";
|
|
10
13
|
import { PRINCIPAL_NAME_INDEX_NAME } from "./session/common.mjs";
|
|
11
14
|
import { MAX_NAME_PART_LENGTH, composeName } from "./oauth2/name.mjs";
|
|
12
|
-
export { Auth, type AuthConfig, type AuthService, type Authorization, type AuthorizationResponse, type AuthorizedHandler, type ClientSecret, type CsrfToken, type DBAdapter, DBIndexedSessionRepository, type KVRepository, MAX_NAME_PART_LENGTH, type Namespace, type NativeClient, type NativeCredential, type OAuth2AuthorizationOptions, OAuth2Client, type OAuth2ClientConfig, OAuth2Error, type OAuth2ErrorType, type OAuth2Token, OIDC_SCOPES, type OidcScope, type OidcToken, PATH, PRINCIPAL_NAME_INDEX_NAME, PROVIDERS, type PersonNameComponents, type Principal, type Provider, type ProviderConfig, RedisIndexedSessionRepository, RedisKVRepository, type Session, type SessionRepository, type StandardClaims, type UserInfo, type WebClient, composeName, oauth2RedirectQuerySchema };
|
|
15
|
+
export { AUTHENTICATION_REASONS, Auth, type AuthConfig, type AuthService, AuthenticationError, type AuthenticationReason, type AuthenticationReasons, type Authorization, type AuthorizationResponse, type AuthorizedHandler, type ClientSecret, type CsrfToken, type DBAdapter, DBIndexedSessionRepository, type EmailSender, type ErrorBody, type KVRepository, MAX_NAME_PART_LENGTH, type Namespace, type NativeClient, type NativeCredential, type OAuth2AuthorizationOptions, OAuth2Client, type OAuth2ClientConfig, OAuth2Error, type OAuth2ErrorType, type OAuth2Token, OIDC_SCOPES, type OidcScope, type OidcToken, PATH, PRINCIPAL_NAME_INDEX_NAME, PROVIDERS, type PersonNameComponents, type Principal, type Provider, type ProviderConfig, RedisIndexedSessionRepository, RedisKVRepository, type Session, type SessionRepository, type StandardClaims, type UserInfo, type WebClient, composeName, errorResponse, oauth2RedirectQuerySchema };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { AUTHENTICATION_REASONS, AuthenticationError } from "./auth/error.mjs";
|
|
1
2
|
import { OAuth2Error } from "./oauth2/error.mjs";
|
|
3
|
+
import { errorResponse } from "./auth/respond.mjs";
|
|
2
4
|
import { OAuth2Client, oauth2RedirectQuerySchema } from "./oauth2/client.mjs";
|
|
3
5
|
import { PRINCIPAL_NAME_INDEX_NAME } from "./session/common.mjs";
|
|
4
6
|
import { Auth, PATH } from "./auth/index.mjs";
|
|
@@ -7,4 +9,4 @@ import { DBIndexedSessionRepository } from "./session/db.mjs";
|
|
|
7
9
|
import { PROVIDERS } from "./core/index.mjs";
|
|
8
10
|
import { OIDC_SCOPES } from "./oauth2/types.mjs";
|
|
9
11
|
import { MAX_NAME_PART_LENGTH, composeName } from "./oauth2/name.mjs";
|
|
10
|
-
export { Auth, DBIndexedSessionRepository, MAX_NAME_PART_LENGTH, OAuth2Client, OAuth2Error, OIDC_SCOPES, PATH, PRINCIPAL_NAME_INDEX_NAME, PROVIDERS, RedisIndexedSessionRepository, RedisKVRepository, composeName, oauth2RedirectQuerySchema };
|
|
12
|
+
export { AUTHENTICATION_REASONS, Auth, AuthenticationError, DBIndexedSessionRepository, MAX_NAME_PART_LENGTH, OAuth2Client, OAuth2Error, OIDC_SCOPES, PATH, PRINCIPAL_NAME_INDEX_NAME, PROVIDERS, RedisIndexedSessionRepository, RedisKVRepository, composeName, errorResponse, oauth2RedirectQuerySchema };
|
package/dist/oauth2/error.cjs
CHANGED
|
@@ -29,13 +29,6 @@ var OAuth2Error = class OAuth2Error extends Error {
|
|
|
29
29
|
Object.setPrototypeOf(this, OAuth2Error.prototype);
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
|
-
var AuthenticationError = class AuthenticationError extends Error {
|
|
33
|
-
constructor(message) {
|
|
34
|
-
super(message);
|
|
35
|
-
Object.setPrototypeOf(this, AuthenticationError.prototype);
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
exports.AuthenticationError = AuthenticationError;
|
|
39
32
|
exports.OAuth2Error = OAuth2Error;
|
|
40
33
|
exports.isKnownOAuth2ErrorType = isKnownOAuth2ErrorType;
|
|
41
34
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.cjs","names":[],"sources":["../../src/oauth2/error.ts"],"sourcesContent":["// reference: https://datatracker.ietf.org/doc/html/rfc6749#section-4.2.2.1\nexport type OAuth2ErrorType =\n | 'invalid_request' // 400\n | 'invalid_client' // 401\n | 'invalid_grant' // 400\n | 'invalid_token' // 401\n | 'invalid_scope' // 400\n | 'unauthorized_client' // 401\n | 'unsupported_grant_type' // 400\n | 'unsupported_response_type' // 400\n | 'insufficient_scope' // 403\n | 'redirect_uri_mismatch' // 400\n | 'access_denied' // 400\n | 'server_error' // 500\n | 'temporarily_unavailable' // 503\n | (string & {});\n\n/** The registered error codes of `OAuth2ErrorType`, for checking untrusted input at runtime. */\nconst OAUTH2_ERROR_TYPES: ReadonlySet<string> = new Set([\n 'invalid_request',\n 'invalid_client',\n 'invalid_grant',\n 'invalid_token',\n 'invalid_scope',\n 'unauthorized_client',\n 'unsupported_grant_type',\n 'unsupported_response_type',\n 'insufficient_scope',\n 'redirect_uri_mismatch',\n 'access_denied',\n 'server_error',\n 'temporarily_unavailable',\n]);\n\nexport function isKnownOAuth2ErrorType(value: string): boolean {\n return OAUTH2_ERROR_TYPES.has(value);\n}\n\nexport class OAuth2Error extends Error {\n readonly status: number;\n readonly error: OAuth2ErrorType;\n\n constructor(status: number, error: OAuth2ErrorType, message?: string) {\n super(message ?? `OAuth2 error: ${error}`);\n this.status = status;\n this.error = error;\n Object.setPrototypeOf(this, OAuth2Error.prototype);\n }\n}\n
|
|
1
|
+
{"version":3,"file":"error.cjs","names":[],"sources":["../../src/oauth2/error.ts"],"sourcesContent":["// reference: https://datatracker.ietf.org/doc/html/rfc6749#section-4.2.2.1\nexport type OAuth2ErrorType =\n | 'invalid_request' // 400\n | 'invalid_client' // 401\n | 'invalid_grant' // 400\n | 'invalid_token' // 401\n | 'invalid_scope' // 400\n | 'unauthorized_client' // 401\n | 'unsupported_grant_type' // 400\n | 'unsupported_response_type' // 400\n | 'insufficient_scope' // 403\n | 'redirect_uri_mismatch' // 400\n | 'access_denied' // 400\n | 'server_error' // 500\n | 'temporarily_unavailable' // 503\n | (string & {});\n\n/** The registered error codes of `OAuth2ErrorType`, for checking untrusted input at runtime. */\nconst OAUTH2_ERROR_TYPES: ReadonlySet<string> = new Set([\n 'invalid_request',\n 'invalid_client',\n 'invalid_grant',\n 'invalid_token',\n 'invalid_scope',\n 'unauthorized_client',\n 'unsupported_grant_type',\n 'unsupported_response_type',\n 'insufficient_scope',\n 'redirect_uri_mismatch',\n 'access_denied',\n 'server_error',\n 'temporarily_unavailable',\n]);\n\nexport function isKnownOAuth2ErrorType(value: string): boolean {\n return OAUTH2_ERROR_TYPES.has(value);\n}\n\nexport class OAuth2Error extends Error {\n readonly status: number;\n readonly error: OAuth2ErrorType;\n\n constructor(status: number, error: OAuth2ErrorType, message?: string) {\n super(message ?? `OAuth2 error: ${error}`);\n this.status = status;\n this.error = error;\n Object.setPrototypeOf(this, OAuth2Error.prototype);\n }\n}\n"],"mappings":";;;AAkBA,MAAM,qCAA0C,IAAI,IAAI;CACtD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAgB,uBAAuB,OAAwB;CAC7D,OAAO,mBAAmB,IAAI,KAAK;AACrC;AAEA,IAAa,cAAb,MAAa,oBAAoB,MAAM;CACrC;CACA;CAEA,YAAY,QAAgB,OAAwB,SAAkB;EACpE,MAAM,WAAW,iBAAiB,OAAO;EACzC,KAAK,SAAS;EACd,KAAK,QAAQ;EACb,OAAO,eAAe,MAAM,YAAY,SAAS;CACnD;AACF"}
|
package/dist/oauth2/error.d.cts
CHANGED
|
@@ -5,7 +5,4 @@ export declare class OAuth2Error extends Error {
|
|
|
5
5
|
readonly error: OAuth2ErrorType;
|
|
6
6
|
constructor(status: number, error: OAuth2ErrorType, message?: string);
|
|
7
7
|
}
|
|
8
|
-
export declare class AuthenticationError extends Error {
|
|
9
|
-
constructor(message: string);
|
|
10
|
-
}
|
|
11
8
|
//# sourceMappingURL=error.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.d.cts","names":[],"sources":["../../src/oauth2/error.ts"],"mappings":"A,YACY;wBAiCI,uBAAuB;qBAI1B,oBAAoB;WACtB;WACA,OAAO;EAEhB,YAAY,gBAAgB,OAAO,iBAAiB
|
|
1
|
+
{"version":3,"file":"error.d.cts","names":[],"sources":["../../src/oauth2/error.ts"],"mappings":"A,YACY;wBAiCI,uBAAuB;qBAI1B,oBAAoB;WACtB;WACA,OAAO;EAEhB,YAAY,gBAAgB,OAAO,iBAAiB"}
|
package/dist/oauth2/error.d.mts
CHANGED
|
@@ -5,7 +5,4 @@ export declare class OAuth2Error extends Error {
|
|
|
5
5
|
readonly error: OAuth2ErrorType;
|
|
6
6
|
constructor(status: number, error: OAuth2ErrorType, message?: string);
|
|
7
7
|
}
|
|
8
|
-
export declare class AuthenticationError extends Error {
|
|
9
|
-
constructor(message: string);
|
|
10
|
-
}
|
|
11
8
|
//# sourceMappingURL=error.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.d.mts","names":[],"sources":["../../src/oauth2/error.ts"],"mappings":"A,YACY;wBAiCI,uBAAuB;qBAI1B,oBAAoB;WACtB;WACA,OAAO;EAEhB,YAAY,gBAAgB,OAAO,iBAAiB
|
|
1
|
+
{"version":3,"file":"error.d.mts","names":[],"sources":["../../src/oauth2/error.ts"],"mappings":"A,YACY;wBAiCI,uBAAuB;qBAI1B,oBAAoB;WACtB;WACA,OAAO;EAEhB,YAAY,gBAAgB,OAAO,iBAAiB"}
|
package/dist/oauth2/error.mjs
CHANGED
|
@@ -28,12 +28,6 @@ var OAuth2Error = class OAuth2Error extends Error {
|
|
|
28
28
|
Object.setPrototypeOf(this, OAuth2Error.prototype);
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
|
-
|
|
32
|
-
constructor(message) {
|
|
33
|
-
super(message);
|
|
34
|
-
Object.setPrototypeOf(this, AuthenticationError.prototype);
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
export { AuthenticationError, OAuth2Error, isKnownOAuth2ErrorType };
|
|
31
|
+
export { OAuth2Error, isKnownOAuth2ErrorType };
|
|
38
32
|
|
|
39
33
|
//# sourceMappingURL=error.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.mjs","names":[],"sources":["../../src/oauth2/error.ts"],"sourcesContent":["// reference: https://datatracker.ietf.org/doc/html/rfc6749#section-4.2.2.1\nexport type OAuth2ErrorType =\n | 'invalid_request' // 400\n | 'invalid_client' // 401\n | 'invalid_grant' // 400\n | 'invalid_token' // 401\n | 'invalid_scope' // 400\n | 'unauthorized_client' // 401\n | 'unsupported_grant_type' // 400\n | 'unsupported_response_type' // 400\n | 'insufficient_scope' // 403\n | 'redirect_uri_mismatch' // 400\n | 'access_denied' // 400\n | 'server_error' // 500\n | 'temporarily_unavailable' // 503\n | (string & {});\n\n/** The registered error codes of `OAuth2ErrorType`, for checking untrusted input at runtime. */\nconst OAUTH2_ERROR_TYPES: ReadonlySet<string> = new Set([\n 'invalid_request',\n 'invalid_client',\n 'invalid_grant',\n 'invalid_token',\n 'invalid_scope',\n 'unauthorized_client',\n 'unsupported_grant_type',\n 'unsupported_response_type',\n 'insufficient_scope',\n 'redirect_uri_mismatch',\n 'access_denied',\n 'server_error',\n 'temporarily_unavailable',\n]);\n\nexport function isKnownOAuth2ErrorType(value: string): boolean {\n return OAUTH2_ERROR_TYPES.has(value);\n}\n\nexport class OAuth2Error extends Error {\n readonly status: number;\n readonly error: OAuth2ErrorType;\n\n constructor(status: number, error: OAuth2ErrorType, message?: string) {\n super(message ?? `OAuth2 error: ${error}`);\n this.status = status;\n this.error = error;\n Object.setPrototypeOf(this, OAuth2Error.prototype);\n }\n}\n
|
|
1
|
+
{"version":3,"file":"error.mjs","names":[],"sources":["../../src/oauth2/error.ts"],"sourcesContent":["// reference: https://datatracker.ietf.org/doc/html/rfc6749#section-4.2.2.1\nexport type OAuth2ErrorType =\n | 'invalid_request' // 400\n | 'invalid_client' // 401\n | 'invalid_grant' // 400\n | 'invalid_token' // 401\n | 'invalid_scope' // 400\n | 'unauthorized_client' // 401\n | 'unsupported_grant_type' // 400\n | 'unsupported_response_type' // 400\n | 'insufficient_scope' // 403\n | 'redirect_uri_mismatch' // 400\n | 'access_denied' // 400\n | 'server_error' // 500\n | 'temporarily_unavailable' // 503\n | (string & {});\n\n/** The registered error codes of `OAuth2ErrorType`, for checking untrusted input at runtime. */\nconst OAUTH2_ERROR_TYPES: ReadonlySet<string> = new Set([\n 'invalid_request',\n 'invalid_client',\n 'invalid_grant',\n 'invalid_token',\n 'invalid_scope',\n 'unauthorized_client',\n 'unsupported_grant_type',\n 'unsupported_response_type',\n 'insufficient_scope',\n 'redirect_uri_mismatch',\n 'access_denied',\n 'server_error',\n 'temporarily_unavailable',\n]);\n\nexport function isKnownOAuth2ErrorType(value: string): boolean {\n return OAUTH2_ERROR_TYPES.has(value);\n}\n\nexport class OAuth2Error extends Error {\n readonly status: number;\n readonly error: OAuth2ErrorType;\n\n constructor(status: number, error: OAuth2ErrorType, message?: string) {\n super(message ?? `OAuth2 error: ${error}`);\n this.status = status;\n this.error = error;\n Object.setPrototypeOf(this, OAuth2Error.prototype);\n }\n}\n"],"mappings":";;AAkBA,MAAM,qCAA0C,IAAI,IAAI;CACtD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAgB,uBAAuB,OAAwB;CAC7D,OAAO,mBAAmB,IAAI,KAAK;AACrC;AAEA,IAAa,cAAb,MAAa,oBAAoB,MAAM;CACrC;CACA;CAEA,YAAY,QAAgB,OAAwB,SAAkB;EACpE,MAAM,WAAW,iBAAiB,OAAO;EACzC,KAAK,SAAS;EACd,KAAK,QAAQ;EACb,OAAO,eAAe,MAAM,YAAY,SAAS;CACnD;AACF"}
|
|
@@ -90,7 +90,7 @@ async function verifyIdToken(idToken, options) {
|
|
|
90
90
|
return payload;
|
|
91
91
|
} catch (error) {
|
|
92
92
|
console.error("Failed to verify id_token", error);
|
|
93
|
-
throw new require_oauth2_error.OAuth2Error(400, "
|
|
93
|
+
throw new require_oauth2_error.OAuth2Error(400, "invalid_grant", "Failed to verify id_token");
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
exports.createAuthorizationUri = createAuthorizationUri;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.cjs","names":["OAuth2Error","createRemoteJWKSet","jwtVerify"],"sources":["../../../src/oauth2/provider/common.ts"],"sourcesContent":["import { createRemoteJWKSet, jwtVerify } from 'jose';\nimport { OAuth2Error } from '../error';\nimport type { ExchangeCodeParams, PkceParameters, RefreshTokenParams } from '../types';\n\nexport function createAuthorizationUri(options: {\n state: string;\n scope: string[];\n clientId: string;\n redirectUri: string;\n authorizationUri: string;\n pkce?: Omit<PkceParameters, 'code_verifier'>;\n}) {\n const url = new URL(options.authorizationUri);\n url.searchParams.append('response_type', 'code');\n url.searchParams.append('client_id', options.clientId);\n url.searchParams.append('state', options.state);\n url.searchParams.append('scope', options.scope.join(' '));\n url.searchParams.append('redirect_uri', options.redirectUri);\n if (options.pkce) {\n url.searchParams.append('code_challenge_method', options.pkce.code_challenge_method);\n url.searchParams.append('code_challenge', options.pkce.code_challenge);\n }\n return url;\n}\n\nexport async function exchangeAuthorizationCode(\n params: ExchangeCodeParams & {\n tokenUri: string;\n authentication?: 'basic' | 'post';\n /** Extra request headers some providers require, e.g. GitHub's `User-Agent`. */\n headers?: Record<string, string>;\n }\n) {\n const body = new URLSearchParams();\n body.append('code', params.code);\n body.append('redirect_uri', params.redirectUri);\n body.append('grant_type', 'authorization_code');\n if (params.pkce) body.append('code_verifier', params.pkce.code_verifier);\n\n const headers: Record<string, string> = {\n ...params.headers,\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret ?? ''}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n // Public clients (Google's installed-app clients, PKCE-only clients) have no secret.\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n return fetch(params.tokenUri, { method: 'POST', headers, body });\n}\n\nexport async function refreshAccessToken(\n params: RefreshTokenParams & {\n tokenUri: string;\n authentication?: 'basic' | 'post';\n }\n) {\n const body = new URLSearchParams();\n body.append('grant_type', 'refresh_token');\n body.append('refresh_token', params.refreshToken);\n\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret ?? ''}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n return fetch(params.tokenUri, { method: 'POST', headers, body });\n}\n\ninterface RevokeTokenParams {\n token: string;\n clientId: string;\n clientSecret?: string;\n tokenRevokeUri: string;\n authentication?: 'basic' | 'post';\n}\n\nexport async function revokeToken(params: RevokeTokenParams) {\n const body = new URLSearchParams();\n body.append('token', params.token);\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret ?? ''}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n const response = await fetch(params.tokenRevokeUri, { method: 'POST', headers, body });\n if (!response.ok) {\n const error = await response.json();\n console.error('Refresh token error:', error);\n throw new OAuth2Error(response.status, 'invalid_request', 'Failed to revoke token');\n }\n}\n\nexport interface VerifyIdTokenOptions {\n jwkSetUri: string;\n /** The `iss` values the provider signs with; anything else is refused. */\n issuer: string | string[];\n /**\n * The client the token must be issued to. An id token proves who the user is only to the client\n * it names in `aud`: any other app on the same identity provider can obtain a validly signed one\n * for the same user, so a token for a foreign client must never sign anyone in here.\n */\n audience: string;\n}\n\nexport async function verifyIdToken<T>(idToken: string, options: VerifyIdTokenOptions) {\n const { jwkSetUri, issuer, audience } = options;\n try {\n const jwks = createRemoteJWKSet(new URL(jwkSetUri));\n const { payload } = await jwtVerify(idToken, jwks, { issuer, audience });\n return payload as T;\n } catch (error) {\n console.error('Failed to verify id_token', error);\n throw new OAuth2Error(400, '
|
|
1
|
+
{"version":3,"file":"common.cjs","names":["OAuth2Error","createRemoteJWKSet","jwtVerify"],"sources":["../../../src/oauth2/provider/common.ts"],"sourcesContent":["import { createRemoteJWKSet, jwtVerify } from 'jose';\nimport { OAuth2Error } from '../error';\nimport type { ExchangeCodeParams, PkceParameters, RefreshTokenParams } from '../types';\n\nexport function createAuthorizationUri(options: {\n state: string;\n scope: string[];\n clientId: string;\n redirectUri: string;\n authorizationUri: string;\n pkce?: Omit<PkceParameters, 'code_verifier'>;\n}) {\n const url = new URL(options.authorizationUri);\n url.searchParams.append('response_type', 'code');\n url.searchParams.append('client_id', options.clientId);\n url.searchParams.append('state', options.state);\n url.searchParams.append('scope', options.scope.join(' '));\n url.searchParams.append('redirect_uri', options.redirectUri);\n if (options.pkce) {\n url.searchParams.append('code_challenge_method', options.pkce.code_challenge_method);\n url.searchParams.append('code_challenge', options.pkce.code_challenge);\n }\n return url;\n}\n\nexport async function exchangeAuthorizationCode(\n params: ExchangeCodeParams & {\n tokenUri: string;\n authentication?: 'basic' | 'post';\n /** Extra request headers some providers require, e.g. GitHub's `User-Agent`. */\n headers?: Record<string, string>;\n }\n) {\n const body = new URLSearchParams();\n body.append('code', params.code);\n body.append('redirect_uri', params.redirectUri);\n body.append('grant_type', 'authorization_code');\n if (params.pkce) body.append('code_verifier', params.pkce.code_verifier);\n\n const headers: Record<string, string> = {\n ...params.headers,\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret ?? ''}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n // Public clients (Google's installed-app clients, PKCE-only clients) have no secret.\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n return fetch(params.tokenUri, { method: 'POST', headers, body });\n}\n\nexport async function refreshAccessToken(\n params: RefreshTokenParams & {\n tokenUri: string;\n authentication?: 'basic' | 'post';\n }\n) {\n const body = new URLSearchParams();\n body.append('grant_type', 'refresh_token');\n body.append('refresh_token', params.refreshToken);\n\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret ?? ''}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n return fetch(params.tokenUri, { method: 'POST', headers, body });\n}\n\ninterface RevokeTokenParams {\n token: string;\n clientId: string;\n clientSecret?: string;\n tokenRevokeUri: string;\n authentication?: 'basic' | 'post';\n}\n\nexport async function revokeToken(params: RevokeTokenParams) {\n const body = new URLSearchParams();\n body.append('token', params.token);\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret ?? ''}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n const response = await fetch(params.tokenRevokeUri, { method: 'POST', headers, body });\n if (!response.ok) {\n const error = await response.json();\n console.error('Refresh token error:', error);\n throw new OAuth2Error(response.status, 'invalid_request', 'Failed to revoke token');\n }\n}\n\nexport interface VerifyIdTokenOptions {\n jwkSetUri: string;\n /** The `iss` values the provider signs with; anything else is refused. */\n issuer: string | string[];\n /**\n * The client the token must be issued to. An id token proves who the user is only to the client\n * it names in `aud`: any other app on the same identity provider can obtain a validly signed one\n * for the same user, so a token for a foreign client must never sign anyone in here.\n */\n audience: string;\n}\n\nexport async function verifyIdToken<T>(idToken: string, options: VerifyIdTokenOptions) {\n const { jwkSetUri, issuer, audience } = options;\n try {\n const jwks = createRemoteJWKSet(new URL(jwkSetUri));\n const { payload } = await jwtVerify(idToken, jwks, { issuer, audience });\n return payload as T;\n } catch (error) {\n console.error('Failed to verify id_token', error);\n // The token is the grant here: one for another audience or issuer is an invalid grant, not a\n // malformed request.\n throw new OAuth2Error(400, 'invalid_grant', 'Failed to verify id_token');\n }\n}\n"],"mappings":"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"}
|
|
@@ -89,7 +89,7 @@ async function verifyIdToken(idToken, options) {
|
|
|
89
89
|
return payload;
|
|
90
90
|
} catch (error) {
|
|
91
91
|
console.error("Failed to verify id_token", error);
|
|
92
|
-
throw new OAuth2Error(400, "
|
|
92
|
+
throw new OAuth2Error(400, "invalid_grant", "Failed to verify id_token");
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
export { createAuthorizationUri, exchangeAuthorizationCode, refreshAccessToken, revokeToken, verifyIdToken };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.mjs","names":[],"sources":["../../../src/oauth2/provider/common.ts"],"sourcesContent":["import { createRemoteJWKSet, jwtVerify } from 'jose';\nimport { OAuth2Error } from '../error';\nimport type { ExchangeCodeParams, PkceParameters, RefreshTokenParams } from '../types';\n\nexport function createAuthorizationUri(options: {\n state: string;\n scope: string[];\n clientId: string;\n redirectUri: string;\n authorizationUri: string;\n pkce?: Omit<PkceParameters, 'code_verifier'>;\n}) {\n const url = new URL(options.authorizationUri);\n url.searchParams.append('response_type', 'code');\n url.searchParams.append('client_id', options.clientId);\n url.searchParams.append('state', options.state);\n url.searchParams.append('scope', options.scope.join(' '));\n url.searchParams.append('redirect_uri', options.redirectUri);\n if (options.pkce) {\n url.searchParams.append('code_challenge_method', options.pkce.code_challenge_method);\n url.searchParams.append('code_challenge', options.pkce.code_challenge);\n }\n return url;\n}\n\nexport async function exchangeAuthorizationCode(\n params: ExchangeCodeParams & {\n tokenUri: string;\n authentication?: 'basic' | 'post';\n /** Extra request headers some providers require, e.g. GitHub's `User-Agent`. */\n headers?: Record<string, string>;\n }\n) {\n const body = new URLSearchParams();\n body.append('code', params.code);\n body.append('redirect_uri', params.redirectUri);\n body.append('grant_type', 'authorization_code');\n if (params.pkce) body.append('code_verifier', params.pkce.code_verifier);\n\n const headers: Record<string, string> = {\n ...params.headers,\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret ?? ''}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n // Public clients (Google's installed-app clients, PKCE-only clients) have no secret.\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n return fetch(params.tokenUri, { method: 'POST', headers, body });\n}\n\nexport async function refreshAccessToken(\n params: RefreshTokenParams & {\n tokenUri: string;\n authentication?: 'basic' | 'post';\n }\n) {\n const body = new URLSearchParams();\n body.append('grant_type', 'refresh_token');\n body.append('refresh_token', params.refreshToken);\n\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret ?? ''}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n return fetch(params.tokenUri, { method: 'POST', headers, body });\n}\n\ninterface RevokeTokenParams {\n token: string;\n clientId: string;\n clientSecret?: string;\n tokenRevokeUri: string;\n authentication?: 'basic' | 'post';\n}\n\nexport async function revokeToken(params: RevokeTokenParams) {\n const body = new URLSearchParams();\n body.append('token', params.token);\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret ?? ''}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n const response = await fetch(params.tokenRevokeUri, { method: 'POST', headers, body });\n if (!response.ok) {\n const error = await response.json();\n console.error('Refresh token error:', error);\n throw new OAuth2Error(response.status, 'invalid_request', 'Failed to revoke token');\n }\n}\n\nexport interface VerifyIdTokenOptions {\n jwkSetUri: string;\n /** The `iss` values the provider signs with; anything else is refused. */\n issuer: string | string[];\n /**\n * The client the token must be issued to. An id token proves who the user is only to the client\n * it names in `aud`: any other app on the same identity provider can obtain a validly signed one\n * for the same user, so a token for a foreign client must never sign anyone in here.\n */\n audience: string;\n}\n\nexport async function verifyIdToken<T>(idToken: string, options: VerifyIdTokenOptions) {\n const { jwkSetUri, issuer, audience } = options;\n try {\n const jwks = createRemoteJWKSet(new URL(jwkSetUri));\n const { payload } = await jwtVerify(idToken, jwks, { issuer, audience });\n return payload as T;\n } catch (error) {\n console.error('Failed to verify id_token', error);\n throw new OAuth2Error(400, '
|
|
1
|
+
{"version":3,"file":"common.mjs","names":[],"sources":["../../../src/oauth2/provider/common.ts"],"sourcesContent":["import { createRemoteJWKSet, jwtVerify } from 'jose';\nimport { OAuth2Error } from '../error';\nimport type { ExchangeCodeParams, PkceParameters, RefreshTokenParams } from '../types';\n\nexport function createAuthorizationUri(options: {\n state: string;\n scope: string[];\n clientId: string;\n redirectUri: string;\n authorizationUri: string;\n pkce?: Omit<PkceParameters, 'code_verifier'>;\n}) {\n const url = new URL(options.authorizationUri);\n url.searchParams.append('response_type', 'code');\n url.searchParams.append('client_id', options.clientId);\n url.searchParams.append('state', options.state);\n url.searchParams.append('scope', options.scope.join(' '));\n url.searchParams.append('redirect_uri', options.redirectUri);\n if (options.pkce) {\n url.searchParams.append('code_challenge_method', options.pkce.code_challenge_method);\n url.searchParams.append('code_challenge', options.pkce.code_challenge);\n }\n return url;\n}\n\nexport async function exchangeAuthorizationCode(\n params: ExchangeCodeParams & {\n tokenUri: string;\n authentication?: 'basic' | 'post';\n /** Extra request headers some providers require, e.g. GitHub's `User-Agent`. */\n headers?: Record<string, string>;\n }\n) {\n const body = new URLSearchParams();\n body.append('code', params.code);\n body.append('redirect_uri', params.redirectUri);\n body.append('grant_type', 'authorization_code');\n if (params.pkce) body.append('code_verifier', params.pkce.code_verifier);\n\n const headers: Record<string, string> = {\n ...params.headers,\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret ?? ''}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n // Public clients (Google's installed-app clients, PKCE-only clients) have no secret.\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n return fetch(params.tokenUri, { method: 'POST', headers, body });\n}\n\nexport async function refreshAccessToken(\n params: RefreshTokenParams & {\n tokenUri: string;\n authentication?: 'basic' | 'post';\n }\n) {\n const body = new URLSearchParams();\n body.append('grant_type', 'refresh_token');\n body.append('refresh_token', params.refreshToken);\n\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret ?? ''}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n return fetch(params.tokenUri, { method: 'POST', headers, body });\n}\n\ninterface RevokeTokenParams {\n token: string;\n clientId: string;\n clientSecret?: string;\n tokenRevokeUri: string;\n authentication?: 'basic' | 'post';\n}\n\nexport async function revokeToken(params: RevokeTokenParams) {\n const body = new URLSearchParams();\n body.append('token', params.token);\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret ?? ''}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n const response = await fetch(params.tokenRevokeUri, { method: 'POST', headers, body });\n if (!response.ok) {\n const error = await response.json();\n console.error('Refresh token error:', error);\n throw new OAuth2Error(response.status, 'invalid_request', 'Failed to revoke token');\n }\n}\n\nexport interface VerifyIdTokenOptions {\n jwkSetUri: string;\n /** The `iss` values the provider signs with; anything else is refused. */\n issuer: string | string[];\n /**\n * The client the token must be issued to. An id token proves who the user is only to the client\n * it names in `aud`: any other app on the same identity provider can obtain a validly signed one\n * for the same user, so a token for a foreign client must never sign anyone in here.\n */\n audience: string;\n}\n\nexport async function verifyIdToken<T>(idToken: string, options: VerifyIdTokenOptions) {\n const { jwkSetUri, issuer, audience } = options;\n try {\n const jwks = createRemoteJWKSet(new URL(jwkSetUri));\n const { payload } = await jwtVerify(idToken, jwks, { issuer, audience });\n return payload as T;\n } catch (error) {\n console.error('Failed to verify id_token', error);\n // The token is the grant here: one for another audience or issuer is an invalid grant, not a\n // malformed request.\n throw new OAuth2Error(400, 'invalid_grant', 'Failed to verify id_token');\n }\n}\n"],"mappings":"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"}
|
|
@@ -53,8 +53,7 @@ function createFacebookProvider() {
|
|
|
53
53
|
sub: data.sub,
|
|
54
54
|
name: data.name,
|
|
55
55
|
picture: data.picture,
|
|
56
|
-
email: data.email
|
|
57
|
-
email_verified: true
|
|
56
|
+
email: data.email
|
|
58
57
|
}
|
|
59
58
|
};
|
|
60
59
|
}
|
|
@@ -108,8 +107,7 @@ async function fetchUserInfo(userInfoUri, accessToken, appSecretProof) {
|
|
|
108
107
|
sub: data.id,
|
|
109
108
|
name: data.name,
|
|
110
109
|
email: data.email,
|
|
111
|
-
picture: data.picture.data.url
|
|
112
|
-
email_verified: data.email_verified
|
|
110
|
+
picture: data.picture.data.url
|
|
113
111
|
}
|
|
114
112
|
};
|
|
115
113
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facebook.cjs","names":["createAuthorizationUri","exchangeAuthorizationCode","OAuth2Error","verifyIdToken","createHmac"],"sources":["../../../src/oauth2/provider/facebook.ts"],"sourcesContent":["import { createHmac } from 'node:crypto';\nimport { invariant } from '@shware/utils';\nimport { OAuth2Error } from '../error';\nimport type { LoginOAuth2NativeParams, OAuth2Token, Provider, UserInfo } from '../types';\nimport { createAuthorizationUri, exchangeAuthorizationCode, verifyIdToken } from './common';\n\n/**\n * `iss` of Limited Login id tokens, per https://limited.facebook.com/.well-known/openid-configuration/\n * (their `aud` is the app id).\n */\nconst FACEBOOK_ISSUER = 'https://www.facebook.com';\n\n/**\n * ref: https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow/\n * ref: https://developers.facebook.com/tools/explorer\n * */\nexport function createFacebookProvider(): Provider {\n return {\n authorizationUri: 'https://www.facebook.com/v21.0/dialog/oauth',\n tokenUri: 'https://graph.facebook.com/v19.0/oauth/access_token',\n userInfoUri: 'https://graph.facebook.com/me',\n jwkSetUri: 'https://limited.facebook.com/.well-known/oauth/openid/jwks/',\n userNameAttribute: 'id',\n defaultScope: ['profile', 'email'],\n createAuthorizationUri({ pkce: _, ...params }) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode({ pkce: _, ...params }) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error } = (await response.json()) as FacebookErrorResponse;\n throw new OAuth2Error(response.status, 'invalid_request', error.message);\n }\n return (await response.json()) as FacebookToken;\n },\n async getUserInfo({ id_token, access_token }, { clientId }) {\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n if (id_token) {\n const data = await verifyIdToken<FacebookDecodedIdToken>(id_token, {\n jwkSetUri: this.jwkSetUri,\n issuer: FACEBOOK_ISSUER,\n audience: clientId,\n });\n return {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n picture: data.picture,\n email: data.email,\n
|
|
1
|
+
{"version":3,"file":"facebook.cjs","names":["createAuthorizationUri","exchangeAuthorizationCode","OAuth2Error","verifyIdToken","createHmac"],"sources":["../../../src/oauth2/provider/facebook.ts"],"sourcesContent":["import { createHmac } from 'node:crypto';\nimport { invariant } from '@shware/utils';\nimport { OAuth2Error } from '../error';\nimport type { LoginOAuth2NativeParams, OAuth2Token, Provider, UserInfo } from '../types';\nimport { createAuthorizationUri, exchangeAuthorizationCode, verifyIdToken } from './common';\n\n/**\n * `iss` of Limited Login id tokens, per https://limited.facebook.com/.well-known/openid-configuration/\n * (their `aud` is the app id).\n */\nconst FACEBOOK_ISSUER = 'https://www.facebook.com';\n\n/**\n * ref: https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow/\n * ref: https://developers.facebook.com/tools/explorer\n * */\nexport function createFacebookProvider(): Provider {\n return {\n authorizationUri: 'https://www.facebook.com/v21.0/dialog/oauth',\n tokenUri: 'https://graph.facebook.com/v19.0/oauth/access_token',\n userInfoUri: 'https://graph.facebook.com/me',\n jwkSetUri: 'https://limited.facebook.com/.well-known/oauth/openid/jwks/',\n userNameAttribute: 'id',\n defaultScope: ['profile', 'email'],\n createAuthorizationUri({ pkce: _, ...params }) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode({ pkce: _, ...params }) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error } = (await response.json()) as FacebookErrorResponse;\n throw new OAuth2Error(response.status, 'invalid_request', error.message);\n }\n return (await response.json()) as FacebookToken;\n },\n async getUserInfo({ id_token, access_token }, { clientId }) {\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n if (id_token) {\n const data = await verifyIdToken<FacebookDecodedIdToken>(id_token, {\n jwkSetUri: this.jwkSetUri,\n issuer: FACEBOOK_ISSUER,\n audience: clientId,\n });\n return {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n picture: data.picture,\n email: data.email,\n // Facebook does not say whether the address is verified: left unset, so a host that\n // trusts only `email_verified === true` treats it as unverified.\n },\n };\n }\n invariant(access_token, 'access_token is required');\n invariant(this.userInfoUri, 'userInfoUri is required');\n return fetchUserInfo(this.userInfoUri, access_token);\n },\n async loginOAuth2Native({ clientId, clientSecret, credentials }: LoginOAuth2NativeParams) {\n const { id_token, access_token } = credentials;\n let userInfo: UserInfo;\n if (id_token) {\n // Limited Login: the id token's `aud` must be this app.\n userInfo = await this.getUserInfo(\n { token_type: 'bearer', access_token: '', id_token },\n {\n clientId,\n }\n );\n } else if (access_token) {\n // The Graph API serves any app's user access token, so `/me` alone would accept a token a\n // foreign app obtained for the same user. With `appsecret_proof` Facebook refuses tokens\n // not issued to the app whose secret signed the proof, binding the token to this client.\n if (!clientSecret) {\n throw new OAuth2Error(400, 'invalid_client', 'access_token login needs the app secret');\n }\n invariant(this.userInfoUri, 'userInfoUri is required');\n const proof = createHmac('sha256', clientSecret).update(access_token).digest('hex');\n userInfo = await fetchUserInfo(this.userInfoUri, access_token, proof);\n } else {\n throw new OAuth2Error(400, 'invalid_request', 'id_token or access_token is required');\n }\n const token: OAuth2Token = {\n token_type: 'bearer',\n access_token: access_token ?? '',\n id_token,\n };\n return { token, userInfo };\n },\n };\n}\n\nexport const facebook = createFacebookProvider();\n\nasync function fetchUserInfo(\n userInfoUri: string,\n accessToken: string,\n appSecretProof?: string\n): Promise<UserInfo<FacebookUserInfo>> {\n const url = new URL(userInfoUri);\n const fields = ['id', 'name', 'email', 'picture.width(320).height(320)'];\n url.searchParams.append('access_token', accessToken);\n url.searchParams.append('fields', fields.join(','));\n if (appSecretProof) url.searchParams.append('appsecret_proof', appSecretProof);\n const headers = { Accept: 'application/json' };\n const response = await fetch(url.href, { headers });\n if (!response.ok) {\n throw new OAuth2Error(response.status, 'invalid_request', 'Failed to fetch user info');\n }\n const data = (await response.json()) as FacebookUserInfo;\n return {\n data,\n claims: {\n sub: data.id,\n name: data.name,\n email: data.email,\n picture: data.picture.data.url,\n // Not stated by the Graph API either; see the Limited Login path above.\n },\n };\n}\n\nexport interface FacebookDecodedIdToken {\n sub: string;\n name: string;\n email: string;\n picture: string;\n nonce?: string;\n}\n\nexport interface FacebookUserInfo {\n id: string;\n name: string;\n email: string;\n picture: {\n data: {\n url: string;\n width: number;\n height: number;\n is_silhouette: boolean;\n };\n };\n}\n\nexport interface FacebookToken extends OAuth2Token {\n access_token: string;\n token_type: 'bearer';\n expires_in: number;\n id_token?: string;\n}\n\nexport type FacebookAppCredential =\n | { state: string; id_token: string } // platform=ios and tracking=limited\n | { state: string; access_token: string }; // tracking=enabled\n\ninterface FacebookErrorResponse {\n error: {\n message: string;\n type: 'OAuthException';\n code: number;\n error_subcode: number;\n fbtrace_id: string;\n };\n}\n"],"mappings":";;;;;;;;;;AAUA,MAAM,kBAAkB;;;;;AAMxB,SAAgB,yBAAmC;CACjD,OAAO;EACL,kBAAkB;EAClB,UAAU;EACV,aAAa;EACb,WAAW;EACX,mBAAmB;EACnB,cAAc,CAAC,WAAW,OAAO;EACjC,uBAAuB,EAAE,MAAM,GAAG,GAAG,UAAU;GAC7C,OAAOA,+BAAAA,uBAAuB;IAC5B,GAAG;IACH,OAAO,OAAO,SAAS,KAAK;IAC5B,kBAAkB,KAAK;GACzB,CAAC;EACH;EACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,GAAG,UAAU;GACtD,MAAM,WAAW,MAAMC,+BAAAA,0BAA0B;IAAE,GAAG;IAAQ,UAAU,KAAK;GAAS,CAAC;GACvF,IAAI,CAAC,SAAS,IAAI;IAChB,MAAM,EAAE,UAAW,MAAM,SAAS,KAAK;IACvC,MAAM,IAAIC,qBAAAA,YAAY,SAAS,QAAQ,mBAAmB,MAAM,OAAO;GACzE;GACA,OAAQ,MAAM,SAAS,KAAK;EAC9B;EACA,MAAM,YAAY,EAAE,UAAU,gBAAgB,EAAE,YAAY;GAC1D,CAAA,GAAA,cAAA,UAAA,CAAU,KAAK,WAAW,uBAAuB;GACjD,IAAI,UAAU;IACZ,MAAM,OAAO,MAAMC,+BAAAA,cAAsC,UAAU;KACjE,WAAW,KAAK;KAChB,QAAQ;KACR,UAAU;IACZ,CAAC;IACD,OAAO;KACL;KACA,QAAQ;MACN,KAAK,KAAK;MACV,MAAM,KAAK;MACX,SAAS,KAAK;MACd,OAAO,KAAK;KAGd;IACF;GACF;GACA,CAAA,GAAA,cAAA,UAAA,CAAU,cAAc,0BAA0B;GAClD,CAAA,GAAA,cAAA,UAAA,CAAU,KAAK,aAAa,yBAAyB;GACrD,OAAO,cAAc,KAAK,aAAa,YAAY;EACrD;EACA,MAAM,kBAAkB,EAAE,UAAU,cAAc,eAAwC;GACxF,MAAM,EAAE,UAAU,iBAAiB;GACnC,IAAI;GACJ,IAAI,UAEF,WAAW,MAAM,KAAK,YACpB;IAAE,YAAY;IAAU,cAAc;IAAI;GAAS,GACnD,EACE,SACF,CACF;QACK,IAAI,cAAc;IAIvB,IAAI,CAAC,cACH,MAAM,IAAID,qBAAAA,YAAY,KAAK,kBAAkB,yCAAyC;IAExF,CAAA,GAAA,cAAA,UAAA,CAAU,KAAK,aAAa,yBAAyB;IACrD,MAAM,SAAA,GAAQE,YAAAA,WAAAA,CAAW,UAAU,YAAY,CAAC,CAAC,OAAO,YAAY,CAAC,CAAC,OAAO,KAAK;IAClF,WAAW,MAAM,cAAc,KAAK,aAAa,cAAc,KAAK;GACtE,OACE,MAAM,IAAIF,qBAAAA,YAAY,KAAK,mBAAmB,sCAAsC;GAOtF,OAAO;IAAE,OAAA;KAJP,YAAY;KACZ,cAAc,gBAAgB;KAC9B;IAEW;IAAG;GAAS;EAC3B;CACF;AACF;AAEA,MAAa,WAAW,uBAAuB;AAE/C,eAAe,cACb,aACA,aACA,gBACqC;CACrC,MAAM,MAAM,IAAI,IAAI,WAAW;CAC/B,MAAM,SAAS;EAAC;EAAM;EAAQ;EAAS;CAAgC;CACvE,IAAI,aAAa,OAAO,gBAAgB,WAAW;CACnD,IAAI,aAAa,OAAO,UAAU,OAAO,KAAK,GAAG,CAAC;CAClD,IAAI,gBAAgB,IAAI,aAAa,OAAO,mBAAmB,cAAc;CAE7E,MAAM,WAAW,MAAM,MAAM,IAAI,MAAM,EAAE,SAAA,EADvB,QAAQ,mBACqB,EAAE,CAAC;CAClD,IAAI,CAAC,SAAS,IACZ,MAAM,IAAIA,qBAAAA,YAAY,SAAS,QAAQ,mBAAmB,2BAA2B;CAEvF,MAAM,OAAQ,MAAM,SAAS,KAAK;CAClC,OAAO;EACL;EACA,QAAQ;GACN,KAAK,KAAK;GACV,MAAM,KAAK;GACX,OAAO,KAAK;GACZ,SAAS,KAAK,QAAQ,KAAK;EAE7B;CACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facebook.d.cts","names":[],"sources":["../../../src/oauth2/provider/facebook.ts"],"mappings":";;;;;wBAgBgB,0BAA0B;
|
|
1
|
+
{"version":3,"file":"facebook.d.cts","names":[],"sources":["../../../src/oauth2/provider/facebook.ts"],"mappings":";;;;;wBAgBgB,0BAA0B;qBAiF7B,UAAQ;iBA8BJ;EACf;EACA;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA;IACE;MACE;MACA;MACA;MACA;;;;iBAKW,sBAAsB;EACrC;EACA;EACA;EACA;;YAGU;EACN;EAAe;;EACf;EAAe"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facebook.d.mts","names":[],"sources":["../../../src/oauth2/provider/facebook.ts"],"mappings":";;;;;wBAgBgB,0BAA0B;
|
|
1
|
+
{"version":3,"file":"facebook.d.mts","names":[],"sources":["../../../src/oauth2/provider/facebook.ts"],"mappings":";;;;;wBAgBgB,0BAA0B;qBAiF7B,UAAQ;iBA8BJ;EACf;EACA;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA;IACE;MACE;MACA;MACA;MACA;;;;iBAKW,sBAAsB;EACrC;EACA;EACA;EACA;;YAGU;EACN;EAAe;;EACf;EAAe"}
|
|
@@ -52,8 +52,7 @@ function createFacebookProvider() {
|
|
|
52
52
|
sub: data.sub,
|
|
53
53
|
name: data.name,
|
|
54
54
|
picture: data.picture,
|
|
55
|
-
email: data.email
|
|
56
|
-
email_verified: true
|
|
55
|
+
email: data.email
|
|
57
56
|
}
|
|
58
57
|
};
|
|
59
58
|
}
|
|
@@ -107,8 +106,7 @@ async function fetchUserInfo(userInfoUri, accessToken, appSecretProof) {
|
|
|
107
106
|
sub: data.id,
|
|
108
107
|
name: data.name,
|
|
109
108
|
email: data.email,
|
|
110
|
-
picture: data.picture.data.url
|
|
111
|
-
email_verified: data.email_verified
|
|
109
|
+
picture: data.picture.data.url
|
|
112
110
|
}
|
|
113
111
|
};
|
|
114
112
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facebook.mjs","names":[],"sources":["../../../src/oauth2/provider/facebook.ts"],"sourcesContent":["import { createHmac } from 'node:crypto';\nimport { invariant } from '@shware/utils';\nimport { OAuth2Error } from '../error';\nimport type { LoginOAuth2NativeParams, OAuth2Token, Provider, UserInfo } from '../types';\nimport { createAuthorizationUri, exchangeAuthorizationCode, verifyIdToken } from './common';\n\n/**\n * `iss` of Limited Login id tokens, per https://limited.facebook.com/.well-known/openid-configuration/\n * (their `aud` is the app id).\n */\nconst FACEBOOK_ISSUER = 'https://www.facebook.com';\n\n/**\n * ref: https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow/\n * ref: https://developers.facebook.com/tools/explorer\n * */\nexport function createFacebookProvider(): Provider {\n return {\n authorizationUri: 'https://www.facebook.com/v21.0/dialog/oauth',\n tokenUri: 'https://graph.facebook.com/v19.0/oauth/access_token',\n userInfoUri: 'https://graph.facebook.com/me',\n jwkSetUri: 'https://limited.facebook.com/.well-known/oauth/openid/jwks/',\n userNameAttribute: 'id',\n defaultScope: ['profile', 'email'],\n createAuthorizationUri({ pkce: _, ...params }) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode({ pkce: _, ...params }) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error } = (await response.json()) as FacebookErrorResponse;\n throw new OAuth2Error(response.status, 'invalid_request', error.message);\n }\n return (await response.json()) as FacebookToken;\n },\n async getUserInfo({ id_token, access_token }, { clientId }) {\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n if (id_token) {\n const data = await verifyIdToken<FacebookDecodedIdToken>(id_token, {\n jwkSetUri: this.jwkSetUri,\n issuer: FACEBOOK_ISSUER,\n audience: clientId,\n });\n return {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n picture: data.picture,\n email: data.email,\n
|
|
1
|
+
{"version":3,"file":"facebook.mjs","names":[],"sources":["../../../src/oauth2/provider/facebook.ts"],"sourcesContent":["import { createHmac } from 'node:crypto';\nimport { invariant } from '@shware/utils';\nimport { OAuth2Error } from '../error';\nimport type { LoginOAuth2NativeParams, OAuth2Token, Provider, UserInfo } from '../types';\nimport { createAuthorizationUri, exchangeAuthorizationCode, verifyIdToken } from './common';\n\n/**\n * `iss` of Limited Login id tokens, per https://limited.facebook.com/.well-known/openid-configuration/\n * (their `aud` is the app id).\n */\nconst FACEBOOK_ISSUER = 'https://www.facebook.com';\n\n/**\n * ref: https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow/\n * ref: https://developers.facebook.com/tools/explorer\n * */\nexport function createFacebookProvider(): Provider {\n return {\n authorizationUri: 'https://www.facebook.com/v21.0/dialog/oauth',\n tokenUri: 'https://graph.facebook.com/v19.0/oauth/access_token',\n userInfoUri: 'https://graph.facebook.com/me',\n jwkSetUri: 'https://limited.facebook.com/.well-known/oauth/openid/jwks/',\n userNameAttribute: 'id',\n defaultScope: ['profile', 'email'],\n createAuthorizationUri({ pkce: _, ...params }) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode({ pkce: _, ...params }) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error } = (await response.json()) as FacebookErrorResponse;\n throw new OAuth2Error(response.status, 'invalid_request', error.message);\n }\n return (await response.json()) as FacebookToken;\n },\n async getUserInfo({ id_token, access_token }, { clientId }) {\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n if (id_token) {\n const data = await verifyIdToken<FacebookDecodedIdToken>(id_token, {\n jwkSetUri: this.jwkSetUri,\n issuer: FACEBOOK_ISSUER,\n audience: clientId,\n });\n return {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n picture: data.picture,\n email: data.email,\n // Facebook does not say whether the address is verified: left unset, so a host that\n // trusts only `email_verified === true` treats it as unverified.\n },\n };\n }\n invariant(access_token, 'access_token is required');\n invariant(this.userInfoUri, 'userInfoUri is required');\n return fetchUserInfo(this.userInfoUri, access_token);\n },\n async loginOAuth2Native({ clientId, clientSecret, credentials }: LoginOAuth2NativeParams) {\n const { id_token, access_token } = credentials;\n let userInfo: UserInfo;\n if (id_token) {\n // Limited Login: the id token's `aud` must be this app.\n userInfo = await this.getUserInfo(\n { token_type: 'bearer', access_token: '', id_token },\n {\n clientId,\n }\n );\n } else if (access_token) {\n // The Graph API serves any app's user access token, so `/me` alone would accept a token a\n // foreign app obtained for the same user. With `appsecret_proof` Facebook refuses tokens\n // not issued to the app whose secret signed the proof, binding the token to this client.\n if (!clientSecret) {\n throw new OAuth2Error(400, 'invalid_client', 'access_token login needs the app secret');\n }\n invariant(this.userInfoUri, 'userInfoUri is required');\n const proof = createHmac('sha256', clientSecret).update(access_token).digest('hex');\n userInfo = await fetchUserInfo(this.userInfoUri, access_token, proof);\n } else {\n throw new OAuth2Error(400, 'invalid_request', 'id_token or access_token is required');\n }\n const token: OAuth2Token = {\n token_type: 'bearer',\n access_token: access_token ?? '',\n id_token,\n };\n return { token, userInfo };\n },\n };\n}\n\nexport const facebook = createFacebookProvider();\n\nasync function fetchUserInfo(\n userInfoUri: string,\n accessToken: string,\n appSecretProof?: string\n): Promise<UserInfo<FacebookUserInfo>> {\n const url = new URL(userInfoUri);\n const fields = ['id', 'name', 'email', 'picture.width(320).height(320)'];\n url.searchParams.append('access_token', accessToken);\n url.searchParams.append('fields', fields.join(','));\n if (appSecretProof) url.searchParams.append('appsecret_proof', appSecretProof);\n const headers = { Accept: 'application/json' };\n const response = await fetch(url.href, { headers });\n if (!response.ok) {\n throw new OAuth2Error(response.status, 'invalid_request', 'Failed to fetch user info');\n }\n const data = (await response.json()) as FacebookUserInfo;\n return {\n data,\n claims: {\n sub: data.id,\n name: data.name,\n email: data.email,\n picture: data.picture.data.url,\n // Not stated by the Graph API either; see the Limited Login path above.\n },\n };\n}\n\nexport interface FacebookDecodedIdToken {\n sub: string;\n name: string;\n email: string;\n picture: string;\n nonce?: string;\n}\n\nexport interface FacebookUserInfo {\n id: string;\n name: string;\n email: string;\n picture: {\n data: {\n url: string;\n width: number;\n height: number;\n is_silhouette: boolean;\n };\n };\n}\n\nexport interface FacebookToken extends OAuth2Token {\n access_token: string;\n token_type: 'bearer';\n expires_in: number;\n id_token?: string;\n}\n\nexport type FacebookAppCredential =\n | { state: string; id_token: string } // platform=ios and tracking=limited\n | { state: string; access_token: string }; // tracking=enabled\n\ninterface FacebookErrorResponse {\n error: {\n message: string;\n type: 'OAuthException';\n code: number;\n error_subcode: number;\n fbtrace_id: string;\n };\n}\n"],"mappings":";;;;;;;;;AAUA,MAAM,kBAAkB;;;;;AAMxB,SAAgB,yBAAmC;CACjD,OAAO;EACL,kBAAkB;EAClB,UAAU;EACV,aAAa;EACb,WAAW;EACX,mBAAmB;EACnB,cAAc,CAAC,WAAW,OAAO;EACjC,uBAAuB,EAAE,MAAM,GAAG,GAAG,UAAU;GAC7C,OAAO,uBAAuB;IAC5B,GAAG;IACH,OAAO,OAAO,SAAS,KAAK;IAC5B,kBAAkB,KAAK;GACzB,CAAC;EACH;EACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,GAAG,UAAU;GACtD,MAAM,WAAW,MAAM,0BAA0B;IAAE,GAAG;IAAQ,UAAU,KAAK;GAAS,CAAC;GACvF,IAAI,CAAC,SAAS,IAAI;IAChB,MAAM,EAAE,UAAW,MAAM,SAAS,KAAK;IACvC,MAAM,IAAI,YAAY,SAAS,QAAQ,mBAAmB,MAAM,OAAO;GACzE;GACA,OAAQ,MAAM,SAAS,KAAK;EAC9B;EACA,MAAM,YAAY,EAAE,UAAU,gBAAgB,EAAE,YAAY;GAC1D,UAAU,KAAK,WAAW,uBAAuB;GACjD,IAAI,UAAU;IACZ,MAAM,OAAO,MAAM,cAAsC,UAAU;KACjE,WAAW,KAAK;KAChB,QAAQ;KACR,UAAU;IACZ,CAAC;IACD,OAAO;KACL;KACA,QAAQ;MACN,KAAK,KAAK;MACV,MAAM,KAAK;MACX,SAAS,KAAK;MACd,OAAO,KAAK;KAGd;IACF;GACF;GACA,UAAU,cAAc,0BAA0B;GAClD,UAAU,KAAK,aAAa,yBAAyB;GACrD,OAAO,cAAc,KAAK,aAAa,YAAY;EACrD;EACA,MAAM,kBAAkB,EAAE,UAAU,cAAc,eAAwC;GACxF,MAAM,EAAE,UAAU,iBAAiB;GACnC,IAAI;GACJ,IAAI,UAEF,WAAW,MAAM,KAAK,YACpB;IAAE,YAAY;IAAU,cAAc;IAAI;GAAS,GACnD,EACE,SACF,CACF;QACK,IAAI,cAAc;IAIvB,IAAI,CAAC,cACH,MAAM,IAAI,YAAY,KAAK,kBAAkB,yCAAyC;IAExF,UAAU,KAAK,aAAa,yBAAyB;IACrD,MAAM,QAAQ,WAAW,UAAU,YAAY,CAAC,CAAC,OAAO,YAAY,CAAC,CAAC,OAAO,KAAK;IAClF,WAAW,MAAM,cAAc,KAAK,aAAa,cAAc,KAAK;GACtE,OACE,MAAM,IAAI,YAAY,KAAK,mBAAmB,sCAAsC;GAOtF,OAAO;IAAE,OAAA;KAJP,YAAY;KACZ,cAAc,gBAAgB;KAC9B;IAEW;IAAG;GAAS;EAC3B;CACF;AACF;AAEA,MAAa,WAAW,uBAAuB;AAE/C,eAAe,cACb,aACA,aACA,gBACqC;CACrC,MAAM,MAAM,IAAI,IAAI,WAAW;CAC/B,MAAM,SAAS;EAAC;EAAM;EAAQ;EAAS;CAAgC;CACvE,IAAI,aAAa,OAAO,gBAAgB,WAAW;CACnD,IAAI,aAAa,OAAO,UAAU,OAAO,KAAK,GAAG,CAAC;CAClD,IAAI,gBAAgB,IAAI,aAAa,OAAO,mBAAmB,cAAc;CAE7E,MAAM,WAAW,MAAM,MAAM,IAAI,MAAM,EAAE,SAAA,EADvB,QAAQ,mBACqB,EAAE,CAAC;CAClD,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,YAAY,SAAS,QAAQ,mBAAmB,2BAA2B;CAEvF,MAAM,OAAQ,MAAM,SAAS,KAAK;CAClC,OAAO;EACL;EACA,QAAQ;GACN,KAAK,KAAK;GACV,MAAM,KAAK;GACX,OAAO,KAAK;GACZ,SAAS,KAAK,QAAQ,KAAK;EAE7B;CACF;AACF"}
|