@c9up/visa 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +156 -0
  3. package/dist/VisaManager.d.ts +94 -0
  4. package/dist/VisaManager.d.ts.map +1 -0
  5. package/dist/VisaManager.js +216 -0
  6. package/dist/VisaManager.js.map +1 -0
  7. package/dist/VisaProvider.d.ts +41 -0
  8. package/dist/VisaProvider.d.ts.map +1 -0
  9. package/dist/VisaProvider.js +153 -0
  10. package/dist/VisaProvider.js.map +1 -0
  11. package/dist/augmentations.d.ts +25 -0
  12. package/dist/augmentations.d.ts.map +1 -0
  13. package/dist/augmentations.js +19 -0
  14. package/dist/augmentations.js.map +1 -0
  15. package/dist/authorize.d.ts +78 -0
  16. package/dist/authorize.d.ts.map +1 -0
  17. package/dist/authorize.js +149 -0
  18. package/dist/authorize.js.map +1 -0
  19. package/dist/clients.d.ts +70 -0
  20. package/dist/clients.d.ts.map +1 -0
  21. package/dist/clients.js +177 -0
  22. package/dist/clients.js.map +1 -0
  23. package/dist/config.d.ts +30 -0
  24. package/dist/config.d.ts.map +1 -0
  25. package/dist/config.js +8 -0
  26. package/dist/config.js.map +1 -0
  27. package/dist/configure.d.ts +28 -0
  28. package/dist/configure.d.ts.map +1 -0
  29. package/dist/configure.js +24 -0
  30. package/dist/configure.js.map +1 -0
  31. package/dist/crypto.d.ts +35 -0
  32. package/dist/crypto.d.ts.map +1 -0
  33. package/dist/crypto.js +56 -0
  34. package/dist/crypto.js.map +1 -0
  35. package/dist/errors.d.ts +47 -0
  36. package/dist/errors.d.ts.map +1 -0
  37. package/dist/errors.js +50 -0
  38. package/dist/errors.js.map +1 -0
  39. package/dist/index.d.ts +25 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +23 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/introspect.d.ts +32 -0
  44. package/dist/introspect.d.ts.map +1 -0
  45. package/dist/introspect.js +110 -0
  46. package/dist/introspect.js.map +1 -0
  47. package/dist/services/main.d.ts +29 -0
  48. package/dist/services/main.d.ts.map +1 -0
  49. package/dist/services/main.js +48 -0
  50. package/dist/services/main.js.map +1 -0
  51. package/dist/store.d.ts +44 -0
  52. package/dist/store.d.ts.map +1 -0
  53. package/dist/store.js +12 -0
  54. package/dist/store.js.map +1 -0
  55. package/dist/stores/memory.d.ts +34 -0
  56. package/dist/stores/memory.d.ts.map +1 -0
  57. package/dist/stores/memory.js +100 -0
  58. package/dist/stores/memory.js.map +1 -0
  59. package/dist/stubs.d.ts +14 -0
  60. package/dist/stubs.d.ts.map +1 -0
  61. package/dist/stubs.js +15 -0
  62. package/dist/stubs.js.map +1 -0
  63. package/dist/testing/main.d.ts +49 -0
  64. package/dist/testing/main.d.ts.map +1 -0
  65. package/dist/testing/main.js +77 -0
  66. package/dist/testing/main.js.map +1 -0
  67. package/dist/token.d.ts +20 -0
  68. package/dist/token.d.ts.map +1 -0
  69. package/dist/token.js +212 -0
  70. package/dist/token.js.map +1 -0
  71. package/dist/types.d.ts +123 -0
  72. package/dist/types.d.ts.map +1 -0
  73. package/dist/types.js +9 -0
  74. package/dist/types.js.map +1 -0
  75. package/package.json +83 -0
  76. package/src/VisaManager.ts +346 -0
  77. package/src/VisaProvider.ts +197 -0
  78. package/src/augmentations.ts +29 -0
  79. package/src/authorize.ts +250 -0
  80. package/src/clients.ts +231 -0
  81. package/src/config.ts +34 -0
  82. package/src/configure.ts +42 -0
  83. package/src/crypto.ts +59 -0
  84. package/src/errors.ts +79 -0
  85. package/src/index.ts +67 -0
  86. package/src/introspect.ts +139 -0
  87. package/src/services/main.ts +57 -0
  88. package/src/store.ts +56 -0
  89. package/src/stores/memory.ts +118 -0
  90. package/src/stubs.ts +16 -0
  91. package/src/testing/main.ts +119 -0
  92. package/src/token.ts +325 -0
  93. package/src/types.ts +134 -0
  94. package/stubs/config/visa.stub +25 -0
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Who is asking, and are they allowed to ask that.
3
+ *
4
+ * Three checks live here and each one closes a known attack: the redirect URI
5
+ * comparison (an open redirect on the client's domain turns into a stolen
6
+ * code), the client authentication (a leaked client_id is not a credential),
7
+ * and the scope narrowing (a client must not be able to grant itself more
8
+ * than it was registered for).
9
+ */
10
+ import { secretMatches } from "./crypto.js";
11
+ import { OAuthError } from "./errors.js";
12
+ /**
13
+ * Does this redirect URI match one the client registered?
14
+ *
15
+ * EXACT string comparison, as OAuth 2.1 §2.3.1 requires — with the single
16
+ * exception it names: a loopback redirect may differ in its port, because a
17
+ * native app cannot know in advance which port the OS will hand it.
18
+ *
19
+ * No prefix matching, no wildcard, no "same origin is close enough": every
20
+ * one of those has a published attack where the code lands on a URL the
21
+ * attacker controls.
22
+ */
23
+ export function redirectUriMatches(candidate, registered) {
24
+ if (registered.includes(candidate))
25
+ return true;
26
+ let url;
27
+ try {
28
+ url = new URL(candidate);
29
+ }
30
+ catch {
31
+ return false;
32
+ }
33
+ if (!isLoopback(url.hostname))
34
+ return false;
35
+ return registered.some((entry) => {
36
+ let known;
37
+ try {
38
+ known = new URL(entry);
39
+ }
40
+ catch {
41
+ return false;
42
+ }
43
+ if (!isLoopback(known.hostname))
44
+ return false;
45
+ // Everything but the port has to be identical.
46
+ return (known.protocol === url.protocol &&
47
+ known.hostname === url.hostname &&
48
+ known.pathname === url.pathname &&
49
+ known.search === url.search);
50
+ });
51
+ }
52
+ function isLoopback(hostname) {
53
+ return hostname === "127.0.0.1" || hostname === "[::1]" || hostname === "::1";
54
+ }
55
+ /**
56
+ * Read `client_id` / `client_secret` out of a request.
57
+ *
58
+ * A client MUST NOT use more than one authentication method in a single
59
+ * request (§2.4): sending both Basic and body credentials is a sign of a
60
+ * confused proxy or of an attempt to have one of them ignored, so it is
61
+ * refused rather than resolved by precedence.
62
+ */
63
+ export function readCredentials(input) {
64
+ const basic = parseBasic(input.authorization);
65
+ const bodyId = stringOrUndefined(input.body.client_id);
66
+ const bodySecret = stringOrUndefined(input.body.client_secret);
67
+ if (basic !== undefined && bodySecret !== undefined) {
68
+ throw new OAuthError("invalid_request", "More than one client authentication method was used.");
69
+ }
70
+ if (basic !== undefined) {
71
+ // A body client_id that disagrees with the header is not a mismatch to
72
+ // resolve — it is two different claims about who is asking.
73
+ if (bodyId !== undefined && bodyId !== basic.clientId) {
74
+ throw new OAuthError("invalid_request", "The client_id in the body does not match the one authenticated.");
75
+ }
76
+ return basic;
77
+ }
78
+ return {
79
+ clientId: bodyId,
80
+ ...(bodySecret === undefined
81
+ ? { method: "none" }
82
+ : { clientSecret: bodySecret, method: "client_secret_post" }),
83
+ };
84
+ }
85
+ function parseBasic(header) {
86
+ if (header === undefined)
87
+ return undefined;
88
+ const [scheme, encoded] = header.split(" ");
89
+ if (scheme?.toLowerCase() !== "basic" || encoded === undefined) {
90
+ return undefined;
91
+ }
92
+ const decoded = Buffer.from(encoded, "base64").toString("utf8");
93
+ const separator = decoded.indexOf(":");
94
+ if (separator === -1) {
95
+ throw new OAuthError("invalid_client", "Malformed Basic credentials.");
96
+ }
97
+ // RFC 6749 §2.3.1 form-encodes both halves before base64. A malformed
98
+ // escape throws `URIError`, which would leave the endpoint answering 500
99
+ // to anything that sends `Basic %zz` — an unauthenticated caller choosing
100
+ // the failure mode.
101
+ try {
102
+ return {
103
+ clientId: decodeURIComponent(decoded.slice(0, separator)),
104
+ clientSecret: decodeURIComponent(decoded.slice(separator + 1)),
105
+ method: "client_secret_basic",
106
+ };
107
+ }
108
+ catch {
109
+ throw new OAuthError("invalid_client", "Malformed Basic credentials.");
110
+ }
111
+ }
112
+ function stringOrUndefined(value) {
113
+ return typeof value === "string" && value !== "" ? value : undefined;
114
+ }
115
+ /**
116
+ * Authenticate a client for the token endpoint.
117
+ *
118
+ * Every failure answers `invalid_client` with no detail — "no such client",
119
+ * "wrong secret" and "that client has no secret" are the same sentence to the
120
+ * caller, because telling them apart is how a client list gets enumerated.
121
+ */
122
+ export async function authenticateClient(credentials, load) {
123
+ if (credentials.clientId === undefined) {
124
+ throw new OAuthError("invalid_client", "Client authentication failed.");
125
+ }
126
+ const client = await load(credentials.clientId);
127
+ if (client === null) {
128
+ throw new OAuthError("invalid_client", "Client authentication failed.");
129
+ }
130
+ // The registered method is the only one accepted. Checked before the
131
+ // secret so a client cannot pick the weaker of two paths.
132
+ if (credentials.method !== undefined &&
133
+ credentials.method !== client.tokenEndpointAuthMethod) {
134
+ throw new OAuthError("invalid_client", "Client authentication failed.");
135
+ }
136
+ if (client.tokenEndpointAuthMethod === "none") {
137
+ // A public client authenticates by not authenticating. Accepting a
138
+ // secret here would let one be registered as public and used as
139
+ // confidential, which is a downgrade the client chooses.
140
+ if (credentials.clientSecret !== undefined) {
141
+ throw new OAuthError("invalid_client", "Client authentication failed.");
142
+ }
143
+ return client;
144
+ }
145
+ if (credentials.clientSecret === undefined ||
146
+ client.secretHash === undefined ||
147
+ !secretMatches(credentials.clientSecret, client.secretHash)) {
148
+ throw new OAuthError("invalid_client", "Client authentication failed.");
149
+ }
150
+ return client;
151
+ }
152
+ /** Is this grant one the client was registered for? */
153
+ export function assertGrantAllowed(client, grant) {
154
+ if (client.grantTypes.includes(grant))
155
+ return;
156
+ throw new OAuthError("unauthorized_client", `This client may not use the ${grant} grant.`);
157
+ }
158
+ /**
159
+ * Narrow a requested scope against what the client may ever have.
160
+ *
161
+ * Absent, the request gets the client's registered set — never "everything".
162
+ * A scope the client was not registered for is refused rather than dropped:
163
+ * silently granting less than asked is how a client ends up believing it has
164
+ * a permission it does not.
165
+ */
166
+ export function resolveScopes(requested, client) {
167
+ if (requested === undefined || requested.trim() === "") {
168
+ return [...client.scopes];
169
+ }
170
+ const asked = requested.split(/\s+/).filter((scope) => scope !== "");
171
+ const unknown = asked.filter((scope) => !client.scopes.includes(scope));
172
+ if (unknown.length > 0) {
173
+ throw new OAuthError("invalid_scope", `Not registered for: ${unknown.join(" ")}`);
174
+ }
175
+ return asked;
176
+ }
177
+ //# sourceMappingURL=clients.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clients.js","sourceRoot":"","sources":["../src/clients.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * `config/visa.ts` — the Adonis shape: a `defineConfig` that returns what the
3
+ * provider reads out of the config store.
4
+ */
5
+ import type { VisaStore } from "./store.js";
6
+ export interface VisaConfigInput {
7
+ /**
8
+ * The public URL this server answers on — `https://auth.example.com`.
9
+ *
10
+ * It is the `iss` every token is bound to, so it is not cosmetic: a client
11
+ * that validates the issuer rejects tokens minted under a different one.
12
+ */
13
+ issuer: string;
14
+ /** Where clients, codes, tokens and consents live. */
15
+ store: VisaStore;
16
+ /** Seconds. Default 3600 — an hour is long enough to be useful, short
17
+ * enough that a leaked token is not a standing invitation. */
18
+ accessTokenTtlSeconds?: number;
19
+ /** Seconds. Default 30 days. */
20
+ refreshTokenTtlSeconds?: number;
21
+ /** Seconds. Default 30 — a code is exchanged in milliseconds. */
22
+ codeTtlSeconds?: number;
23
+ issueRefreshTokens?: boolean;
24
+ /** See `AuthorizeOptions.allowPlainChallenge` — off, and stay off. */
25
+ allowPlainChallenge?: boolean;
26
+ /** Where the endpoints are mounted. Default `/oauth`. */
27
+ prefix?: string;
28
+ }
29
+ export declare function defineConfig(config: VisaConfigInput): VisaConfigInput;
30
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,WAAW,eAAe;IAC/B;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,KAAK,EAAE,SAAS,CAAC;IACjB;kEAC8D;IAC9D,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,gCAAgC;IAChC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,iEAAiE;IACjE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,sEAAsE;IACtE,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,CAErE"}
package/dist/config.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `config/visa.ts` — the Adonis shape: a `defineConfig` that returns what the
3
+ * provider reads out of the config store.
4
+ */
5
+ export function defineConfig(config) {
6
+ return config;
7
+ }
8
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA4BH,MAAM,UAAU,YAAY,CAAC,MAAuB;IACnD,OAAO,MAAM,CAAC;AACf,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * `ream configure @c9up/visa` — wire the server in one command.
3
+ *
4
+ * The provider alone is not enough: it reads `config/visa.ts` and refuses to
5
+ * boot without one, on purpose — an authorization server that invented its own
6
+ * issuer would mint tokens nobody can validate.
7
+ *
8
+ * What it does NOT write is the store and the `/authorize` route. Both are
9
+ * decisions rather than boilerplate, and a generated guess at either is wrong
10
+ * in a way that only shows up in production: a memory store loses every
11
+ * session on deploy, and a consent screen nobody wrote grants silently.
12
+ */
13
+ interface Codemods {
14
+ addProvider(importPath: string): Promise<void>;
15
+ addEnvVars(vars: Record<string, string>): Promise<void>;
16
+ writeFile(filePath: string, content: string, options?: {
17
+ force?: boolean;
18
+ }): Promise<void>;
19
+ makeUsingStub(stubsRoot: string, stubPath: string, state?: Record<string, string | number | boolean>, options?: {
20
+ force?: boolean;
21
+ }): Promise<{
22
+ path: string;
23
+ contents: string;
24
+ }>;
25
+ }
26
+ export declare function configure(codemods: Codemods): Promise<void>;
27
+ export {};
28
+ //# sourceMappingURL=configure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,UAAU,QAAQ;IACjB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,SAAS,CACR,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,aAAa,CACZ,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,EACjD,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAC3B,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/C;AAED,wBAAsB,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAUjE"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * `ream configure @c9up/visa` — wire the server in one command.
3
+ *
4
+ * The provider alone is not enough: it reads `config/visa.ts` and refuses to
5
+ * boot without one, on purpose — an authorization server that invented its own
6
+ * issuer would mint tokens nobody can validate.
7
+ *
8
+ * What it does NOT write is the store and the `/authorize` route. Both are
9
+ * decisions rather than boilerplate, and a generated guess at either is wrong
10
+ * in a way that only shows up in production: a memory store loses every
11
+ * session on deploy, and a consent screen nobody wrote grants silently.
12
+ */
13
+ import { stubsRoot } from "./stubs.js";
14
+ export async function configure(codemods) {
15
+ // The config below reads this, so it is declared here. Writing the file
16
+ // without it leaves an application whose config asks the environment for
17
+ // something nothing ever put there.
18
+ await codemods.addEnvVars({
19
+ VISA_ISSUER: "http://localhost:3333",
20
+ });
21
+ await codemods.addProvider("@c9up/visa/provider");
22
+ await codemods.makeUsingStub(stubsRoot, "config/visa.stub");
23
+ }
24
+ //# sourceMappingURL=configure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure.js","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAkBvC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAkB;IACjD,wEAAwE;IACxE,yEAAyE;IACzE,oCAAoC;IACpC,MAAM,QAAQ,CAAC,UAAU,CAAC;QACzB,WAAW,EAAE,uBAAuB;KACpC,CAAC,CAAC;IAEH,MAAM,QAAQ,CAAC,WAAW,CAAC,qBAAqB,CAAC,CAAC;IAClD,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Every secret in this package goes through here.
3
+ *
4
+ * `node:crypto` only — a dependency in the path that mints and compares
5
+ * credentials is a dependency that can replace them.
6
+ */
7
+ /**
8
+ * 256 bits of randomness, base64url.
9
+ *
10
+ * The length is not a preference: a token is guessed offline, so its entropy
11
+ * is the only thing standing between an attacker and someone's session.
12
+ */
13
+ export declare function randomToken(bytes?: number): string;
14
+ /** SHA-256, base64url — the shape PKCE's `S256` needs (RFC 7636 §4.2). */
15
+ export declare function sha256(value: string): string;
16
+ /**
17
+ * What goes in the store for a token or a client secret.
18
+ *
19
+ * SHA-256 with no salt and no stretching, DELIBERATELY: these are values this
20
+ * package generated with 256 bits of entropy, so there is no dictionary to run
21
+ * and nothing for a slow hash to buy. Stretching here would only make every
22
+ * token lookup expensive — and a token lookup happens on every single request.
23
+ * A USER password is the opposite case and belongs in sigil.
24
+ */
25
+ export declare function hashSecret(secret: string): string;
26
+ /**
27
+ * Compare in constant time.
28
+ *
29
+ * A `===` on a secret leaks its prefix through timing, one byte at a time,
30
+ * which is enough to reconstruct it remotely given patience.
31
+ */
32
+ export declare function secretMatches(candidate: string, stored: string): boolean;
33
+ /** The same comparison for two already-hashed values. */
34
+ export declare function hashMatches(a: string, b: string): boolean;
35
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,SAAK,GAAG,MAAM,CAE9C;AAED,0EAA0E;AAC1E,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAOxE;AAED,yDAAyD;AACzD,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAKzD"}
package/dist/crypto.js ADDED
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Every secret in this package goes through here.
3
+ *
4
+ * `node:crypto` only — a dependency in the path that mints and compares
5
+ * credentials is a dependency that can replace them.
6
+ */
7
+ import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
8
+ /**
9
+ * 256 bits of randomness, base64url.
10
+ *
11
+ * The length is not a preference: a token is guessed offline, so its entropy
12
+ * is the only thing standing between an attacker and someone's session.
13
+ */
14
+ export function randomToken(bytes = 32) {
15
+ return randomBytes(bytes).toString("base64url");
16
+ }
17
+ /** SHA-256, base64url — the shape PKCE's `S256` needs (RFC 7636 §4.2). */
18
+ export function sha256(value) {
19
+ return createHash("sha256").update(value).digest("base64url");
20
+ }
21
+ /**
22
+ * What goes in the store for a token or a client secret.
23
+ *
24
+ * SHA-256 with no salt and no stretching, DELIBERATELY: these are values this
25
+ * package generated with 256 bits of entropy, so there is no dictionary to run
26
+ * and nothing for a slow hash to buy. Stretching here would only make every
27
+ * token lookup expensive — and a token lookup happens on every single request.
28
+ * A USER password is the opposite case and belongs in sigil.
29
+ */
30
+ export function hashSecret(secret) {
31
+ return sha256(secret);
32
+ }
33
+ /**
34
+ * Compare in constant time.
35
+ *
36
+ * A `===` on a secret leaks its prefix through timing, one byte at a time,
37
+ * which is enough to reconstruct it remotely given patience.
38
+ */
39
+ export function secretMatches(candidate, stored) {
40
+ const a = Buffer.from(hashSecret(candidate));
41
+ const b = Buffer.from(stored);
42
+ // Different lengths cannot be compared in constant time by
43
+ // `timingSafeEqual`, and a length mismatch is not a secret anyway.
44
+ if (a.length !== b.length)
45
+ return false;
46
+ return timingSafeEqual(a, b);
47
+ }
48
+ /** The same comparison for two already-hashed values. */
49
+ export function hashMatches(a, b) {
50
+ const left = Buffer.from(a);
51
+ const right = Buffer.from(b);
52
+ if (left.length !== right.length)
53
+ return false;
54
+ return timingSafeEqual(left, right);
55
+ }
56
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEvE;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,KAAK,GAAG,EAAE;IACrC,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACjD,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,MAAM,CAAC,KAAa;IACnC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,MAAc;IACxC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;AACvB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,SAAiB,EAAE,MAAc;IAC9D,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;IAC7C,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,2DAA2D;IAC3D,mEAAmE;IACnE,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACxC,OAAO,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC9B,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,WAAW,CAAC,CAAS,EAAE,CAAS;IAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7B,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC/C,OAAO,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACrC,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Two kinds of failure, and they are not interchangeable.
3
+ *
4
+ * A PROTOCOL error is part of the OAuth response: the spec names the string
5
+ * (`invalid_grant`, `invalid_client`…), the client reads it, and leaking no
6
+ * more than that string is deliberate — "which of the four checks failed" is
7
+ * information an attacker uses to enumerate.
8
+ *
9
+ * A FRAMEWORK error is a mistake in the application: a store that does not
10
+ * answer, a client registered without a redirect URI. Those carry a
11
+ * `E_VISA_*` code and a message meant for a developer, never for a response
12
+ * body.
13
+ */
14
+ /** Error codes the authorization endpoint may return (OAuth 2.1 §4.1.2.1). */
15
+ export type AuthorizationErrorCode = "invalid_request" | "unauthorized_client" | "access_denied" | "unsupported_response_type" | "invalid_scope" | "server_error" | "temporarily_unavailable";
16
+ /** Error codes the token endpoint may return (OAuth 2.1 §4.1.4). */
17
+ export type TokenErrorCode = "invalid_request" | "invalid_client" | "invalid_grant" | "unauthorized_client" | "unsupported_grant_type" | "invalid_scope";
18
+ export type ProtocolErrorCode = AuthorizationErrorCode | TokenErrorCode;
19
+ /**
20
+ * A failure the client is told about, in the words the spec chose.
21
+ *
22
+ * `description` is optional and OPTIONAL on purpose: it goes on the wire, so
23
+ * it says what the client can act on and never why a check failed internally.
24
+ */
25
+ export declare class OAuthError extends Error {
26
+ readonly code: ProtocolErrorCode;
27
+ readonly description: string | undefined;
28
+ /** 401 for `invalid_client`, 400 for everything else (§4.1.4). */
29
+ readonly status: number;
30
+ constructor(code: ProtocolErrorCode, description?: string, options?: {
31
+ status?: number;
32
+ });
33
+ /** The body, exactly as §4.1.4 shapes it. */
34
+ toResponse(): {
35
+ error: ProtocolErrorCode;
36
+ error_description?: string;
37
+ };
38
+ }
39
+ /** A mistake in the application, addressed to whoever wired it. */
40
+ export declare class VisaError extends Error {
41
+ readonly code: string;
42
+ readonly hint: string | undefined;
43
+ constructor(code: string, message: string, options?: {
44
+ hint?: string;
45
+ });
46
+ }
47
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,8EAA8E;AAC9E,MAAM,MAAM,sBAAsB,GAC/B,iBAAiB,GACjB,qBAAqB,GACrB,eAAe,GACf,2BAA2B,GAC3B,eAAe,GACf,cAAc,GACd,yBAAyB,CAAC;AAE7B,oEAAoE;AACpE,MAAM,MAAM,cAAc,GACvB,iBAAiB,GACjB,gBAAgB,GAChB,eAAe,GACf,qBAAqB,GACrB,wBAAwB,GACxB,eAAe,CAAC;AAEnB,MAAM,MAAM,iBAAiB,GAAG,sBAAsB,GAAG,cAAc,CAAC;AAExE;;;;;GAKG;AACH,qBAAa,UAAW,SAAQ,KAAK;IACpC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAGvB,IAAI,EAAE,iBAAiB,EACvB,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO;IASlC,6CAA6C;IAC7C,UAAU,IAAI;QAAE,KAAK,EAAE,iBAAiB,CAAC;QAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;KAAE;CAKtE;AAED,mEAAmE;AACnE,qBAAa,SAAU,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;gBAEtB,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAO;CAM1E"}
package/dist/errors.js ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Two kinds of failure, and they are not interchangeable.
3
+ *
4
+ * A PROTOCOL error is part of the OAuth response: the spec names the string
5
+ * (`invalid_grant`, `invalid_client`…), the client reads it, and leaking no
6
+ * more than that string is deliberate — "which of the four checks failed" is
7
+ * information an attacker uses to enumerate.
8
+ *
9
+ * A FRAMEWORK error is a mistake in the application: a store that does not
10
+ * answer, a client registered without a redirect URI. Those carry a
11
+ * `E_VISA_*` code and a message meant for a developer, never for a response
12
+ * body.
13
+ */
14
+ /**
15
+ * A failure the client is told about, in the words the spec chose.
16
+ *
17
+ * `description` is optional and OPTIONAL on purpose: it goes on the wire, so
18
+ * it says what the client can act on and never why a check failed internally.
19
+ */
20
+ export class OAuthError extends Error {
21
+ code;
22
+ description;
23
+ /** 401 for `invalid_client`, 400 for everything else (§4.1.4). */
24
+ status;
25
+ constructor(code, description, options = {}) {
26
+ super(description === undefined ? code : `${code}: ${description}`);
27
+ this.name = "OAuthError";
28
+ this.code = code;
29
+ this.description = description;
30
+ this.status = options.status ?? (code === "invalid_client" ? 401 : 400);
31
+ }
32
+ /** The body, exactly as §4.1.4 shapes it. */
33
+ toResponse() {
34
+ return this.description === undefined
35
+ ? { error: this.code }
36
+ : { error: this.code, error_description: this.description };
37
+ }
38
+ }
39
+ /** A mistake in the application, addressed to whoever wired it. */
40
+ export class VisaError extends Error {
41
+ code;
42
+ hint;
43
+ constructor(code, message, options = {}) {
44
+ super(message);
45
+ this.name = "VisaError";
46
+ this.code = code;
47
+ this.hint = options.hint;
48
+ }
49
+ }
50
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAuBH;;;;;GAKG;AACH,MAAM,OAAO,UAAW,SAAQ,KAAK;IAC3B,IAAI,CAAoB;IACxB,WAAW,CAAqB;IACzC,kEAAkE;IACzD,MAAM,CAAS;IAExB,YACC,IAAuB,EACvB,WAAoB,EACpB,UAA+B,EAAE;QAEjC,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,WAAW,EAAE,CAAC,CAAC;QACpE,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,KAAK,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACzE,CAAC;IAED,6CAA6C;IAC7C,UAAU;QACT,OAAO,IAAI,CAAC,WAAW,KAAK,SAAS;YACpC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE;YACtB,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,iBAAiB,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC;IAC9D,CAAC;CACD;AAED,mEAAmE;AACnE,MAAM,OAAO,SAAU,SAAQ,KAAK;IAC1B,IAAI,CAAS;IACb,IAAI,CAAqB;IAElC,YAAY,IAAY,EAAE,OAAe,EAAE,UAA6B,EAAE;QACzE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC1B,CAAC;CACD"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Visa — an OAuth 2.1 authorization server.
3
+ *
4
+ * It issues tokens to OTHER applications on a user's behalf. Knowing who that
5
+ * user is stays warden's job; visa never authenticates anyone, it only asks
6
+ * the application who is signed in.
7
+ *
8
+ * Nothing here depends on a transport or on a database: the manager takes
9
+ * values and returns values, the store is a contract. The HTTP endpoints are
10
+ * in the provider.
11
+ */
12
+ import "./augmentations.js";
13
+ export { type AuthorizationRequest, type AuthorizeOptions, errorRedirect, issueAuthorizationCode, successRedirect, UnredirectableError, type ValidatedRequest, validateAuthorizationRequest, } from "./authorize.js";
14
+ export { assertGrantAllowed, authenticateClient, type ClientCredentials, readCredentials, redirectUriMatches, resolveScopes, } from "./clients.js";
15
+ export { defineConfig, type VisaConfigInput } from "./config.js";
16
+ export { hashSecret, randomToken, secretMatches, sha256 } from "./crypto.js";
17
+ export { type AuthorizationErrorCode, OAuthError, type ProtocolErrorCode, type TokenErrorCode, VisaError, } from "./errors.js";
18
+ export { introspect, revoke, verifyAccessToken } from "./introspect.js";
19
+ export { clearVisa, default as visa, getVisa, setVisa, } from "./services/main.js";
20
+ export type { VisaStore } from "./store.js";
21
+ export { MemoryStore } from "./stores/memory.js";
22
+ export { type TokenOptions, type TokenRequest, token } from "./token.js";
23
+ export type { AccessToken, AuthorizationCode, Client, ClientAuthMethod, Consent, GrantType, IntrospectionResponse, RefreshToken, TokenResponse, } from "./types.js";
24
+ export { type AuthorizationOutcome, type VisaConfig, VisaManager, } from "./VisaManager.js";
25
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,oBAAoB,CAAC;AAE5B,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,aAAa,EACb,sBAAsB,EACtB,eAAe,EACf,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,4BAA4B,GAC5B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACN,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,iBAAiB,EACtB,eAAe,EACf,kBAAkB,EAClB,aAAa,GACb,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,EACN,KAAK,sBAAsB,EAC3B,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,SAAS,GACT,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EACN,SAAS,EACT,OAAO,IAAI,IAAI,EACf,OAAO,EACP,OAAO,GACP,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACzE,YAAY,EACX,WAAW,EACX,iBAAiB,EACjB,MAAM,EACN,gBAAgB,EAChB,OAAO,EACP,SAAS,EACT,qBAAqB,EACrB,YAAY,EACZ,aAAa,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,UAAU,EACf,WAAW,GACX,MAAM,kBAAkB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Visa — an OAuth 2.1 authorization server.
3
+ *
4
+ * It issues tokens to OTHER applications on a user's behalf. Knowing who that
5
+ * user is stays warden's job; visa never authenticates anyone, it only asks
6
+ * the application who is signed in.
7
+ *
8
+ * Nothing here depends on a transport or on a database: the manager takes
9
+ * values and returns values, the store is a contract. The HTTP endpoints are
10
+ * in the provider.
11
+ */
12
+ import "./augmentations.js";
13
+ export { errorRedirect, issueAuthorizationCode, successRedirect, UnredirectableError, validateAuthorizationRequest, } from "./authorize.js";
14
+ export { assertGrantAllowed, authenticateClient, readCredentials, redirectUriMatches, resolveScopes, } from "./clients.js";
15
+ export { defineConfig } from "./config.js";
16
+ export { hashSecret, randomToken, secretMatches, sha256 } from "./crypto.js";
17
+ export { OAuthError, VisaError, } from "./errors.js";
18
+ export { introspect, revoke, verifyAccessToken } from "./introspect.js";
19
+ export { clearVisa, default as visa, getVisa, setVisa, } from "./services/main.js";
20
+ export { MemoryStore } from "./stores/memory.js";
21
+ export { token } from "./token.js";
22
+ export { VisaManager, } from "./VisaManager.js";
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,oBAAoB,CAAC;AAE5B,OAAO,EAGN,aAAa,EACb,sBAAsB,EACtB,eAAe,EACf,mBAAmB,EAEnB,4BAA4B,GAC5B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACN,kBAAkB,EAClB,kBAAkB,EAElB,eAAe,EACf,kBAAkB,EAClB,aAAa,GACb,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAwB,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,EAEN,UAAU,EAGV,SAAS,GACT,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EACN,SAAS,EACT,OAAO,IAAI,IAAI,EACf,OAAO,EACP,OAAO,GACP,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAwC,KAAK,EAAE,MAAM,YAAY,CAAC;AAYzE,OAAO,EAGN,WAAW,GACX,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Revocation (RFC 7009) and introspection (RFC 7662).
3
+ *
4
+ * Both answer the same way to a token that is unknown, expired, revoked or
5
+ * belongs to someone else — revocation with 200, introspection with
6
+ * `{ active: false }`. That uniformity IS the security property: a different
7
+ * answer per case turns either endpoint into an oracle for guessing tokens.
8
+ */
9
+ import { type ClientCredentials } from "./clients.js";
10
+ import type { VisaStore } from "./store.js";
11
+ import type { IntrospectionResponse } from "./types.js";
12
+ export declare function introspect(request: Record<string, unknown>, credentials: ClientCredentials, store: VisaStore, now?: Date, issuer?: string): Promise<IntrospectionResponse>;
13
+ /**
14
+ * Revoke a token. Answers nothing, always successfully (RFC 7009 §2.2).
15
+ *
16
+ * Revoking a REFRESH token takes its whole family with it, access tokens
17
+ * included: "sign me out" has to mean the session is over, not that one of
18
+ * its two credentials was retired.
19
+ */
20
+ export declare function revoke(request: Record<string, unknown>, credentials: ClientCredentials, store: VisaStore, now?: Date): Promise<void>;
21
+ /**
22
+ * Is this bearer token good, and for what?
23
+ *
24
+ * What a resource server calls on every request — so it reads the store
25
+ * directly instead of going through introspection's client authentication.
26
+ */
27
+ export declare function verifyAccessToken(presented: string, store: VisaStore, now?: Date): Promise<{
28
+ clientId: string;
29
+ userId?: string;
30
+ scopes: string[];
31
+ } | null>;
32
+ //# sourceMappingURL=introspect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"introspect.d.ts","sourceRoot":"","sources":["../src/introspect.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAsB,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAG1E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAKxD,wBAAsB,UAAU,CAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,WAAW,EAAE,iBAAiB,EAC9B,KAAK,EAAE,SAAS,EAChB,GAAG,GAAE,IAAiB,EACtB,MAAM,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,qBAAqB,CAAC,CAsDhC;AAED;;;;;;GAMG;AACH,wBAAsB,MAAM,CAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,WAAW,EAAE,iBAAiB,EAC9B,KAAK,EAAE,SAAS,EAChB,GAAG,GAAE,IAAiB,GACpB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACtC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,SAAS,EAChB,GAAG,GAAE,IAAiB,GACpB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CAAC,CASzE"}