@shware/security 9.2.1 → 9.3.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 CHANGED
@@ -12,8 +12,8 @@ indexed by principal so a user's sessions can be listed and revoked.
12
12
  pnpm add @shware/security zod
13
13
  ```
14
14
 
15
- No peer dependencies. `ioredis` is a regular dependency; you only need a client instance if you
16
- use the Redis repositories.
15
+ `zod` is a peer dependency (the schemas are read from the host's copy). `ioredis` is an optional
16
+ peer: you only need it, and a client instance, if you use the Redis repositories.
17
17
 
18
18
  ## Entry points
19
19
 
@@ -184,7 +184,10 @@ throw new AuthenticationError('EMAIL_ALREADY_USED', {
184
184
  ```
185
185
 
186
186
  `AuthenticationReason` is the union of all reasons, for the client-side switch;
187
- `AUTHENTICATION_REASONS` lists the built-in ones at runtime, for a message table.
187
+ `AUTHENTICATION_REASONS` lists the built-in ones at runtime, for a message table. The reasons the
188
+ SDK answers about the request itself (`INVALID_ARGUMENT`, `INVALID_JSON`,
189
+ `VERIFICATION_CODE_RESEND_TOO_SOON`, `EMAIL_DELIVERY_FAILED`, …; see "Errors") are
190
+ `REQUEST_REASONS` and `RequestReason` in the same way.
188
191
 
189
192
  `Auth` answers it in the shape of the route: the redirect flow lands on `errorUri` with
190
193
  `error=access_denied&reason=EMAIL_ALREADY_USED&provider=GOOGLE`; the JSON routes (native, One
@@ -14,6 +14,23 @@ const AUTHENTICATION_REASONS = [
14
14
  "SIGNUP_DISABLED"
15
15
  ];
16
16
  /**
17
+ * The reasons the SDK answers with for a request it cannot act on, as the `ErrorInfo.reason` of a
18
+ * `RequestError` (see `respond.ts`): about the request itself, not who is signing in, so the host
19
+ * never throws them. Every throw site is typed against this list, so a new reason is added here
20
+ * first, and a client's message table can cover them all.
21
+ */
22
+ const REQUEST_REASONS = [
23
+ "INVALID_ARGUMENT",
24
+ "INVALID_ATTRIBUTES",
25
+ "INVALID_CODE_VERIFIER",
26
+ "INVALID_CONTENT_TYPE",
27
+ "INVALID_JSON",
28
+ "TURNSTILE_VERIFICATION_FAILED",
29
+ "UNAUTHENTICATED",
30
+ "VERIFICATION_CODE_RESEND_TOO_SOON",
31
+ "EMAIL_DELIVERY_FAILED"
32
+ ];
33
+ /**
17
34
  * Thrown by the host's `onAuthorized` to refuse a sign-in it could complete but will not: an
18
35
  * address another account holds, a banned user, a closed beta. `reason` is the code the client
19
36
  * branches on, `metadata` what it needs to act (the provider to sign in with instead); both
@@ -43,5 +60,6 @@ var AuthenticationError = class AuthenticationError extends Error {
43
60
  };
44
61
  exports.AUTHENTICATION_REASONS = AUTHENTICATION_REASONS;
45
62
  exports.AuthenticationError = AuthenticationError;
63
+ exports.REQUEST_REASONS = REQUEST_REASONS;
46
64
 
47
65
  //# sourceMappingURL=error.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"error.cjs","names":["given"],"sources":["../../src/auth/error.ts"],"sourcesContent":["type Metadata = Record<string, string>;\n/** A reason that carries no metadata. */\ntype NoMetadata = { [key: string]: never };\n\n/**\n * The reasons `onAuthorized` may refuse a sign-in with, each with the metadata it carries (string\n * fields only: metadata travels as URL parameters and JSON). The built-in ones cover the account\n * states every host meets; add your own by augmenting the interface, and `AuthenticationError`\n * accepts exactly these reasons, requiring the metadata when one of its fields is required:\n *\n * ```ts\n * declare module '@shware/security' {\n * interface AuthenticationReasons {\n * CLOSED_BETA: { invite?: string };\n * }\n * }\n * ```\n */\nexport interface AuthenticationReasons {\n // Thrown by the SDK itself.\n /** The emailed code is wrong, or none is pending for the address. */\n INVALID_VERIFICATION_CODE: NoMetadata;\n /** More than `verificationCode.maxAttempts` guesses at the code; it is discarded. */\n VERIFICATION_CODE_ATTEMPTS_EXCEEDED: NoMetadata;\n /** The id token carries no nonce and the provider's flow requires one (`oauth2.nonce.optional`). */\n NONCE_MISSING: NoMetadata;\n /**\n * The id token's nonce is not the one this sign-in was started with: not issued for this\n * provider, used already, or (redirect flow) not the one sent with the authorization request.\n */\n NONCE_MISMATCH: NoMetadata;\n /** The desktop sign-in code is unknown, expired, used, or its verifier does not match. */\n INVALID_DESKTOP_CODE: NoMetadata;\n\n // For the host's `onAuthorized`.\n /** The account is disabled: banned, or closed by its owner. */\n ACCOUNT_DISABLED: NoMetadata;\n /** The account is locked for now; `until` is when it opens again, when known. */\n ACCOUNT_LOCKED: { until?: string };\n /** The address belongs to another account; `provider` is the one to sign in with instead. */\n EMAIL_ALREADY_USED: { provider?: string };\n /** The host accepts only verified addresses and this provider did not verify it. */\n EMAIL_NOT_VERIFIED: { provider?: string };\n /** No new accounts: registration is closed or by invitation. */\n SIGNUP_DISABLED: NoMetadata;\n}\n\n/** The built-in reasons, for a client's message table or a check that every reason is handled. */\nexport const AUTHENTICATION_REASONS = [\n 'INVALID_VERIFICATION_CODE',\n 'VERIFICATION_CODE_ATTEMPTS_EXCEEDED',\n 'NONCE_MISSING',\n 'NONCE_MISMATCH',\n 'INVALID_DESKTOP_CODE',\n 'ACCOUNT_DISABLED',\n 'ACCOUNT_LOCKED',\n 'EMAIL_ALREADY_USED',\n 'EMAIL_NOT_VERIFIED',\n 'SIGNUP_DISABLED',\n] as const satisfies readonly (keyof AuthenticationReasons)[];\n\n/**\n * Each reason's metadata as declared; a field that is not a string makes the reason unusable\n * (`never`).\n */\ntype Reasons = {\n [K in keyof AuthenticationReasons]: AuthenticationReasons[K] extends Partial<Metadata>\n ? AuthenticationReasons[K]\n : never;\n};\n\n/** A reason `onAuthorized` may refuse with: the keys of `AuthenticationReasons`. */\nexport type AuthenticationReason = keyof Reasons;\n\n/** `true` when the reason's metadata has no required field, so it may be left out. */\ntype Optional<R extends AuthenticationReason> = Reasons[R] extends NoMetadata\n ? true\n : Partial<Reasons[R]> extends Reasons[R]\n ? true\n : false;\ntype Options<R extends AuthenticationReason> = {\n message?: string;\n cause?: unknown;\n} & (Optional<R> extends true ? { metadata?: Reasons[R] } : { metadata: Reasons[R] });\n\n/**\n * Thrown by the host's `onAuthorized` to refuse a sign-in it could complete but will not: an\n * address another account holds, a banned user, a closed beta. `reason` is the code the client\n * branches on, `metadata` what it needs to act (the provider to sign in with instead); both\n * travel as they are, so keep them short and free of secrets. `Auth` answers a refusal itself,\n * in the shape of the route it came through:\n *\n * - redirect flow (`loginOAuth2Code`): `errorUri?error=access_denied&reason=<reason>&<metadata>`;\n * - JSON routes (native, One Tap, email, desktop): `403` with a google.rpc error body whose\n * `ErrorInfo` holds `reason` and `metadata`.\n *\n * The SDK throws it too, for a credential it checked itself (a wrong emailed code, a replayed\n * nonce), so a client handles both the same way. Anything else `onAuthorized` throws is a server\n * fault: `server_error` on the redirect, rethrown to the host's error handler on the JSON routes.\n */\nexport class AuthenticationError<\n R extends AuthenticationReason = AuthenticationReason,\n> extends Error {\n readonly reason: R;\n readonly metadata: Metadata;\n\n constructor(reason: R, ...[options]: Optional<R> extends true ? [Options<R>?] : [Options<R>]) {\n const given = options;\n const { message, cause, metadata = {} } = given ?? {};\n super(message ?? `Sign-in refused: ${reason}`, { cause });\n this.name = 'AuthenticationError';\n this.reason = reason;\n this.metadata = {};\n for (const [key, value] of Object.entries(metadata)) {\n if (typeof value === 'string') this.metadata[key] = value;\n }\n Object.setPrototypeOf(this, AuthenticationError.prototype);\n }\n}\n"],"mappings":";;;AAgDA,MAAa,yBAAyB;CACpC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;;;;;;;;;;AAyCA,IAAa,sBAAb,MAAa,4BAEH,MAAM;CACd;CACA;CAEA,YAAY,QAAW,GAAG,CAAC,UAAmE;EAE5F,MAAM,EAAE,SAAS,OAAO,WAAW,CAAC,MAAMA,WAAS,CAAC;EACpD,MAAM,WAAW,oBAAoB,UAAU,EAAE,MAAM,CAAC;EACxD,KAAK,OAAO;EACZ,KAAK,SAAS;EACd,KAAK,WAAW,CAAC;EACjB,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,GAChD,IAAI,OAAO,UAAU,UAAU,KAAK,SAAS,OAAO;EAEtD,OAAO,eAAe,MAAM,oBAAoB,SAAS;CAC3D;AACF"}
1
+ {"version":3,"file":"error.cjs","names":["given"],"sources":["../../src/auth/error.ts"],"sourcesContent":["type Metadata = Record<string, string>;\n/** A reason that carries no metadata. */\ntype NoMetadata = { [key: string]: never };\n\n/**\n * The reasons `onAuthorized` may refuse a sign-in with, each with the metadata it carries (string\n * fields only: metadata travels as URL parameters and JSON). The built-in ones cover the account\n * states every host meets; add your own by augmenting the interface, and `AuthenticationError`\n * accepts exactly these reasons, requiring the metadata when one of its fields is required:\n *\n * ```ts\n * declare module '@shware/security' {\n * interface AuthenticationReasons {\n * CLOSED_BETA: { invite?: string };\n * }\n * }\n * ```\n */\nexport interface AuthenticationReasons {\n // Thrown by the SDK itself.\n /** The emailed code is wrong, or none is pending for the address. */\n INVALID_VERIFICATION_CODE: NoMetadata;\n /** More than `verificationCode.maxAttempts` guesses at the code; it is discarded. */\n VERIFICATION_CODE_ATTEMPTS_EXCEEDED: NoMetadata;\n /** The id token carries no nonce and the provider's flow requires one (`oauth2.nonce.optional`). */\n NONCE_MISSING: NoMetadata;\n /**\n * The id token's nonce is not the one this sign-in was started with: not issued for this\n * provider, used already, or (redirect flow) not the one sent with the authorization request.\n */\n NONCE_MISMATCH: NoMetadata;\n /** The desktop sign-in code is unknown, expired, used, or its verifier does not match. */\n INVALID_DESKTOP_CODE: NoMetadata;\n\n // For the host's `onAuthorized`.\n /** The account is disabled: banned, or closed by its owner. */\n ACCOUNT_DISABLED: NoMetadata;\n /** The account is locked for now; `until` is when it opens again, when known. */\n ACCOUNT_LOCKED: { until?: string };\n /** The address belongs to another account; `provider` is the one to sign in with instead. */\n EMAIL_ALREADY_USED: { provider?: string };\n /** The host accepts only verified addresses and this provider did not verify it. */\n EMAIL_NOT_VERIFIED: { provider?: string };\n /** No new accounts: registration is closed or by invitation. */\n SIGNUP_DISABLED: NoMetadata;\n}\n\n/** The built-in reasons, for a client's message table or a check that every reason is handled. */\nexport const AUTHENTICATION_REASONS = [\n 'INVALID_VERIFICATION_CODE',\n 'VERIFICATION_CODE_ATTEMPTS_EXCEEDED',\n 'NONCE_MISSING',\n 'NONCE_MISMATCH',\n 'INVALID_DESKTOP_CODE',\n 'ACCOUNT_DISABLED',\n 'ACCOUNT_LOCKED',\n 'EMAIL_ALREADY_USED',\n 'EMAIL_NOT_VERIFIED',\n 'SIGNUP_DISABLED',\n] as const satisfies readonly (keyof AuthenticationReasons)[];\n\n/**\n * The reasons the SDK answers with for a request it cannot act on, as the `ErrorInfo.reason` of a\n * `RequestError` (see `respond.ts`): about the request itself, not who is signing in, so the host\n * never throws them. Every throw site is typed against this list, so a new reason is added here\n * first, and a client's message table can cover them all.\n */\nexport const REQUEST_REASONS = [\n /** 400: a field failed schema validation; a `BadRequest` detail names it. */\n 'INVALID_ARGUMENT',\n /** 400: a login body's `attributes` is not strings to strings within the size limit. */\n 'INVALID_ATTRIBUTES',\n /** 400: a native login's `code_verifier` is not 43-128 characters from `[A-Za-z0-9-._~]`. */\n 'INVALID_CODE_VERIFIER',\n /** 400: a form route was posted a content type other than `application/x-www-form-urlencoded`. */\n 'INVALID_CONTENT_TYPE',\n /** 400: the body of a JSON route is not JSON. */\n 'INVALID_JSON',\n /** 400: the Turnstile token sent with an email code request was rejected. */\n 'TURNSTILE_VERIFICATION_FAILED',\n /** 401: `desktopAuthorize` without a signed-in session. */\n 'UNAUTHENTICATED',\n /** 429: a code was sent to this email within `verificationCode.resendCooldown`. */\n 'VERIFICATION_CODE_RESEND_TOO_SOON',\n /** 503: the email sender threw; the cooldown is released so the user can retry at once. */\n 'EMAIL_DELIVERY_FAILED',\n] as const;\n\n/** A reason the SDK answers a malformed, too-soon or unauthenticated request with. */\nexport type RequestReason = (typeof REQUEST_REASONS)[number];\n\n/**\n * Each reason's metadata as declared; a field that is not a string makes the reason unusable\n * (`never`).\n */\ntype Reasons = {\n [K in keyof AuthenticationReasons]: AuthenticationReasons[K] extends Partial<Metadata>\n ? AuthenticationReasons[K]\n : never;\n};\n\n/** A reason `onAuthorized` may refuse with: the keys of `AuthenticationReasons`. */\nexport type AuthenticationReason = keyof Reasons;\n\n/** `true` when the reason's metadata has no required field, so it may be left out. */\ntype Optional<R extends AuthenticationReason> = Reasons[R] extends NoMetadata\n ? true\n : Partial<Reasons[R]> extends Reasons[R]\n ? true\n : false;\ntype Options<R extends AuthenticationReason> = {\n message?: string;\n cause?: unknown;\n} & (Optional<R> extends true ? { metadata?: Reasons[R] } : { metadata: Reasons[R] });\n\n/**\n * Thrown by the host's `onAuthorized` to refuse a sign-in it could complete but will not: an\n * address another account holds, a banned user, a closed beta. `reason` is the code the client\n * branches on, `metadata` what it needs to act (the provider to sign in with instead); both\n * travel as they are, so keep them short and free of secrets. `Auth` answers a refusal itself,\n * in the shape of the route it came through:\n *\n * - redirect flow (`loginOAuth2Code`): `errorUri?error=access_denied&reason=<reason>&<metadata>`;\n * - JSON routes (native, One Tap, email, desktop): `403` with a google.rpc error body whose\n * `ErrorInfo` holds `reason` and `metadata`.\n *\n * The SDK throws it too, for a credential it checked itself (a wrong emailed code, a replayed\n * nonce), so a client handles both the same way. Anything else `onAuthorized` throws is a server\n * fault: `server_error` on the redirect, rethrown to the host's error handler on the JSON routes.\n */\nexport class AuthenticationError<\n R extends AuthenticationReason = AuthenticationReason,\n> extends Error {\n readonly reason: R;\n readonly metadata: Metadata;\n\n constructor(reason: R, ...[options]: Optional<R> extends true ? [Options<R>?] : [Options<R>]) {\n const given = options;\n const { message, cause, metadata = {} } = given ?? {};\n super(message ?? `Sign-in refused: ${reason}`, { cause });\n this.name = 'AuthenticationError';\n this.reason = reason;\n this.metadata = {};\n for (const [key, value] of Object.entries(metadata)) {\n if (typeof value === 'string') this.metadata[key] = value;\n }\n Object.setPrototypeOf(this, AuthenticationError.prototype);\n }\n}\n"],"mappings":";;;AAgDA,MAAa,yBAAyB;CACpC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;AAQA,MAAa,kBAAkB;CAE7B;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;AACF;;;;;;;;;;;;;;;;AA4CA,IAAa,sBAAb,MAAa,4BAEH,MAAM;CACd;CACA;CAEA,YAAY,QAAW,GAAG,CAAC,UAAmE;EAE5F,MAAM,EAAE,SAAS,OAAO,WAAW,CAAC,MAAMA,WAAS,CAAC;EACpD,MAAM,WAAW,oBAAoB,UAAU,EAAE,MAAM,CAAC;EACxD,KAAK,OAAO;EACZ,KAAK,SAAS;EACd,KAAK,WAAW,CAAC;EACjB,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,GAChD,IAAI,OAAO,UAAU,UAAU,KAAK,SAAS,OAAO;EAEtD,OAAO,eAAe,MAAM,oBAAoB,SAAS;CAC3D;AACF"}
@@ -50,6 +50,33 @@ export interface AuthenticationReasons {
50
50
  }
51
51
  /** The built-in reasons, for a client's message table or a check that every reason is handled. */
52
52
  export declare const AUTHENTICATION_REASONS: readonly ["INVALID_VERIFICATION_CODE", "VERIFICATION_CODE_ATTEMPTS_EXCEEDED", "NONCE_MISSING", "NONCE_MISMATCH", "INVALID_DESKTOP_CODE", "ACCOUNT_DISABLED", "ACCOUNT_LOCKED", "EMAIL_ALREADY_USED", "EMAIL_NOT_VERIFIED", "SIGNUP_DISABLED"];
53
+ /**
54
+ * The reasons the SDK answers with for a request it cannot act on, as the `ErrorInfo.reason` of a
55
+ * `RequestError` (see `respond.ts`): about the request itself, not who is signing in, so the host
56
+ * never throws them. Every throw site is typed against this list, so a new reason is added here
57
+ * first, and a client's message table can cover them all.
58
+ */
59
+ export declare const REQUEST_REASONS: readonly [
60
+ /** 400: a field failed schema validation; a `BadRequest` detail names it. */
61
+ 'INVALID_ARGUMENT',
62
+ /** 400: a login body's `attributes` is not strings to strings within the size limit. */
63
+ 'INVALID_ATTRIBUTES',
64
+ /** 400: a native login's `code_verifier` is not 43-128 characters from `[A-Za-z0-9-._~]`. */
65
+ 'INVALID_CODE_VERIFIER',
66
+ /** 400: a form route was posted a content type other than `application/x-www-form-urlencoded`. */
67
+ 'INVALID_CONTENT_TYPE',
68
+ /** 400: the body of a JSON route is not JSON. */
69
+ 'INVALID_JSON',
70
+ /** 400: the Turnstile token sent with an email code request was rejected. */
71
+ 'TURNSTILE_VERIFICATION_FAILED',
72
+ /** 401: `desktopAuthorize` without a signed-in session. */
73
+ 'UNAUTHENTICATED',
74
+ /** 429: a code was sent to this email within `verificationCode.resendCooldown`. */
75
+ 'VERIFICATION_CODE_RESEND_TOO_SOON',
76
+ /** 503: the email sender threw; the cooldown is released so the user can retry at once. */
77
+ 'EMAIL_DELIVERY_FAILED'];
78
+ /** A reason the SDK answers a malformed, too-soon or unauthenticated request with. */
79
+ export type RequestReason = (typeof REQUEST_REASONS)[number];
53
80
  /**
54
81
  * Each reason's metadata as declared; a field that is not a string makes the reason unusable
55
82
  * (`never`).
@@ -1 +1 @@
1
- {"version":3,"file":"error.d.cts","names":[],"sources":["../../src/auth/error.ts"],"mappings":"A,KAAK,WAAW;;KAEX;GAAgB;;;;;;;;;;;;;;;;iBAgBJ;;EAGf,2BAA2B;;EAE3B,qCAAqC;;EAErC,eAAe;;;;;EAKf,gBAAgB;;EAEhB,sBAAsB;;EAItB,kBAAkB;;EAElB;IAAkB;;;EAElB;IAAsB;;;EAEtB;IAAsB;;;EAEtB,iBAAiB;;;qBAIN;;;;;KAiBR,aACF,WAAW,wBAAwB,sBAAsB,WAAW,QAAQ,YACzE,sBAAsB;;YAKhB,6BAA6B;;KAGpC,SAAS,UAAU,wBAAwB,QAAQ,WAAW,oBAE/D,QAAQ,QAAQ,YAAY,QAAQ;KAGnC,QAAQ,UAAU;EACrB;EACA;KACG,SAAS;EAAoB,WAAW,QAAQ;;EAAS,UAAU,QAAQ;;;;;;;;;;;;;;;;;qBAiBnE,oBACX,UAAU,uBAAuB,8BACzB;WACC,QAAQ;WACR,UAAU;EAEnB,YAAY,QAAQ,OAAO,UAAU,SAAS,mBAAmB,QAAQ,QAAQ,QAAQ"}
1
+ {"version":3,"file":"error.d.cts","names":[],"sources":["../../src/auth/error.ts"],"mappings":"A,KAAK,WAAW;;KAEX;GAAgB;;;;;;;;;;;;;;;;iBAgBJ;;EAGf,2BAA2B;;EAE3B,qCAAqC;;EAErC,eAAe;;;;;EAKf,gBAAgB;;EAEhB,sBAAsB;;EAItB,kBAAkB;;EAElB;IAAkB;;;EAElB;IAAsB;;;EAEtB;IAAsB;;;EAEtB,iBAAiB;;;qBAIN;;;;;;;qBAmBA;;;;;;;;;;;;;;;;;;;;YAsBD,wBAAwB;;;;;KAM/B,aACF,WAAW,wBAAwB,sBAAsB,WAAW,QAAQ,YACzE,sBAAsB;;YAKhB,6BAA6B;;KAGpC,SAAS,UAAU,wBAAwB,QAAQ,WAAW,oBAE/D,QAAQ,QAAQ,YAAY,QAAQ;KAGnC,QAAQ,UAAU;EACrB;EACA;KACG,SAAS;EAAoB,WAAW,QAAQ;;EAAS,UAAU,QAAQ;;;;;;;;;;;;;;;;;qBAiBnE,oBACX,UAAU,uBAAuB,8BACzB;WACC,QAAQ;WACR,UAAU;EAEnB,YAAY,QAAQ,OAAO,UAAU,SAAS,mBAAmB,QAAQ,QAAQ,QAAQ"}
@@ -50,6 +50,33 @@ export interface AuthenticationReasons {
50
50
  }
51
51
  /** The built-in reasons, for a client's message table or a check that every reason is handled. */
52
52
  export declare const AUTHENTICATION_REASONS: readonly ["INVALID_VERIFICATION_CODE", "VERIFICATION_CODE_ATTEMPTS_EXCEEDED", "NONCE_MISSING", "NONCE_MISMATCH", "INVALID_DESKTOP_CODE", "ACCOUNT_DISABLED", "ACCOUNT_LOCKED", "EMAIL_ALREADY_USED", "EMAIL_NOT_VERIFIED", "SIGNUP_DISABLED"];
53
+ /**
54
+ * The reasons the SDK answers with for a request it cannot act on, as the `ErrorInfo.reason` of a
55
+ * `RequestError` (see `respond.ts`): about the request itself, not who is signing in, so the host
56
+ * never throws them. Every throw site is typed against this list, so a new reason is added here
57
+ * first, and a client's message table can cover them all.
58
+ */
59
+ export declare const REQUEST_REASONS: readonly [
60
+ /** 400: a field failed schema validation; a `BadRequest` detail names it. */
61
+ 'INVALID_ARGUMENT',
62
+ /** 400: a login body's `attributes` is not strings to strings within the size limit. */
63
+ 'INVALID_ATTRIBUTES',
64
+ /** 400: a native login's `code_verifier` is not 43-128 characters from `[A-Za-z0-9-._~]`. */
65
+ 'INVALID_CODE_VERIFIER',
66
+ /** 400: a form route was posted a content type other than `application/x-www-form-urlencoded`. */
67
+ 'INVALID_CONTENT_TYPE',
68
+ /** 400: the body of a JSON route is not JSON. */
69
+ 'INVALID_JSON',
70
+ /** 400: the Turnstile token sent with an email code request was rejected. */
71
+ 'TURNSTILE_VERIFICATION_FAILED',
72
+ /** 401: `desktopAuthorize` without a signed-in session. */
73
+ 'UNAUTHENTICATED',
74
+ /** 429: a code was sent to this email within `verificationCode.resendCooldown`. */
75
+ 'VERIFICATION_CODE_RESEND_TOO_SOON',
76
+ /** 503: the email sender threw; the cooldown is released so the user can retry at once. */
77
+ 'EMAIL_DELIVERY_FAILED'];
78
+ /** A reason the SDK answers a malformed, too-soon or unauthenticated request with. */
79
+ export type RequestReason = (typeof REQUEST_REASONS)[number];
53
80
  /**
54
81
  * Each reason's metadata as declared; a field that is not a string makes the reason unusable
55
82
  * (`never`).
@@ -1 +1 @@
1
- {"version":3,"file":"error.d.mts","names":[],"sources":["../../src/auth/error.ts"],"mappings":"A,KAAK,WAAW;;KAEX;GAAgB;;;;;;;;;;;;;;;;iBAgBJ;;EAGf,2BAA2B;;EAE3B,qCAAqC;;EAErC,eAAe;;;;;EAKf,gBAAgB;;EAEhB,sBAAsB;;EAItB,kBAAkB;;EAElB;IAAkB;;;EAElB;IAAsB;;;EAEtB;IAAsB;;;EAEtB,iBAAiB;;;qBAIN;;;;;KAiBR,aACF,WAAW,wBAAwB,sBAAsB,WAAW,QAAQ,YACzE,sBAAsB;;YAKhB,6BAA6B;;KAGpC,SAAS,UAAU,wBAAwB,QAAQ,WAAW,oBAE/D,QAAQ,QAAQ,YAAY,QAAQ;KAGnC,QAAQ,UAAU;EACrB;EACA;KACG,SAAS;EAAoB,WAAW,QAAQ;;EAAS,UAAU,QAAQ;;;;;;;;;;;;;;;;;qBAiBnE,oBACX,UAAU,uBAAuB,8BACzB;WACC,QAAQ;WACR,UAAU;EAEnB,YAAY,QAAQ,OAAO,UAAU,SAAS,mBAAmB,QAAQ,QAAQ,QAAQ"}
1
+ {"version":3,"file":"error.d.mts","names":[],"sources":["../../src/auth/error.ts"],"mappings":"A,KAAK,WAAW;;KAEX;GAAgB;;;;;;;;;;;;;;;;iBAgBJ;;EAGf,2BAA2B;;EAE3B,qCAAqC;;EAErC,eAAe;;;;;EAKf,gBAAgB;;EAEhB,sBAAsB;;EAItB,kBAAkB;;EAElB;IAAkB;;;EAElB;IAAsB;;;EAEtB;IAAsB;;;EAEtB,iBAAiB;;;qBAIN;;;;;;;qBAmBA;;;;;;;;;;;;;;;;;;;;YAsBD,wBAAwB;;;;;KAM/B,aACF,WAAW,wBAAwB,sBAAsB,WAAW,QAAQ,YACzE,sBAAsB;;YAKhB,6BAA6B;;KAGpC,SAAS,UAAU,wBAAwB,QAAQ,WAAW,oBAE/D,QAAQ,QAAQ,YAAY,QAAQ;KAGnC,QAAQ,UAAU;EACrB;EACA;KACG,SAAS;EAAoB,WAAW,QAAQ;;EAAS,UAAU,QAAQ;;;;;;;;;;;;;;;;;qBAiBnE,oBACX,UAAU,uBAAuB,8BACzB;WACC,QAAQ;WACR,UAAU;EAEnB,YAAY,QAAQ,OAAO,UAAU,SAAS,mBAAmB,QAAQ,QAAQ,QAAQ"}
@@ -13,6 +13,23 @@ const AUTHENTICATION_REASONS = [
13
13
  "SIGNUP_DISABLED"
14
14
  ];
15
15
  /**
16
+ * The reasons the SDK answers with for a request it cannot act on, as the `ErrorInfo.reason` of a
17
+ * `RequestError` (see `respond.ts`): about the request itself, not who is signing in, so the host
18
+ * never throws them. Every throw site is typed against this list, so a new reason is added here
19
+ * first, and a client's message table can cover them all.
20
+ */
21
+ const REQUEST_REASONS = [
22
+ "INVALID_ARGUMENT",
23
+ "INVALID_ATTRIBUTES",
24
+ "INVALID_CODE_VERIFIER",
25
+ "INVALID_CONTENT_TYPE",
26
+ "INVALID_JSON",
27
+ "TURNSTILE_VERIFICATION_FAILED",
28
+ "UNAUTHENTICATED",
29
+ "VERIFICATION_CODE_RESEND_TOO_SOON",
30
+ "EMAIL_DELIVERY_FAILED"
31
+ ];
32
+ /**
16
33
  * Thrown by the host's `onAuthorized` to refuse a sign-in it could complete but will not: an
17
34
  * address another account holds, a banned user, a closed beta. `reason` is the code the client
18
35
  * branches on, `metadata` what it needs to act (the provider to sign in with instead); both
@@ -40,6 +57,6 @@ var AuthenticationError = class AuthenticationError extends Error {
40
57
  Object.setPrototypeOf(this, AuthenticationError.prototype);
41
58
  }
42
59
  };
43
- export { AUTHENTICATION_REASONS, AuthenticationError };
60
+ export { AUTHENTICATION_REASONS, AuthenticationError, REQUEST_REASONS };
44
61
 
45
62
  //# sourceMappingURL=error.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"error.mjs","names":["given"],"sources":["../../src/auth/error.ts"],"sourcesContent":["type Metadata = Record<string, string>;\n/** A reason that carries no metadata. */\ntype NoMetadata = { [key: string]: never };\n\n/**\n * The reasons `onAuthorized` may refuse a sign-in with, each with the metadata it carries (string\n * fields only: metadata travels as URL parameters and JSON). The built-in ones cover the account\n * states every host meets; add your own by augmenting the interface, and `AuthenticationError`\n * accepts exactly these reasons, requiring the metadata when one of its fields is required:\n *\n * ```ts\n * declare module '@shware/security' {\n * interface AuthenticationReasons {\n * CLOSED_BETA: { invite?: string };\n * }\n * }\n * ```\n */\nexport interface AuthenticationReasons {\n // Thrown by the SDK itself.\n /** The emailed code is wrong, or none is pending for the address. */\n INVALID_VERIFICATION_CODE: NoMetadata;\n /** More than `verificationCode.maxAttempts` guesses at the code; it is discarded. */\n VERIFICATION_CODE_ATTEMPTS_EXCEEDED: NoMetadata;\n /** The id token carries no nonce and the provider's flow requires one (`oauth2.nonce.optional`). */\n NONCE_MISSING: NoMetadata;\n /**\n * The id token's nonce is not the one this sign-in was started with: not issued for this\n * provider, used already, or (redirect flow) not the one sent with the authorization request.\n */\n NONCE_MISMATCH: NoMetadata;\n /** The desktop sign-in code is unknown, expired, used, or its verifier does not match. */\n INVALID_DESKTOP_CODE: NoMetadata;\n\n // For the host's `onAuthorized`.\n /** The account is disabled: banned, or closed by its owner. */\n ACCOUNT_DISABLED: NoMetadata;\n /** The account is locked for now; `until` is when it opens again, when known. */\n ACCOUNT_LOCKED: { until?: string };\n /** The address belongs to another account; `provider` is the one to sign in with instead. */\n EMAIL_ALREADY_USED: { provider?: string };\n /** The host accepts only verified addresses and this provider did not verify it. */\n EMAIL_NOT_VERIFIED: { provider?: string };\n /** No new accounts: registration is closed or by invitation. */\n SIGNUP_DISABLED: NoMetadata;\n}\n\n/** The built-in reasons, for a client's message table or a check that every reason is handled. */\nexport const AUTHENTICATION_REASONS = [\n 'INVALID_VERIFICATION_CODE',\n 'VERIFICATION_CODE_ATTEMPTS_EXCEEDED',\n 'NONCE_MISSING',\n 'NONCE_MISMATCH',\n 'INVALID_DESKTOP_CODE',\n 'ACCOUNT_DISABLED',\n 'ACCOUNT_LOCKED',\n 'EMAIL_ALREADY_USED',\n 'EMAIL_NOT_VERIFIED',\n 'SIGNUP_DISABLED',\n] as const satisfies readonly (keyof AuthenticationReasons)[];\n\n/**\n * Each reason's metadata as declared; a field that is not a string makes the reason unusable\n * (`never`).\n */\ntype Reasons = {\n [K in keyof AuthenticationReasons]: AuthenticationReasons[K] extends Partial<Metadata>\n ? AuthenticationReasons[K]\n : never;\n};\n\n/** A reason `onAuthorized` may refuse with: the keys of `AuthenticationReasons`. */\nexport type AuthenticationReason = keyof Reasons;\n\n/** `true` when the reason's metadata has no required field, so it may be left out. */\ntype Optional<R extends AuthenticationReason> = Reasons[R] extends NoMetadata\n ? true\n : Partial<Reasons[R]> extends Reasons[R]\n ? true\n : false;\ntype Options<R extends AuthenticationReason> = {\n message?: string;\n cause?: unknown;\n} & (Optional<R> extends true ? { metadata?: Reasons[R] } : { metadata: Reasons[R] });\n\n/**\n * Thrown by the host's `onAuthorized` to refuse a sign-in it could complete but will not: an\n * address another account holds, a banned user, a closed beta. `reason` is the code the client\n * branches on, `metadata` what it needs to act (the provider to sign in with instead); both\n * travel as they are, so keep them short and free of secrets. `Auth` answers a refusal itself,\n * in the shape of the route it came through:\n *\n * - redirect flow (`loginOAuth2Code`): `errorUri?error=access_denied&reason=<reason>&<metadata>`;\n * - JSON routes (native, One Tap, email, desktop): `403` with a google.rpc error body whose\n * `ErrorInfo` holds `reason` and `metadata`.\n *\n * The SDK throws it too, for a credential it checked itself (a wrong emailed code, a replayed\n * nonce), so a client handles both the same way. Anything else `onAuthorized` throws is a server\n * fault: `server_error` on the redirect, rethrown to the host's error handler on the JSON routes.\n */\nexport class AuthenticationError<\n R extends AuthenticationReason = AuthenticationReason,\n> extends Error {\n readonly reason: R;\n readonly metadata: Metadata;\n\n constructor(reason: R, ...[options]: Optional<R> extends true ? [Options<R>?] : [Options<R>]) {\n const given = options;\n const { message, cause, metadata = {} } = given ?? {};\n super(message ?? `Sign-in refused: ${reason}`, { cause });\n this.name = 'AuthenticationError';\n this.reason = reason;\n this.metadata = {};\n for (const [key, value] of Object.entries(metadata)) {\n if (typeof value === 'string') this.metadata[key] = value;\n }\n Object.setPrototypeOf(this, AuthenticationError.prototype);\n }\n}\n"],"mappings":";;AAgDA,MAAa,yBAAyB;CACpC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;;;;;;;;;;AAyCA,IAAa,sBAAb,MAAa,4BAEH,MAAM;CACd;CACA;CAEA,YAAY,QAAW,GAAG,CAAC,UAAmE;EAE5F,MAAM,EAAE,SAAS,OAAO,WAAW,CAAC,MAAMA,WAAS,CAAC;EACpD,MAAM,WAAW,oBAAoB,UAAU,EAAE,MAAM,CAAC;EACxD,KAAK,OAAO;EACZ,KAAK,SAAS;EACd,KAAK,WAAW,CAAC;EACjB,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,GAChD,IAAI,OAAO,UAAU,UAAU,KAAK,SAAS,OAAO;EAEtD,OAAO,eAAe,MAAM,oBAAoB,SAAS;CAC3D;AACF"}
1
+ {"version":3,"file":"error.mjs","names":["given"],"sources":["../../src/auth/error.ts"],"sourcesContent":["type Metadata = Record<string, string>;\n/** A reason that carries no metadata. */\ntype NoMetadata = { [key: string]: never };\n\n/**\n * The reasons `onAuthorized` may refuse a sign-in with, each with the metadata it carries (string\n * fields only: metadata travels as URL parameters and JSON). The built-in ones cover the account\n * states every host meets; add your own by augmenting the interface, and `AuthenticationError`\n * accepts exactly these reasons, requiring the metadata when one of its fields is required:\n *\n * ```ts\n * declare module '@shware/security' {\n * interface AuthenticationReasons {\n * CLOSED_BETA: { invite?: string };\n * }\n * }\n * ```\n */\nexport interface AuthenticationReasons {\n // Thrown by the SDK itself.\n /** The emailed code is wrong, or none is pending for the address. */\n INVALID_VERIFICATION_CODE: NoMetadata;\n /** More than `verificationCode.maxAttempts` guesses at the code; it is discarded. */\n VERIFICATION_CODE_ATTEMPTS_EXCEEDED: NoMetadata;\n /** The id token carries no nonce and the provider's flow requires one (`oauth2.nonce.optional`). */\n NONCE_MISSING: NoMetadata;\n /**\n * The id token's nonce is not the one this sign-in was started with: not issued for this\n * provider, used already, or (redirect flow) not the one sent with the authorization request.\n */\n NONCE_MISMATCH: NoMetadata;\n /** The desktop sign-in code is unknown, expired, used, or its verifier does not match. */\n INVALID_DESKTOP_CODE: NoMetadata;\n\n // For the host's `onAuthorized`.\n /** The account is disabled: banned, or closed by its owner. */\n ACCOUNT_DISABLED: NoMetadata;\n /** The account is locked for now; `until` is when it opens again, when known. */\n ACCOUNT_LOCKED: { until?: string };\n /** The address belongs to another account; `provider` is the one to sign in with instead. */\n EMAIL_ALREADY_USED: { provider?: string };\n /** The host accepts only verified addresses and this provider did not verify it. */\n EMAIL_NOT_VERIFIED: { provider?: string };\n /** No new accounts: registration is closed or by invitation. */\n SIGNUP_DISABLED: NoMetadata;\n}\n\n/** The built-in reasons, for a client's message table or a check that every reason is handled. */\nexport const AUTHENTICATION_REASONS = [\n 'INVALID_VERIFICATION_CODE',\n 'VERIFICATION_CODE_ATTEMPTS_EXCEEDED',\n 'NONCE_MISSING',\n 'NONCE_MISMATCH',\n 'INVALID_DESKTOP_CODE',\n 'ACCOUNT_DISABLED',\n 'ACCOUNT_LOCKED',\n 'EMAIL_ALREADY_USED',\n 'EMAIL_NOT_VERIFIED',\n 'SIGNUP_DISABLED',\n] as const satisfies readonly (keyof AuthenticationReasons)[];\n\n/**\n * The reasons the SDK answers with for a request it cannot act on, as the `ErrorInfo.reason` of a\n * `RequestError` (see `respond.ts`): about the request itself, not who is signing in, so the host\n * never throws them. Every throw site is typed against this list, so a new reason is added here\n * first, and a client's message table can cover them all.\n */\nexport const REQUEST_REASONS = [\n /** 400: a field failed schema validation; a `BadRequest` detail names it. */\n 'INVALID_ARGUMENT',\n /** 400: a login body's `attributes` is not strings to strings within the size limit. */\n 'INVALID_ATTRIBUTES',\n /** 400: a native login's `code_verifier` is not 43-128 characters from `[A-Za-z0-9-._~]`. */\n 'INVALID_CODE_VERIFIER',\n /** 400: a form route was posted a content type other than `application/x-www-form-urlencoded`. */\n 'INVALID_CONTENT_TYPE',\n /** 400: the body of a JSON route is not JSON. */\n 'INVALID_JSON',\n /** 400: the Turnstile token sent with an email code request was rejected. */\n 'TURNSTILE_VERIFICATION_FAILED',\n /** 401: `desktopAuthorize` without a signed-in session. */\n 'UNAUTHENTICATED',\n /** 429: a code was sent to this email within `verificationCode.resendCooldown`. */\n 'VERIFICATION_CODE_RESEND_TOO_SOON',\n /** 503: the email sender threw; the cooldown is released so the user can retry at once. */\n 'EMAIL_DELIVERY_FAILED',\n] as const;\n\n/** A reason the SDK answers a malformed, too-soon or unauthenticated request with. */\nexport type RequestReason = (typeof REQUEST_REASONS)[number];\n\n/**\n * Each reason's metadata as declared; a field that is not a string makes the reason unusable\n * (`never`).\n */\ntype Reasons = {\n [K in keyof AuthenticationReasons]: AuthenticationReasons[K] extends Partial<Metadata>\n ? AuthenticationReasons[K]\n : never;\n};\n\n/** A reason `onAuthorized` may refuse with: the keys of `AuthenticationReasons`. */\nexport type AuthenticationReason = keyof Reasons;\n\n/** `true` when the reason's metadata has no required field, so it may be left out. */\ntype Optional<R extends AuthenticationReason> = Reasons[R] extends NoMetadata\n ? true\n : Partial<Reasons[R]> extends Reasons[R]\n ? true\n : false;\ntype Options<R extends AuthenticationReason> = {\n message?: string;\n cause?: unknown;\n} & (Optional<R> extends true ? { metadata?: Reasons[R] } : { metadata: Reasons[R] });\n\n/**\n * Thrown by the host's `onAuthorized` to refuse a sign-in it could complete but will not: an\n * address another account holds, a banned user, a closed beta. `reason` is the code the client\n * branches on, `metadata` what it needs to act (the provider to sign in with instead); both\n * travel as they are, so keep them short and free of secrets. `Auth` answers a refusal itself,\n * in the shape of the route it came through:\n *\n * - redirect flow (`loginOAuth2Code`): `errorUri?error=access_denied&reason=<reason>&<metadata>`;\n * - JSON routes (native, One Tap, email, desktop): `403` with a google.rpc error body whose\n * `ErrorInfo` holds `reason` and `metadata`.\n *\n * The SDK throws it too, for a credential it checked itself (a wrong emailed code, a replayed\n * nonce), so a client handles both the same way. Anything else `onAuthorized` throws is a server\n * fault: `server_error` on the redirect, rethrown to the host's error handler on the JSON routes.\n */\nexport class AuthenticationError<\n R extends AuthenticationReason = AuthenticationReason,\n> extends Error {\n readonly reason: R;\n readonly metadata: Metadata;\n\n constructor(reason: R, ...[options]: Optional<R> extends true ? [Options<R>?] : [Options<R>]) {\n const given = options;\n const { message, cause, metadata = {} } = given ?? {};\n super(message ?? `Sign-in refused: ${reason}`, { cause });\n this.name = 'AuthenticationError';\n this.reason = reason;\n this.metadata = {};\n for (const [key, value] of Object.entries(metadata)) {\n if (typeof value === 'string') this.metadata[key] = value;\n }\n Object.setPrototypeOf(this, AuthenticationError.prototype);\n }\n}\n"],"mappings":";;AAgDA,MAAa,yBAAyB;CACpC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;AAQA,MAAa,kBAAkB;CAE7B;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;AACF;;;;;;;;;;;;;;;;AA4CA,IAAa,sBAAb,MAAa,4BAEH,MAAM;CACd;CACA;CAEA,YAAY,QAAW,GAAG,CAAC,UAAmE;EAE5F,MAAM,EAAE,SAAS,OAAO,WAAW,CAAC,MAAMA,WAAS,CAAC;EACpD,MAAM,WAAW,oBAAoB,UAAU,EAAE,MAAM,CAAC;EACxD,KAAK,OAAO;EACZ,KAAK,SAAS;EACd,KAAK,WAAW,CAAC;EACjB,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,GAChD,IAAI,OAAO,UAAU,UAAU,KAAK,SAAS,OAAO;EAEtD,OAAO,eAAe,MAAM,oBAAoB,SAAS;CAC3D;AACF"}
@@ -30,9 +30,14 @@ var RequestError = class RequestError extends Error {
30
30
  Object.setPrototypeOf(this, RequestError.prototype);
31
31
  }
32
32
  };
33
- function invalidArgument(reason, message, details) {
33
+ /**
34
+ * A malformed request: the `ErrorInfo` with `reason` first, then any further `details` (a
35
+ * `BadRequest` naming the fields a schema refused), so `getErrorInfo` finds the reason whatever
36
+ * else the body carries.
37
+ */
38
+ function invalidArgument(reason, message, details = []) {
34
39
  const body = errorBody(400, "INVALID_ARGUMENT", reason, message);
35
- if (details) body.error.details = details;
40
+ body.error.details.push(...details);
36
41
  return new RequestError(body);
37
42
  }
38
43
  function unauthenticated(message) {
@@ -1 +1 @@
1
- {"version":3,"file":"respond.cjs","names":["AuthenticationError","OAuth2Error"],"sources":["../../src/auth/respond.ts"],"sourcesContent":["import { OAuth2Error, type OAuth2ErrorType } from '../oauth2/error';\nimport { AuthenticationError } from './error';\n\n/**\n * How the JSON routes answer a failure: a google.rpc error body, as `@shware/http` reads it, with\n * one `ErrorInfo` whose `reason` the client branches on. The redirect flow answers differently\n * (`error=…` on `errorUri`, see `Auth.redirect`), since a page, not a client, reads it.\n */\nexport interface ErrorBody {\n error: {\n code: number;\n status: string;\n message: string;\n details: unknown[];\n };\n}\n\nconst ERROR_INFO = 'type.googleapis.com/google.rpc.ErrorInfo';\n\nexport function errorBody(\n code: number,\n status: string,\n reason: string,\n message: string,\n metadata?: Record<string, string>\n): ErrorBody {\n const info = { '@type': ERROR_INFO, reason, ...(metadata ? { metadata } : {}) };\n return { error: { code, status, message, details: [info] } };\n}\n\n/**\n * A request a route cannot act on, answered as it is: malformed, too soon, not signed in. Thrown\n * by the validation helpers so a route reads as one straight line; `errorResponse` turns it into\n * the response. Unlike `AuthenticationError`, which is about who is signing in, this is about\n * the request itself, so it is not the host's to throw.\n */\nexport class RequestError extends Error {\n readonly body: ErrorBody;\n\n constructor(body: ErrorBody) {\n super(body.error.message);\n this.name = 'RequestError';\n this.body = body;\n Object.setPrototypeOf(this, RequestError.prototype);\n }\n}\n\nexport function invalidArgument(reason: string, message: string, details?: unknown[]) {\n const body = errorBody(400, 'INVALID_ARGUMENT', reason, message);\n if (details) body.error.details = details;\n return new RequestError(body);\n}\n\nexport function unauthenticated(message: string) {\n return new RequestError(errorBody(401, 'UNAUTHENTICATED', 'UNAUTHENTICATED', message));\n}\n\nexport function resourceExhausted(reason: string, message: string) {\n return new RequestError(errorBody(429, 'RESOURCE_EXHAUSTED', reason, message));\n}\n\nexport function unavailable(reason: string, message: string) {\n return new RequestError(errorBody(503, 'UNAVAILABLE', reason, message));\n}\n\n/**\n * What an `OAuth2Error` from the provider client means to the caller of a JSON route: the request\n * named something that does not exist (400), the credential it sent was refused (403), or the\n * provider is down (503). Its message stays in the logs: it can name a client id or quote the\n * provider's response.\n */\nconst OAUTH2_STATUS: Record<OAuth2ErrorType, [code: number, status: string]> = {\n invalid_request: [400, 'INVALID_ARGUMENT'],\n invalid_scope: [400, 'INVALID_ARGUMENT'],\n unsupported_grant_type: [400, 'INVALID_ARGUMENT'],\n unsupported_response_type: [400, 'INVALID_ARGUMENT'],\n redirect_uri_mismatch: [400, 'INVALID_ARGUMENT'],\n invalid_client: [403, 'PERMISSION_DENIED'],\n invalid_grant: [403, 'PERMISSION_DENIED'],\n invalid_token: [403, 'PERMISSION_DENIED'],\n unauthorized_client: [403, 'PERMISSION_DENIED'],\n insufficient_scope: [403, 'PERMISSION_DENIED'],\n access_denied: [403, 'PERMISSION_DENIED'],\n server_error: [503, 'UNAVAILABLE'],\n temporarily_unavailable: [503, 'UNAVAILABLE'],\n};\n\n/**\n * The response a JSON route gives for an error it knows, or `null` for one it does not, which is\n * the host's (a 500). Every JSON route runs under this, so the three kinds of failure have one\n * mapping: a `RequestError` as it is, an `AuthenticationError` as `403 PERMISSION_DENIED` with the\n * reason and metadata (the host's refusal or the SDK's own: a wrong code, a replayed nonce), an\n * `OAuth2Error` by what it means (`OAUTH2_<error>` as the reason). Exported for the host's error\n * handler, so a flow of its own that calls `Auth` directly (`verifyEmailCode`) answers the same way:\n * `errorResponse(error) ?? yourHandler(error)`.\n */\nexport function errorResponse(error: unknown): Response | null {\n if (error instanceof RequestError) {\n return Response.json(error.body, { status: error.body.error.code });\n }\n if (error instanceof AuthenticationError) {\n const body = errorBody(403, 'PERMISSION_DENIED', error.reason, error.message, error.metadata);\n return Response.json(body, { status: body.error.code });\n }\n if (error instanceof OAuth2Error) {\n console.warn('OAuth2 login failed', error);\n const [code, status] = OAUTH2_STATUS[error.error] ?? [503, 'UNAVAILABLE'];\n const reason = `OAUTH2_${error.error.toUpperCase().replace(/[^A-Z0-9]/g, '_')}`;\n const body = errorBody(code, status, reason, `OAuth2 ${error.error}`);\n return Response.json(body, { status: body.error.code });\n }\n return null;\n}\n"],"mappings":";;;;AAiBA,MAAM,aAAa;AAEnB,SAAgB,UACd,MACA,QACA,QACA,SACA,UACW;CAEX,OAAO,EAAE,OAAO;EAAE;EAAM;EAAQ;EAAS,SAAS,CAAC;GADpC,SAAS;GAAY;GAAQ,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;EACrB,CAAC;CAAE,EAAE;AAC7D;;;;;;;AAQA,IAAa,eAAb,MAAa,qBAAqB,MAAM;CACtC;CAEA,YAAY,MAAiB;EAC3B,MAAM,KAAK,MAAM,OAAO;EACxB,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,aAAa,SAAS;CACpD;AACF;AAEA,SAAgB,gBAAgB,QAAgB,SAAiB,SAAqB;CACpF,MAAM,OAAO,UAAU,KAAK,oBAAoB,QAAQ,OAAO;CAC/D,IAAI,SAAS,KAAK,MAAM,UAAU;CAClC,OAAO,IAAI,aAAa,IAAI;AAC9B;AAEA,SAAgB,gBAAgB,SAAiB;CAC/C,OAAO,IAAI,aAAa,UAAU,KAAK,mBAAmB,mBAAmB,OAAO,CAAC;AACvF;AAEA,SAAgB,kBAAkB,QAAgB,SAAiB;CACjE,OAAO,IAAI,aAAa,UAAU,KAAK,sBAAsB,QAAQ,OAAO,CAAC;AAC/E;AAEA,SAAgB,YAAY,QAAgB,SAAiB;CAC3D,OAAO,IAAI,aAAa,UAAU,KAAK,eAAe,QAAQ,OAAO,CAAC;AACxE;;;;;;;AAQA,MAAM,gBAAyE;CAC7E,iBAAiB,CAAC,KAAK,kBAAkB;CACzC,eAAe,CAAC,KAAK,kBAAkB;CACvC,wBAAwB,CAAC,KAAK,kBAAkB;CAChD,2BAA2B,CAAC,KAAK,kBAAkB;CACnD,uBAAuB,CAAC,KAAK,kBAAkB;CAC/C,gBAAgB,CAAC,KAAK,mBAAmB;CACzC,eAAe,CAAC,KAAK,mBAAmB;CACxC,eAAe,CAAC,KAAK,mBAAmB;CACxC,qBAAqB,CAAC,KAAK,mBAAmB;CAC9C,oBAAoB,CAAC,KAAK,mBAAmB;CAC7C,eAAe,CAAC,KAAK,mBAAmB;CACxC,cAAc,CAAC,KAAK,aAAa;CACjC,yBAAyB,CAAC,KAAK,aAAa;AAC9C;;;;;;;;;;AAWA,SAAgB,cAAc,OAAiC;CAC7D,IAAI,iBAAiB,cACnB,OAAO,SAAS,KAAK,MAAM,MAAM,EAAE,QAAQ,MAAM,KAAK,MAAM,KAAK,CAAC;CAEpE,IAAI,iBAAiBA,mBAAAA,qBAAqB;EACxC,MAAM,OAAO,UAAU,KAAK,qBAAqB,MAAM,QAAQ,MAAM,SAAS,MAAM,QAAQ;EAC5F,OAAO,SAAS,KAAK,MAAM,EAAE,QAAQ,KAAK,MAAM,KAAK,CAAC;CACxD;CACA,IAAI,iBAAiBC,qBAAAA,aAAa;EAChC,QAAQ,KAAK,uBAAuB,KAAK;EACzC,MAAM,CAAC,MAAM,UAAU,cAAc,MAAM,UAAU,CAAC,KAAK,aAAa;EAExE,MAAM,OAAO,UAAU,MAAM,QAAQ,UADZ,MAAM,MAAM,YAAY,CAAC,CAAC,QAAQ,cAAc,GAAG,KAC/B,UAAU,MAAM,OAAO;EACpE,OAAO,SAAS,KAAK,MAAM,EAAE,QAAQ,KAAK,MAAM,KAAK,CAAC;CACxD;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"respond.cjs","names":["AuthenticationError","OAuth2Error"],"sources":["../../src/auth/respond.ts"],"sourcesContent":["import { OAuth2Error, type OAuth2ErrorType } from '../oauth2/error';\nimport { AuthenticationError, type RequestReason } from './error';\n\n/**\n * How the JSON routes answer a failure: a google.rpc error body, as `@shware/http` reads it, with\n * one `ErrorInfo` whose `reason` the client branches on. The redirect flow answers differently\n * (`error=…` on `errorUri`, see `Auth.redirect`), since a page, not a client, reads it.\n */\nexport interface ErrorBody {\n error: {\n code: number;\n status: string;\n message: string;\n details: unknown[];\n };\n}\n\nconst ERROR_INFO = 'type.googleapis.com/google.rpc.ErrorInfo';\n\nexport function errorBody(\n code: number,\n status: string,\n reason: string,\n message: string,\n metadata?: Record<string, string>\n): ErrorBody {\n const info = { '@type': ERROR_INFO, reason, ...(metadata ? { metadata } : {}) };\n return { error: { code, status, message, details: [info] } };\n}\n\n/**\n * A request a route cannot act on, answered as it is: malformed, too soon, not signed in. Thrown\n * by the validation helpers so a route reads as one straight line; `errorResponse` turns it into\n * the response. Unlike `AuthenticationError`, which is about who is signing in, this is about\n * the request itself, so it is not the host's to throw.\n */\nexport class RequestError extends Error {\n readonly body: ErrorBody;\n\n constructor(body: ErrorBody) {\n super(body.error.message);\n this.name = 'RequestError';\n this.body = body;\n Object.setPrototypeOf(this, RequestError.prototype);\n }\n}\n\n/**\n * A malformed request: the `ErrorInfo` with `reason` first, then any further `details` (a\n * `BadRequest` naming the fields a schema refused), so `getErrorInfo` finds the reason whatever\n * else the body carries.\n */\nexport function invalidArgument(reason: RequestReason, message: string, details: unknown[] = []) {\n const body = errorBody(400, 'INVALID_ARGUMENT', reason, message);\n body.error.details.push(...details);\n return new RequestError(body);\n}\n\nexport function unauthenticated(message: string) {\n const reason: RequestReason = 'UNAUTHENTICATED';\n return new RequestError(errorBody(401, 'UNAUTHENTICATED', reason, message));\n}\n\nexport function resourceExhausted(reason: RequestReason, message: string) {\n return new RequestError(errorBody(429, 'RESOURCE_EXHAUSTED', reason, message));\n}\n\nexport function unavailable(reason: RequestReason, message: string) {\n return new RequestError(errorBody(503, 'UNAVAILABLE', reason, message));\n}\n\n/**\n * What an `OAuth2Error` from the provider client means to the caller of a JSON route: the request\n * named something that does not exist (400), the credential it sent was refused (403), or the\n * provider is down (503). Its message stays in the logs: it can name a client id or quote the\n * provider's response.\n */\nconst OAUTH2_STATUS: Record<OAuth2ErrorType, [code: number, status: string]> = {\n invalid_request: [400, 'INVALID_ARGUMENT'],\n invalid_scope: [400, 'INVALID_ARGUMENT'],\n unsupported_grant_type: [400, 'INVALID_ARGUMENT'],\n unsupported_response_type: [400, 'INVALID_ARGUMENT'],\n redirect_uri_mismatch: [400, 'INVALID_ARGUMENT'],\n invalid_client: [403, 'PERMISSION_DENIED'],\n invalid_grant: [403, 'PERMISSION_DENIED'],\n invalid_token: [403, 'PERMISSION_DENIED'],\n unauthorized_client: [403, 'PERMISSION_DENIED'],\n insufficient_scope: [403, 'PERMISSION_DENIED'],\n access_denied: [403, 'PERMISSION_DENIED'],\n server_error: [503, 'UNAVAILABLE'],\n temporarily_unavailable: [503, 'UNAVAILABLE'],\n};\n\n/**\n * The response a JSON route gives for an error it knows, or `null` for one it does not, which is\n * the host's (a 500). Every JSON route runs under this, so the three kinds of failure have one\n * mapping: a `RequestError` as it is, an `AuthenticationError` as `403 PERMISSION_DENIED` with the\n * reason and metadata (the host's refusal or the SDK's own: a wrong code, a replayed nonce), an\n * `OAuth2Error` by what it means (`OAUTH2_<error>` as the reason). Exported for the host's error\n * handler, so a flow of its own that calls `Auth` directly (`verifyEmailCode`) answers the same way:\n * `errorResponse(error) ?? yourHandler(error)`.\n */\nexport function errorResponse(error: unknown): Response | null {\n if (error instanceof RequestError) {\n return Response.json(error.body, { status: error.body.error.code });\n }\n if (error instanceof AuthenticationError) {\n const body = errorBody(403, 'PERMISSION_DENIED', error.reason, error.message, error.metadata);\n return Response.json(body, { status: body.error.code });\n }\n if (error instanceof OAuth2Error) {\n console.warn('OAuth2 login failed', error);\n const [code, status] = OAUTH2_STATUS[error.error] ?? [503, 'UNAVAILABLE'];\n const reason = `OAUTH2_${error.error.toUpperCase().replace(/[^A-Z0-9]/g, '_')}`;\n const body = errorBody(code, status, reason, `OAuth2 ${error.error}`);\n return Response.json(body, { status: body.error.code });\n }\n return null;\n}\n"],"mappings":";;;;AAiBA,MAAM,aAAa;AAEnB,SAAgB,UACd,MACA,QACA,QACA,SACA,UACW;CAEX,OAAO,EAAE,OAAO;EAAE;EAAM;EAAQ;EAAS,SAAS,CAAC;GADpC,SAAS;GAAY;GAAQ,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;EACrB,CAAC;CAAE,EAAE;AAC7D;;;;;;;AAQA,IAAa,eAAb,MAAa,qBAAqB,MAAM;CACtC;CAEA,YAAY,MAAiB;EAC3B,MAAM,KAAK,MAAM,OAAO;EACxB,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,aAAa,SAAS;CACpD;AACF;;;;;;AAOA,SAAgB,gBAAgB,QAAuB,SAAiB,UAAqB,CAAC,GAAG;CAC/F,MAAM,OAAO,UAAU,KAAK,oBAAoB,QAAQ,OAAO;CAC/D,KAAK,MAAM,QAAQ,KAAK,GAAG,OAAO;CAClC,OAAO,IAAI,aAAa,IAAI;AAC9B;AAEA,SAAgB,gBAAgB,SAAiB;CAE/C,OAAO,IAAI,aAAa,UAAU,KAAK,mBAAmB,mBAAQ,OAAO,CAAC;AAC5E;AAEA,SAAgB,kBAAkB,QAAuB,SAAiB;CACxE,OAAO,IAAI,aAAa,UAAU,KAAK,sBAAsB,QAAQ,OAAO,CAAC;AAC/E;AAEA,SAAgB,YAAY,QAAuB,SAAiB;CAClE,OAAO,IAAI,aAAa,UAAU,KAAK,eAAe,QAAQ,OAAO,CAAC;AACxE;;;;;;;AAQA,MAAM,gBAAyE;CAC7E,iBAAiB,CAAC,KAAK,kBAAkB;CACzC,eAAe,CAAC,KAAK,kBAAkB;CACvC,wBAAwB,CAAC,KAAK,kBAAkB;CAChD,2BAA2B,CAAC,KAAK,kBAAkB;CACnD,uBAAuB,CAAC,KAAK,kBAAkB;CAC/C,gBAAgB,CAAC,KAAK,mBAAmB;CACzC,eAAe,CAAC,KAAK,mBAAmB;CACxC,eAAe,CAAC,KAAK,mBAAmB;CACxC,qBAAqB,CAAC,KAAK,mBAAmB;CAC9C,oBAAoB,CAAC,KAAK,mBAAmB;CAC7C,eAAe,CAAC,KAAK,mBAAmB;CACxC,cAAc,CAAC,KAAK,aAAa;CACjC,yBAAyB,CAAC,KAAK,aAAa;AAC9C;;;;;;;;;;AAWA,SAAgB,cAAc,OAAiC;CAC7D,IAAI,iBAAiB,cACnB,OAAO,SAAS,KAAK,MAAM,MAAM,EAAE,QAAQ,MAAM,KAAK,MAAM,KAAK,CAAC;CAEpE,IAAI,iBAAiBA,mBAAAA,qBAAqB;EACxC,MAAM,OAAO,UAAU,KAAK,qBAAqB,MAAM,QAAQ,MAAM,SAAS,MAAM,QAAQ;EAC5F,OAAO,SAAS,KAAK,MAAM,EAAE,QAAQ,KAAK,MAAM,KAAK,CAAC;CACxD;CACA,IAAI,iBAAiBC,qBAAAA,aAAa;EAChC,QAAQ,KAAK,uBAAuB,KAAK;EACzC,MAAM,CAAC,MAAM,UAAU,cAAc,MAAM,UAAU,CAAC,KAAK,aAAa;EAExE,MAAM,OAAO,UAAU,MAAM,QAAQ,UADZ,MAAM,MAAM,YAAY,CAAC,CAAC,QAAQ,cAAc,GAAG,KAC/B,UAAU,MAAM,OAAO;EACpE,OAAO,SAAS,KAAK,MAAM,EAAE,QAAQ,KAAK,MAAM,KAAK,CAAC;CACxD;CACA,OAAO;AACT"}
@@ -1,3 +1,4 @@
1
+ import { RequestReason } from "./error.cjs";
1
2
  /**
2
3
  * How the JSON routes answer a failure: a google.rpc error body, as `@shware/http` reads it, with
3
4
  * one `ErrorInfo` whose `reason` the client branches on. The redirect flow answers differently
@@ -22,10 +23,15 @@ export declare class RequestError extends Error {
22
23
  readonly body: ErrorBody;
23
24
  constructor(body: ErrorBody);
24
25
  }
25
- export declare function invalidArgument(reason: string, message: string, details?: unknown[]): RequestError;
26
+ /**
27
+ * A malformed request: the `ErrorInfo` with `reason` first, then any further `details` (a
28
+ * `BadRequest` naming the fields a schema refused), so `getErrorInfo` finds the reason whatever
29
+ * else the body carries.
30
+ */
31
+ export declare function invalidArgument(reason: RequestReason, message: string, details?: unknown[]): RequestError;
26
32
  export declare function unauthenticated(message: string): RequestError;
27
- export declare function resourceExhausted(reason: string, message: string): RequestError;
28
- export declare function unavailable(reason: string, message: string): RequestError;
33
+ export declare function resourceExhausted(reason: RequestReason, message: string): RequestError;
34
+ export declare function unavailable(reason: RequestReason, message: string): RequestError;
29
35
  /**
30
36
  * The response a JSON route gives for an error it knows, or `null` for one it does not, which is
31
37
  * the host's (a 500). Every JSON route runs under this, so the three kinds of failure have one
@@ -1 +1 @@
1
- {"version":3,"file":"respond.d.cts","names":[],"sources":["../../src/auth/respond.ts"],"mappings":";;;;;iBAQiB;EACf;IACE;IACA;IACA;IACA;;;wBAMY,UACd,cACA,gBACA,gBACA,iBACA,WAAW,yBACV;;;;;;;qBAWU,qBAAqB;WACvB,MAAM;EAEf,YAAY,MAAM;;wBAQJ,gBAAgB,gBAAgB,iBAAiB,sBAAmB;wBAMpE,gBAAgB,kBAAe;wBAI/B,kBAAkB,gBAAgB,kBAAe;wBAIjD,YAAY,gBAAgB,kBAAe;;;;;;;;;;wBAmC3C,cAAc,iBAAiB"}
1
+ {"version":3,"file":"respond.d.cts","names":[],"sources":["../../src/auth/respond.ts"],"mappings":";;;;;;iBAQiB;EACf;IACE;IACA;IACA;IACA;;;wBAMY,UACd,cACA,gBACA,gBACA,iBACA,WAAW,yBACV;;;;;;;qBAWU,qBAAqB;WACvB,MAAM;EAEf,YAAY,MAAM;;;;;;;wBAaJ,gBAAgB,QAAQ,eAAe,iBAAiB,sBAAuB;wBAM/E,gBAAgB,kBAAe;wBAK/B,kBAAkB,QAAQ,eAAe,kBAAe;wBAIxD,YAAY,QAAQ,eAAe,kBAAe;;;;;;;;;;wBAmClD,cAAc,iBAAiB"}
@@ -1,3 +1,4 @@
1
+ import { RequestReason } from "./error.mjs";
1
2
  /**
2
3
  * How the JSON routes answer a failure: a google.rpc error body, as `@shware/http` reads it, with
3
4
  * one `ErrorInfo` whose `reason` the client branches on. The redirect flow answers differently
@@ -22,10 +23,15 @@ export declare class RequestError extends Error {
22
23
  readonly body: ErrorBody;
23
24
  constructor(body: ErrorBody);
24
25
  }
25
- export declare function invalidArgument(reason: string, message: string, details?: unknown[]): RequestError;
26
+ /**
27
+ * A malformed request: the `ErrorInfo` with `reason` first, then any further `details` (a
28
+ * `BadRequest` naming the fields a schema refused), so `getErrorInfo` finds the reason whatever
29
+ * else the body carries.
30
+ */
31
+ export declare function invalidArgument(reason: RequestReason, message: string, details?: unknown[]): RequestError;
26
32
  export declare function unauthenticated(message: string): RequestError;
27
- export declare function resourceExhausted(reason: string, message: string): RequestError;
28
- export declare function unavailable(reason: string, message: string): RequestError;
33
+ export declare function resourceExhausted(reason: RequestReason, message: string): RequestError;
34
+ export declare function unavailable(reason: RequestReason, message: string): RequestError;
29
35
  /**
30
36
  * The response a JSON route gives for an error it knows, or `null` for one it does not, which is
31
37
  * the host's (a 500). Every JSON route runs under this, so the three kinds of failure have one
@@ -1 +1 @@
1
- {"version":3,"file":"respond.d.mts","names":[],"sources":["../../src/auth/respond.ts"],"mappings":";;;;;iBAQiB;EACf;IACE;IACA;IACA;IACA;;;wBAMY,UACd,cACA,gBACA,gBACA,iBACA,WAAW,yBACV;;;;;;;qBAWU,qBAAqB;WACvB,MAAM;EAEf,YAAY,MAAM;;wBAQJ,gBAAgB,gBAAgB,iBAAiB,sBAAmB;wBAMpE,gBAAgB,kBAAe;wBAI/B,kBAAkB,gBAAgB,kBAAe;wBAIjD,YAAY,gBAAgB,kBAAe;;;;;;;;;;wBAmC3C,cAAc,iBAAiB"}
1
+ {"version":3,"file":"respond.d.mts","names":[],"sources":["../../src/auth/respond.ts"],"mappings":";;;;;;iBAQiB;EACf;IACE;IACA;IACA;IACA;;;wBAMY,UACd,cACA,gBACA,gBACA,iBACA,WAAW,yBACV;;;;;;;qBAWU,qBAAqB;WACvB,MAAM;EAEf,YAAY,MAAM;;;;;;;wBAaJ,gBAAgB,QAAQ,eAAe,iBAAiB,sBAAuB;wBAM/E,gBAAgB,kBAAe;wBAK/B,kBAAkB,QAAQ,eAAe,kBAAe;wBAIxD,YAAY,QAAQ,eAAe,kBAAe;;;;;;;;;;wBAmClD,cAAc,iBAAiB"}
@@ -29,9 +29,14 @@ var RequestError = class RequestError extends Error {
29
29
  Object.setPrototypeOf(this, RequestError.prototype);
30
30
  }
31
31
  };
32
- function invalidArgument(reason, message, details) {
32
+ /**
33
+ * A malformed request: the `ErrorInfo` with `reason` first, then any further `details` (a
34
+ * `BadRequest` naming the fields a schema refused), so `getErrorInfo` finds the reason whatever
35
+ * else the body carries.
36
+ */
37
+ function invalidArgument(reason, message, details = []) {
33
38
  const body = errorBody(400, "INVALID_ARGUMENT", reason, message);
34
- if (details) body.error.details = details;
39
+ body.error.details.push(...details);
35
40
  return new RequestError(body);
36
41
  }
37
42
  function unauthenticated(message) {
@@ -1 +1 @@
1
- {"version":3,"file":"respond.mjs","names":[],"sources":["../../src/auth/respond.ts"],"sourcesContent":["import { OAuth2Error, type OAuth2ErrorType } from '../oauth2/error';\nimport { AuthenticationError } from './error';\n\n/**\n * How the JSON routes answer a failure: a google.rpc error body, as `@shware/http` reads it, with\n * one `ErrorInfo` whose `reason` the client branches on. The redirect flow answers differently\n * (`error=…` on `errorUri`, see `Auth.redirect`), since a page, not a client, reads it.\n */\nexport interface ErrorBody {\n error: {\n code: number;\n status: string;\n message: string;\n details: unknown[];\n };\n}\n\nconst ERROR_INFO = 'type.googleapis.com/google.rpc.ErrorInfo';\n\nexport function errorBody(\n code: number,\n status: string,\n reason: string,\n message: string,\n metadata?: Record<string, string>\n): ErrorBody {\n const info = { '@type': ERROR_INFO, reason, ...(metadata ? { metadata } : {}) };\n return { error: { code, status, message, details: [info] } };\n}\n\n/**\n * A request a route cannot act on, answered as it is: malformed, too soon, not signed in. Thrown\n * by the validation helpers so a route reads as one straight line; `errorResponse` turns it into\n * the response. Unlike `AuthenticationError`, which is about who is signing in, this is about\n * the request itself, so it is not the host's to throw.\n */\nexport class RequestError extends Error {\n readonly body: ErrorBody;\n\n constructor(body: ErrorBody) {\n super(body.error.message);\n this.name = 'RequestError';\n this.body = body;\n Object.setPrototypeOf(this, RequestError.prototype);\n }\n}\n\nexport function invalidArgument(reason: string, message: string, details?: unknown[]) {\n const body = errorBody(400, 'INVALID_ARGUMENT', reason, message);\n if (details) body.error.details = details;\n return new RequestError(body);\n}\n\nexport function unauthenticated(message: string) {\n return new RequestError(errorBody(401, 'UNAUTHENTICATED', 'UNAUTHENTICATED', message));\n}\n\nexport function resourceExhausted(reason: string, message: string) {\n return new RequestError(errorBody(429, 'RESOURCE_EXHAUSTED', reason, message));\n}\n\nexport function unavailable(reason: string, message: string) {\n return new RequestError(errorBody(503, 'UNAVAILABLE', reason, message));\n}\n\n/**\n * What an `OAuth2Error` from the provider client means to the caller of a JSON route: the request\n * named something that does not exist (400), the credential it sent was refused (403), or the\n * provider is down (503). Its message stays in the logs: it can name a client id or quote the\n * provider's response.\n */\nconst OAUTH2_STATUS: Record<OAuth2ErrorType, [code: number, status: string]> = {\n invalid_request: [400, 'INVALID_ARGUMENT'],\n invalid_scope: [400, 'INVALID_ARGUMENT'],\n unsupported_grant_type: [400, 'INVALID_ARGUMENT'],\n unsupported_response_type: [400, 'INVALID_ARGUMENT'],\n redirect_uri_mismatch: [400, 'INVALID_ARGUMENT'],\n invalid_client: [403, 'PERMISSION_DENIED'],\n invalid_grant: [403, 'PERMISSION_DENIED'],\n invalid_token: [403, 'PERMISSION_DENIED'],\n unauthorized_client: [403, 'PERMISSION_DENIED'],\n insufficient_scope: [403, 'PERMISSION_DENIED'],\n access_denied: [403, 'PERMISSION_DENIED'],\n server_error: [503, 'UNAVAILABLE'],\n temporarily_unavailable: [503, 'UNAVAILABLE'],\n};\n\n/**\n * The response a JSON route gives for an error it knows, or `null` for one it does not, which is\n * the host's (a 500). Every JSON route runs under this, so the three kinds of failure have one\n * mapping: a `RequestError` as it is, an `AuthenticationError` as `403 PERMISSION_DENIED` with the\n * reason and metadata (the host's refusal or the SDK's own: a wrong code, a replayed nonce), an\n * `OAuth2Error` by what it means (`OAUTH2_<error>` as the reason). Exported for the host's error\n * handler, so a flow of its own that calls `Auth` directly (`verifyEmailCode`) answers the same way:\n * `errorResponse(error) ?? yourHandler(error)`.\n */\nexport function errorResponse(error: unknown): Response | null {\n if (error instanceof RequestError) {\n return Response.json(error.body, { status: error.body.error.code });\n }\n if (error instanceof AuthenticationError) {\n const body = errorBody(403, 'PERMISSION_DENIED', error.reason, error.message, error.metadata);\n return Response.json(body, { status: body.error.code });\n }\n if (error instanceof OAuth2Error) {\n console.warn('OAuth2 login failed', error);\n const [code, status] = OAUTH2_STATUS[error.error] ?? [503, 'UNAVAILABLE'];\n const reason = `OAUTH2_${error.error.toUpperCase().replace(/[^A-Z0-9]/g, '_')}`;\n const body = errorBody(code, status, reason, `OAuth2 ${error.error}`);\n return Response.json(body, { status: body.error.code });\n }\n return null;\n}\n"],"mappings":";;;AAiBA,MAAM,aAAa;AAEnB,SAAgB,UACd,MACA,QACA,QACA,SACA,UACW;CAEX,OAAO,EAAE,OAAO;EAAE;EAAM;EAAQ;EAAS,SAAS,CAAC;GADpC,SAAS;GAAY;GAAQ,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;EACrB,CAAC;CAAE,EAAE;AAC7D;;;;;;;AAQA,IAAa,eAAb,MAAa,qBAAqB,MAAM;CACtC;CAEA,YAAY,MAAiB;EAC3B,MAAM,KAAK,MAAM,OAAO;EACxB,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,aAAa,SAAS;CACpD;AACF;AAEA,SAAgB,gBAAgB,QAAgB,SAAiB,SAAqB;CACpF,MAAM,OAAO,UAAU,KAAK,oBAAoB,QAAQ,OAAO;CAC/D,IAAI,SAAS,KAAK,MAAM,UAAU;CAClC,OAAO,IAAI,aAAa,IAAI;AAC9B;AAEA,SAAgB,gBAAgB,SAAiB;CAC/C,OAAO,IAAI,aAAa,UAAU,KAAK,mBAAmB,mBAAmB,OAAO,CAAC;AACvF;AAEA,SAAgB,kBAAkB,QAAgB,SAAiB;CACjE,OAAO,IAAI,aAAa,UAAU,KAAK,sBAAsB,QAAQ,OAAO,CAAC;AAC/E;AAEA,SAAgB,YAAY,QAAgB,SAAiB;CAC3D,OAAO,IAAI,aAAa,UAAU,KAAK,eAAe,QAAQ,OAAO,CAAC;AACxE;;;;;;;AAQA,MAAM,gBAAyE;CAC7E,iBAAiB,CAAC,KAAK,kBAAkB;CACzC,eAAe,CAAC,KAAK,kBAAkB;CACvC,wBAAwB,CAAC,KAAK,kBAAkB;CAChD,2BAA2B,CAAC,KAAK,kBAAkB;CACnD,uBAAuB,CAAC,KAAK,kBAAkB;CAC/C,gBAAgB,CAAC,KAAK,mBAAmB;CACzC,eAAe,CAAC,KAAK,mBAAmB;CACxC,eAAe,CAAC,KAAK,mBAAmB;CACxC,qBAAqB,CAAC,KAAK,mBAAmB;CAC9C,oBAAoB,CAAC,KAAK,mBAAmB;CAC7C,eAAe,CAAC,KAAK,mBAAmB;CACxC,cAAc,CAAC,KAAK,aAAa;CACjC,yBAAyB,CAAC,KAAK,aAAa;AAC9C;;;;;;;;;;AAWA,SAAgB,cAAc,OAAiC;CAC7D,IAAI,iBAAiB,cACnB,OAAO,SAAS,KAAK,MAAM,MAAM,EAAE,QAAQ,MAAM,KAAK,MAAM,KAAK,CAAC;CAEpE,IAAI,iBAAiB,qBAAqB;EACxC,MAAM,OAAO,UAAU,KAAK,qBAAqB,MAAM,QAAQ,MAAM,SAAS,MAAM,QAAQ;EAC5F,OAAO,SAAS,KAAK,MAAM,EAAE,QAAQ,KAAK,MAAM,KAAK,CAAC;CACxD;CACA,IAAI,iBAAiB,aAAa;EAChC,QAAQ,KAAK,uBAAuB,KAAK;EACzC,MAAM,CAAC,MAAM,UAAU,cAAc,MAAM,UAAU,CAAC,KAAK,aAAa;EAExE,MAAM,OAAO,UAAU,MAAM,QAAQ,UADZ,MAAM,MAAM,YAAY,CAAC,CAAC,QAAQ,cAAc,GAAG,KAC/B,UAAU,MAAM,OAAO;EACpE,OAAO,SAAS,KAAK,MAAM,EAAE,QAAQ,KAAK,MAAM,KAAK,CAAC;CACxD;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"respond.mjs","names":[],"sources":["../../src/auth/respond.ts"],"sourcesContent":["import { OAuth2Error, type OAuth2ErrorType } from '../oauth2/error';\nimport { AuthenticationError, type RequestReason } from './error';\n\n/**\n * How the JSON routes answer a failure: a google.rpc error body, as `@shware/http` reads it, with\n * one `ErrorInfo` whose `reason` the client branches on. The redirect flow answers differently\n * (`error=…` on `errorUri`, see `Auth.redirect`), since a page, not a client, reads it.\n */\nexport interface ErrorBody {\n error: {\n code: number;\n status: string;\n message: string;\n details: unknown[];\n };\n}\n\nconst ERROR_INFO = 'type.googleapis.com/google.rpc.ErrorInfo';\n\nexport function errorBody(\n code: number,\n status: string,\n reason: string,\n message: string,\n metadata?: Record<string, string>\n): ErrorBody {\n const info = { '@type': ERROR_INFO, reason, ...(metadata ? { metadata } : {}) };\n return { error: { code, status, message, details: [info] } };\n}\n\n/**\n * A request a route cannot act on, answered as it is: malformed, too soon, not signed in. Thrown\n * by the validation helpers so a route reads as one straight line; `errorResponse` turns it into\n * the response. Unlike `AuthenticationError`, which is about who is signing in, this is about\n * the request itself, so it is not the host's to throw.\n */\nexport class RequestError extends Error {\n readonly body: ErrorBody;\n\n constructor(body: ErrorBody) {\n super(body.error.message);\n this.name = 'RequestError';\n this.body = body;\n Object.setPrototypeOf(this, RequestError.prototype);\n }\n}\n\n/**\n * A malformed request: the `ErrorInfo` with `reason` first, then any further `details` (a\n * `BadRequest` naming the fields a schema refused), so `getErrorInfo` finds the reason whatever\n * else the body carries.\n */\nexport function invalidArgument(reason: RequestReason, message: string, details: unknown[] = []) {\n const body = errorBody(400, 'INVALID_ARGUMENT', reason, message);\n body.error.details.push(...details);\n return new RequestError(body);\n}\n\nexport function unauthenticated(message: string) {\n const reason: RequestReason = 'UNAUTHENTICATED';\n return new RequestError(errorBody(401, 'UNAUTHENTICATED', reason, message));\n}\n\nexport function resourceExhausted(reason: RequestReason, message: string) {\n return new RequestError(errorBody(429, 'RESOURCE_EXHAUSTED', reason, message));\n}\n\nexport function unavailable(reason: RequestReason, message: string) {\n return new RequestError(errorBody(503, 'UNAVAILABLE', reason, message));\n}\n\n/**\n * What an `OAuth2Error` from the provider client means to the caller of a JSON route: the request\n * named something that does not exist (400), the credential it sent was refused (403), or the\n * provider is down (503). Its message stays in the logs: it can name a client id or quote the\n * provider's response.\n */\nconst OAUTH2_STATUS: Record<OAuth2ErrorType, [code: number, status: string]> = {\n invalid_request: [400, 'INVALID_ARGUMENT'],\n invalid_scope: [400, 'INVALID_ARGUMENT'],\n unsupported_grant_type: [400, 'INVALID_ARGUMENT'],\n unsupported_response_type: [400, 'INVALID_ARGUMENT'],\n redirect_uri_mismatch: [400, 'INVALID_ARGUMENT'],\n invalid_client: [403, 'PERMISSION_DENIED'],\n invalid_grant: [403, 'PERMISSION_DENIED'],\n invalid_token: [403, 'PERMISSION_DENIED'],\n unauthorized_client: [403, 'PERMISSION_DENIED'],\n insufficient_scope: [403, 'PERMISSION_DENIED'],\n access_denied: [403, 'PERMISSION_DENIED'],\n server_error: [503, 'UNAVAILABLE'],\n temporarily_unavailable: [503, 'UNAVAILABLE'],\n};\n\n/**\n * The response a JSON route gives for an error it knows, or `null` for one it does not, which is\n * the host's (a 500). Every JSON route runs under this, so the three kinds of failure have one\n * mapping: a `RequestError` as it is, an `AuthenticationError` as `403 PERMISSION_DENIED` with the\n * reason and metadata (the host's refusal or the SDK's own: a wrong code, a replayed nonce), an\n * `OAuth2Error` by what it means (`OAUTH2_<error>` as the reason). Exported for the host's error\n * handler, so a flow of its own that calls `Auth` directly (`verifyEmailCode`) answers the same way:\n * `errorResponse(error) ?? yourHandler(error)`.\n */\nexport function errorResponse(error: unknown): Response | null {\n if (error instanceof RequestError) {\n return Response.json(error.body, { status: error.body.error.code });\n }\n if (error instanceof AuthenticationError) {\n const body = errorBody(403, 'PERMISSION_DENIED', error.reason, error.message, error.metadata);\n return Response.json(body, { status: body.error.code });\n }\n if (error instanceof OAuth2Error) {\n console.warn('OAuth2 login failed', error);\n const [code, status] = OAUTH2_STATUS[error.error] ?? [503, 'UNAVAILABLE'];\n const reason = `OAUTH2_${error.error.toUpperCase().replace(/[^A-Z0-9]/g, '_')}`;\n const body = errorBody(code, status, reason, `OAuth2 ${error.error}`);\n return Response.json(body, { status: body.error.code });\n }\n return null;\n}\n"],"mappings":";;;AAiBA,MAAM,aAAa;AAEnB,SAAgB,UACd,MACA,QACA,QACA,SACA,UACW;CAEX,OAAO,EAAE,OAAO;EAAE;EAAM;EAAQ;EAAS,SAAS,CAAC;GADpC,SAAS;GAAY;GAAQ,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;EACrB,CAAC;CAAE,EAAE;AAC7D;;;;;;;AAQA,IAAa,eAAb,MAAa,qBAAqB,MAAM;CACtC;CAEA,YAAY,MAAiB;EAC3B,MAAM,KAAK,MAAM,OAAO;EACxB,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,aAAa,SAAS;CACpD;AACF;;;;;;AAOA,SAAgB,gBAAgB,QAAuB,SAAiB,UAAqB,CAAC,GAAG;CAC/F,MAAM,OAAO,UAAU,KAAK,oBAAoB,QAAQ,OAAO;CAC/D,KAAK,MAAM,QAAQ,KAAK,GAAG,OAAO;CAClC,OAAO,IAAI,aAAa,IAAI;AAC9B;AAEA,SAAgB,gBAAgB,SAAiB;CAE/C,OAAO,IAAI,aAAa,UAAU,KAAK,mBAAmB,mBAAQ,OAAO,CAAC;AAC5E;AAEA,SAAgB,kBAAkB,QAAuB,SAAiB;CACxE,OAAO,IAAI,aAAa,UAAU,KAAK,sBAAsB,QAAQ,OAAO,CAAC;AAC/E;AAEA,SAAgB,YAAY,QAAuB,SAAiB;CAClE,OAAO,IAAI,aAAa,UAAU,KAAK,eAAe,QAAQ,OAAO,CAAC;AACxE;;;;;;;AAQA,MAAM,gBAAyE;CAC7E,iBAAiB,CAAC,KAAK,kBAAkB;CACzC,eAAe,CAAC,KAAK,kBAAkB;CACvC,wBAAwB,CAAC,KAAK,kBAAkB;CAChD,2BAA2B,CAAC,KAAK,kBAAkB;CACnD,uBAAuB,CAAC,KAAK,kBAAkB;CAC/C,gBAAgB,CAAC,KAAK,mBAAmB;CACzC,eAAe,CAAC,KAAK,mBAAmB;CACxC,eAAe,CAAC,KAAK,mBAAmB;CACxC,qBAAqB,CAAC,KAAK,mBAAmB;CAC9C,oBAAoB,CAAC,KAAK,mBAAmB;CAC7C,eAAe,CAAC,KAAK,mBAAmB;CACxC,cAAc,CAAC,KAAK,aAAa;CACjC,yBAAyB,CAAC,KAAK,aAAa;AAC9C;;;;;;;;;;AAWA,SAAgB,cAAc,OAAiC;CAC7D,IAAI,iBAAiB,cACnB,OAAO,SAAS,KAAK,MAAM,MAAM,EAAE,QAAQ,MAAM,KAAK,MAAM,KAAK,CAAC;CAEpE,IAAI,iBAAiB,qBAAqB;EACxC,MAAM,OAAO,UAAU,KAAK,qBAAqB,MAAM,QAAQ,MAAM,SAAS,MAAM,QAAQ;EAC5F,OAAO,SAAS,KAAK,MAAM,EAAE,QAAQ,KAAK,MAAM,KAAK,CAAC;CACxD;CACA,IAAI,iBAAiB,aAAa;EAChC,QAAQ,KAAK,uBAAuB,KAAK;EACzC,MAAM,CAAC,MAAM,UAAU,cAAc,MAAM,UAAU,CAAC,KAAK,aAAa;EAExE,MAAM,OAAO,UAAU,MAAM,QAAQ,UADZ,MAAM,MAAM,YAAY,CAAC,CAAC,QAAQ,cAAc,GAAG,KAC/B,UAAU,MAAM,OAAO;EACpE,OAAO,SAAS,KAAK,MAAM,EAAE,QAAQ,KAAK,MAAM,KAAK,CAAC;CACxD;CACA,OAAO;AACT"}
package/dist/index.cjs CHANGED
@@ -21,6 +21,7 @@ exports.OIDC_SCOPES = require_oauth2_types.OIDC_SCOPES;
21
21
  exports.PATH = require_auth_index.PATH;
22
22
  exports.PRINCIPAL_NAME_INDEX_NAME = require_session_common.PRINCIPAL_NAME_INDEX_NAME;
23
23
  exports.PROVIDERS = require_core_index.PROVIDERS;
24
+ exports.REQUEST_REASONS = require_auth_error.REQUEST_REASONS;
24
25
  exports.RedisIndexedSessionRepository = require_session_redis.RedisIndexedSessionRepository;
25
26
  exports.RedisKVRepository = require_session_redis.RedisKVRepository;
26
27
  exports.composeName = require_oauth2_name.composeName;
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { CsrfToken, PROVIDERS, Principal, Provider } from "./core/index.cjs";
2
2
  import { KVRepository, Namespace, Session, SessionRepository } from "./session/types.cjs";
3
3
  import { EmailSender } from "./auth/email-codes.cjs";
4
- import { AUTHENTICATION_REASONS, AuthenticationError, AuthenticationReason, AuthenticationReasons } from "./auth/error.cjs";
4
+ import { AUTHENTICATION_REASONS, AuthenticationError, AuthenticationReason, AuthenticationReasons, REQUEST_REASONS, RequestReason } from "./auth/error.cjs";
5
5
  import { AuthorizationResponse, ClientSecret, NativeClient, NativeCredential, OAuth2ClientConfig, OAuth2Token, OIDC_SCOPES, OidcScope, OidcToken, PersonNameComponents, ProviderConfig, StandardClaims, UserInfo, WebClient } from "./oauth2/types.cjs";
6
6
  import { AuthConfig, AuthService, Authorization, AuthorizedHandler, LogoutOptions, LogoutScope, OAuth2AuthorizationOptions } from "./auth/types.cjs";
7
7
  import { Auth, PATH } from "./auth/index.cjs";
@@ -12,4 +12,4 @@ import { RedisIndexedSessionRepository, RedisKVRepository } from "./session/redi
12
12
  import { DBAdapter, DBIndexedSessionRepository } from "./session/db.cjs";
13
13
  import { PRINCIPAL_NAME_INDEX_NAME } from "./session/common.cjs";
14
14
  import { MAX_NAME_PART_LENGTH, composeName } from "./oauth2/name.cjs";
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, type LogoutOptions, type LogoutScope, 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 };
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, type LogoutOptions, type LogoutScope, 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, REQUEST_REASONS, RedisIndexedSessionRepository, RedisKVRepository, type RequestReason, type Session, type SessionRepository, type StandardClaims, type UserInfo, type WebClient, composeName, errorResponse, oauth2RedirectQuerySchema };
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { CsrfToken, PROVIDERS, Principal, Provider } from "./core/index.mjs";
2
2
  import { KVRepository, Namespace, Session, SessionRepository } from "./session/types.mjs";
3
3
  import { EmailSender } from "./auth/email-codes.mjs";
4
- import { AUTHENTICATION_REASONS, AuthenticationError, AuthenticationReason, AuthenticationReasons } from "./auth/error.mjs";
4
+ import { AUTHENTICATION_REASONS, AuthenticationError, AuthenticationReason, AuthenticationReasons, REQUEST_REASONS, RequestReason } from "./auth/error.mjs";
5
5
  import { AuthorizationResponse, ClientSecret, NativeClient, NativeCredential, OAuth2ClientConfig, OAuth2Token, OIDC_SCOPES, OidcScope, OidcToken, PersonNameComponents, ProviderConfig, StandardClaims, UserInfo, WebClient } from "./oauth2/types.mjs";
6
6
  import { AuthConfig, AuthService, Authorization, AuthorizedHandler, LogoutOptions, LogoutScope, OAuth2AuthorizationOptions } from "./auth/types.mjs";
7
7
  import { Auth, PATH } from "./auth/index.mjs";
@@ -12,4 +12,4 @@ import { RedisIndexedSessionRepository, RedisKVRepository } from "./session/redi
12
12
  import { DBAdapter, DBIndexedSessionRepository } from "./session/db.mjs";
13
13
  import { PRINCIPAL_NAME_INDEX_NAME } from "./session/common.mjs";
14
14
  import { MAX_NAME_PART_LENGTH, composeName } from "./oauth2/name.mjs";
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, type LogoutOptions, type LogoutScope, 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 };
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, type LogoutOptions, type LogoutScope, 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, REQUEST_REASONS, RedisIndexedSessionRepository, RedisKVRepository, type RequestReason, type Session, type SessionRepository, type StandardClaims, type UserInfo, type WebClient, composeName, errorResponse, oauth2RedirectQuerySchema };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { AUTHENTICATION_REASONS, AuthenticationError } from "./auth/error.mjs";
1
+ import { AUTHENTICATION_REASONS, AuthenticationError, REQUEST_REASONS } from "./auth/error.mjs";
2
2
  import { OAuth2Error } from "./oauth2/error.mjs";
3
3
  import { errorResponse } from "./auth/respond.mjs";
4
4
  import { OAuth2Client, oauth2RedirectQuerySchema } from "./oauth2/client.mjs";
@@ -9,4 +9,4 @@ import { DBIndexedSessionRepository } from "./session/db.mjs";
9
9
  import { PROVIDERS } from "./core/index.mjs";
10
10
  import { OIDC_SCOPES } from "./oauth2/types.mjs";
11
11
  import { MAX_NAME_PART_LENGTH, composeName } from "./oauth2/name.mjs";
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 };
12
+ export { AUTHENTICATION_REASONS, Auth, AuthenticationError, DBIndexedSessionRepository, MAX_NAME_PART_LENGTH, OAuth2Client, OAuth2Error, OIDC_SCOPES, PATH, PRINCIPAL_NAME_INDEX_NAME, PROVIDERS, REQUEST_REASONS, RedisIndexedSessionRepository, RedisKVRepository, composeName, errorResponse, oauth2RedirectQuerySchema };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shware/security",
3
- "version": "9.2.1",
3
+ "version": "9.3.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,8 +0,0 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- // src/utils/oauth2.ts
3
- function inWechatBrowser() {
4
- return navigator.userAgent.includes("MicroMessenger");
5
- }
6
- exports.inWechatBrowser = inWechatBrowser;
7
-
8
- //# sourceMappingURL=oauth2.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"oauth2.cjs","names":[],"sources":["../../src/utils/oauth2.ts"],"sourcesContent":["export function inWechatBrowser() {\n return navigator.userAgent.includes('MicroMessenger');\n}\n"],"mappings":";;AAAA,SAAgB,kBAAkB;CAChC,OAAO,UAAU,UAAU,SAAS,gBAAgB;AACtD"}
@@ -1,2 +0,0 @@
1
- export declare function inWechatBrowser(): boolean;
2
- //# sourceMappingURL=oauth2.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"oauth2.d.cts","names":[],"sources":["../../src/utils/oauth2.ts"],"mappings":"A,wBAAgB"}
@@ -1,2 +0,0 @@
1
- export declare function inWechatBrowser(): boolean;
2
- //# sourceMappingURL=oauth2.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"oauth2.d.mts","names":[],"sources":["../../src/utils/oauth2.ts"],"mappings":"A,wBAAgB"}
@@ -1,7 +0,0 @@
1
- // src/utils/oauth2.ts
2
- function inWechatBrowser() {
3
- return navigator.userAgent.includes("MicroMessenger");
4
- }
5
- export { inWechatBrowser };
6
-
7
- //# sourceMappingURL=oauth2.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"oauth2.mjs","names":[],"sources":["../../src/utils/oauth2.ts"],"sourcesContent":["export function inWechatBrowser() {\n return navigator.userAgent.includes('MicroMessenger');\n}\n"],"mappings":";AAAA,SAAgB,kBAAkB;CAChC,OAAO,UAAU,UAAU,SAAS,gBAAgB;AACtD"}