@dwk/oauth 0.1.0-beta.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 (63) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +175 -0
  3. package/dist/encoding.d.ts +16 -0
  4. package/dist/encoding.d.ts.map +1 -0
  5. package/dist/encoding.js +26 -0
  6. package/dist/encoding.js.map +1 -0
  7. package/dist/errors.d.ts +54 -0
  8. package/dist/errors.d.ts.map +1 -0
  9. package/dist/errors.js +66 -0
  10. package/dist/errors.js.map +1 -0
  11. package/dist/http.d.ts +19 -0
  12. package/dist/http.d.ts.map +1 -0
  13. package/dist/http.js +50 -0
  14. package/dist/http.js.map +1 -0
  15. package/dist/index.d.ts +42 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +39 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/introspection.d.ts +83 -0
  20. package/dist/introspection.d.ts.map +1 -0
  21. package/dist/introspection.js +118 -0
  22. package/dist/introspection.js.map +1 -0
  23. package/dist/log.d.ts +42 -0
  24. package/dist/log.d.ts.map +1 -0
  25. package/dist/log.js +40 -0
  26. package/dist/log.js.map +1 -0
  27. package/dist/metadata.d.ts +79 -0
  28. package/dist/metadata.d.ts.map +1 -0
  29. package/dist/metadata.js +67 -0
  30. package/dist/metadata.js.map +1 -0
  31. package/dist/observability.d.ts +37 -0
  32. package/dist/observability.d.ts.map +1 -0
  33. package/dist/observability.js +25 -0
  34. package/dist/observability.js.map +1 -0
  35. package/dist/par.d.ts +67 -0
  36. package/dist/par.d.ts.map +1 -0
  37. package/dist/par.js +132 -0
  38. package/dist/par.js.map +1 -0
  39. package/dist/registration.d.ts +71 -0
  40. package/dist/registration.d.ts.map +1 -0
  41. package/dist/registration.js +258 -0
  42. package/dist/registration.js.map +1 -0
  43. package/dist/revocation.d.ts +35 -0
  44. package/dist/revocation.d.ts.map +1 -0
  45. package/dist/revocation.js +50 -0
  46. package/dist/revocation.js.map +1 -0
  47. package/dist/store.d.ts +90 -0
  48. package/dist/store.d.ts.map +1 -0
  49. package/dist/store.js +13 -0
  50. package/dist/store.js.map +1 -0
  51. package/package.json +53 -0
  52. package/src/encoding.ts +26 -0
  53. package/src/errors.ts +80 -0
  54. package/src/http.ts +51 -0
  55. package/src/index.ts +75 -0
  56. package/src/introspection.ts +185 -0
  57. package/src/log.ts +43 -0
  58. package/src/metadata.ts +133 -0
  59. package/src/observability.ts +56 -0
  60. package/src/par.ts +205 -0
  61. package/src/registration.ts +336 -0
  62. package/src/revocation.ts +92 -0
  63. package/src/store.ts +93 -0
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026 David W. Keith
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,175 @@
1
+ # `@dwk/oauth`
2
+
3
+ > OAuth 2.0 authorization-server building blocks: RFC 8414 metadata, RFC 7662
4
+ > introspection, RFC 7009 revocation, RFC 9126 PAR, RFC 7591 dynamic client
5
+ > registration. Cross-standard reusable.
6
+
7
+ Part of the [`@dwk` IndieWeb + Solid cohort](../../README.md). See the
8
+ [package specification](../../spec/packages/oauth.md) for the full requirements.
9
+
10
+ This package is **cross-standard reusable**: it takes plain-data inputs only, has
11
+ no Workers-runtime dependency (only Web Fetch + Web Crypto), and unit-tests in
12
+ isolation (Node, no `workerd`). It is **protocol-agnostic** — no IndieWeb/Solid
13
+ claim handling is baked in; the caller supplies issuer/audience policy and the
14
+ storage seams. It exists so [`@dwk/indieauth`](../indieauth) and the eventual
15
+ Solid-OIDC OP share one audited implementation of these primitives rather than
16
+ diverging.
17
+
18
+ ## What it provides
19
+
20
+ | Endpoint / artifact | RFC | Factory |
21
+ | --- | --- | --- |
22
+ | Authorization-server metadata | [8414](https://www.rfc-editor.org/rfc/rfc8414) | `buildAuthorizationServerMetadata` |
23
+ | Token introspection | [7662](https://www.rfc-editor.org/rfc/rfc7662) | `createIntrospectionHandler` |
24
+ | Token revocation | [7009](https://www.rfc-editor.org/rfc/rfc7009) | `createRevocationHandler` |
25
+ | Pushed authorization requests | [9126](https://www.rfc-editor.org/rfc/rfc9126) | `createPushedAuthorizationRequestHandler` |
26
+ | Dynamic client registration | [7591](https://www.rfc-editor.org/rfc/rfc7591) | `createClientRegistrationHandler` |
27
+ | OAuth error registry | [6749 §5.2](https://www.rfc-editor.org/rfc/rfc6749#section-5.2) | `OAuthError`, `oauthErrorResponse` |
28
+
29
+ ## The static / dynamic split
30
+
31
+ The **metadata document** (`/.well-known/oauth-authorization-server`) is static
32
+ JSON derived from config known at build time, so **Anglesite serves it** — no
33
+ Worker route is needed merely to publish it. `buildAuthorizationServerMetadata`
34
+ is the single source of truth that drives both that static document and any
35
+ runtime behaviour that must agree with what was advertised.
36
+
37
+ The **four POST endpoints** are stateful, so they are the handlers this lib
38
+ provides. Each is a path-agnostic `(request) => Promise<Response>` you mount
39
+ wherever you like and back with your own strongly-consistent storage.
40
+
41
+ ## Storage is yours (never KV)
42
+
43
+ This lib owns no database. Token, client, and pushed-request records flow through
44
+ the plain-data seams in `./store`, which you back with a strongly-consistent
45
+ store (D1 with session consistency, or a Durable Object) via
46
+ [`@dwk/store`](../store). Authoritative token/authz state **MUST NOT** live in
47
+ KV: a stale introspection or revocation result is a security bug.
48
+
49
+ ## Usage
50
+
51
+ ### Metadata (RFC 8414)
52
+
53
+ ```ts
54
+ import { buildAuthorizationServerMetadata } from "@dwk/oauth";
55
+
56
+ const metadata = buildAuthorizationServerMetadata({
57
+ issuer: "https://example.com",
58
+ authorizationEndpoint: "https://example.com/authorize",
59
+ tokenEndpoint: "https://example.com/token",
60
+ introspectionEndpoint: "https://example.com/introspect",
61
+ revocationEndpoint: "https://example.com/revoke",
62
+ pushedAuthorizationRequestEndpoint: "https://example.com/par",
63
+ registrationEndpoint: "https://example.com/register",
64
+ scopesSupported: ["read", "write"],
65
+ dpopSigningAlgValuesSupported: ["ES256"],
66
+ });
67
+ ```
68
+
69
+ Endpoint URLs and optional value lists are emitted only when supplied, so the
70
+ document never advertises an endpoint you did not mount.
71
+
72
+ ### Introspection (RFC 7662)
73
+
74
+ ```ts
75
+ import { createIntrospectionHandler } from "@dwk/oauth";
76
+
77
+ const introspect = createIntrospectionHandler({
78
+ // Required: the endpoint MUST be protected against token scanning.
79
+ authenticate: (request) => verifyResourceServer(request),
80
+ // Look the token up in your strongly-consistent store.
81
+ lookupToken: async (token) => store.findToken(token),
82
+ });
83
+
84
+ // active → mapped RFC 7662 claims (snake_case), inactive → { active: false }.
85
+ const response = await introspect(request);
86
+ ```
87
+
88
+ A DPoP-bound token surfaces its key thumbprint as `cnf: { jkt }`. The `active`
89
+ flag is derived from the record's `revoked`/`expiresAt`/`notBefore` (or an
90
+ explicit `active` field). `isTokenActive` and `buildIntrospectionResponse` are
91
+ exported as pure helpers.
92
+
93
+ ### Revocation (RFC 7009)
94
+
95
+ ```ts
96
+ import { createRevocationHandler } from "@dwk/oauth";
97
+
98
+ const revoke = createRevocationHandler({
99
+ revokeToken: async (token) => store.revoke(token), // MUST be idempotent
100
+ // authenticate is optional — omit for public clients using `none`.
101
+ });
102
+ ```
103
+
104
+ Revocation always returns `200` (even for an unknown token), so a client can
105
+ retry safely and cannot probe token existence by status code.
106
+
107
+ ### Pushed authorization requests (RFC 9126)
108
+
109
+ ```ts
110
+ import {
111
+ createPushedAuthorizationRequestHandler,
112
+ parseRequestUri,
113
+ } from "@dwk/oauth";
114
+
115
+ const par = createPushedAuthorizationRequestHandler({
116
+ saveRequest: async (record) => store.savePushedRequest(record),
117
+ validate: (params) => (params.code_challenge ? null : "PKCE required"),
118
+ lifetimeSeconds: 60,
119
+ dpopBinding: true, // verify a DPoP proof at PAR and record its jkt
120
+ endpoint: "https://example.com/par",
121
+ });
122
+ // → 201 { request_uri, expires_in }
123
+ ```
124
+
125
+ At the **authorization endpoint** (in your package), recover and single-use the
126
+ reference:
127
+
128
+ ```ts
129
+ const reference = parseRequestUri(url.searchParams.get("request_uri")!);
130
+ const pushed = reference ? await store.consumePushedRequest(reference, now) : null;
131
+ ```
132
+
133
+ The `authenticate` hook is `(request, clientId?) => boolean | Promise<boolean>`:
134
+ the handler parses the body, hands the extracted `client_id` to the hook, and
135
+ passes a **pre-parse clone** of the request, so an authenticator can read the
136
+ body itself (e.g. a `client_secret_post` credential) without disturbing the
137
+ handler's own parse. This is what lets a PAR authenticator enforce the RFC 9126
138
+ §2.1 requirement that the authenticated client match the request's `client_id`.
139
+
140
+ ### Dynamic client registration (RFC 7591)
141
+
142
+ ```ts
143
+ import { createClientRegistrationHandler } from "@dwk/oauth";
144
+
145
+ const register = createClientRegistrationHandler({
146
+ saveClient: async (record) => store.saveClient(record),
147
+ // authenticate is optional (gate open registration with an initial token).
148
+ redirectUriPolicy: (uri) => uri.startsWith("https://"),
149
+ });
150
+ // → 201 client information response with the issued client_id (+ secret).
151
+ ```
152
+
153
+ Validation is strict on the security-relevant fields (`redirect_uris`,
154
+ `token_endpoint_auth_method`, the `authorization_code` ⇔ `code` pairing) and
155
+ ignores unrecognized members rather than echoing arbitrary client-supplied data
156
+ back. `validateClientMetadata` is exported as a pure helper.
157
+
158
+ ## Errors
159
+
160
+ `OAuthError` is the registered error-code registry; `oauthErrorResponse(code,
161
+ description?, status?, headers?)` builds the RFC 6749 §5.2 JSON body. `code` is
162
+ typed to the registry, so a non-registered code (the kind that breaks
163
+ standards-compliant clients) cannot be emitted by accident.
164
+
165
+ ## Out of scope
166
+
167
+ - The authorization and token endpoints themselves (those are
168
+ [`@dwk/indieauth`](../indieauth) / the future OP, which compose this lib).
169
+ - Storage (you supply it; see above).
170
+ - `DPoP-Nonce` issuance and the `use_dpop_nonce` flow.
171
+ - JWK Set hosting and access-token signing/verification.
172
+
173
+ ## License
174
+
175
+ [ISC](../../LICENSE)
@@ -0,0 +1,16 @@
1
+ /**
2
+ * base64url + random-identifier helpers.
3
+ *
4
+ * Kept dependency-free and runtime-agnostic (Web Crypto / `btoa` only) so the
5
+ * surrounding modules unit-test without a Workers runtime.
6
+ */
7
+ /** Encode bytes as unpadded base64url (RFC 4648 §5). */
8
+ export declare function bytesToBase64url(bytes: Uint8Array): string;
9
+ /**
10
+ * A cryptographically-random unpadded-base64url identifier of `bytes` entropy
11
+ * (default 32 bytes = 256 bits). Used for `request_uri` references, generated
12
+ * `client_id`s, and client secrets, where unguessability is the security
13
+ * property.
14
+ */
15
+ export declare function randomIdentifier(bytes?: number): string;
16
+ //# sourceMappingURL=encoding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoding.d.ts","sourceRoot":"","sources":["../src/encoding.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,wDAAwD;AACxD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAO1D;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,SAAK,GAAG,MAAM,CAEnD"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * base64url + random-identifier helpers.
3
+ *
4
+ * Kept dependency-free and runtime-agnostic (Web Crypto / `btoa` only) so the
5
+ * surrounding modules unit-test without a Workers runtime.
6
+ */
7
+ /** Encode bytes as unpadded base64url (RFC 4648 §5). */
8
+ export function bytesToBase64url(bytes) {
9
+ let binary = "";
10
+ for (const byte of bytes)
11
+ binary += String.fromCharCode(byte);
12
+ return btoa(binary)
13
+ .replace(/\+/g, "-")
14
+ .replace(/\//g, "_")
15
+ .replace(/=+$/, "");
16
+ }
17
+ /**
18
+ * A cryptographically-random unpadded-base64url identifier of `bytes` entropy
19
+ * (default 32 bytes = 256 bits). Used for `request_uri` references, generated
20
+ * `client_id`s, and client secrets, where unguessability is the security
21
+ * property.
22
+ */
23
+ export function randomIdentifier(bytes = 32) {
24
+ return bytesToBase64url(crypto.getRandomValues(new Uint8Array(bytes)));
25
+ }
26
+ //# sourceMappingURL=encoding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoding.js","sourceRoot":"","sources":["../src/encoding.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,wDAAwD;AACxD,MAAM,UAAU,gBAAgB,CAAC,KAAiB;IAChD,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC9D,OAAO,IAAI,CAAC,MAAM,CAAC;SAChB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACxB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAK,GAAG,EAAE;IACzC,OAAO,gBAAgB,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACzE,CAAC"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The shared OAuth 2.0 error registry.
3
+ *
4
+ * OAuth defines a fixed vocabulary of `error` codes across its specs; emitting a
5
+ * non-registered code (the finding in [#39]) makes a server fail conformance and
6
+ * confuses standards-compliant clients, which switch on these exact strings.
7
+ * Centralizing them here lets `@dwk/indieauth` and the eventual Solid-OIDC OP
8
+ * share one audited registry rather than re-spelling literals per call site.
9
+ *
10
+ * @see https://www.rfc-editor.org/rfc/rfc6749#section-5.2 (token endpoint)
11
+ * @see https://www.rfc-editor.org/rfc/rfc7591#section-3.2.2 (registration)
12
+ * @see https://www.rfc-editor.org/rfc/rfc9449#section-12 (DPoP)
13
+ */
14
+ /** The registered OAuth 2.0 `error` codes this lib emits. */
15
+ export declare const OAuthError: {
16
+ /** The request is missing a parameter, malformed, or otherwise invalid. */
17
+ readonly InvalidRequest: "invalid_request";
18
+ /** Client authentication failed or the client is unknown. */
19
+ readonly InvalidClient: "invalid_client";
20
+ /** The grant (code, refresh token, …) is invalid, expired, or revoked. */
21
+ readonly InvalidGrant: "invalid_grant";
22
+ /** The authenticated client is not authorized to use this grant type. */
23
+ readonly UnauthorizedClient: "unauthorized_client";
24
+ /** The grant type is not supported by the authorization server. */
25
+ readonly UnsupportedGrantType: "unsupported_grant_type";
26
+ /** The requested scope is invalid, unknown, or exceeds what was granted. */
27
+ readonly InvalidScope: "invalid_scope";
28
+ /** An unexpected server-side condition prevented fulfilling the request. */
29
+ readonly ServerError: "server_error";
30
+ /** The server is temporarily overloaded or under maintenance. */
31
+ readonly TemporarilyUnavailable: "temporarily_unavailable";
32
+ /** One or more submitted client metadata fields are invalid. */
33
+ readonly InvalidClientMetadata: "invalid_client_metadata";
34
+ /** One or more `redirect_uris` are invalid. */
35
+ readonly InvalidRedirectUri: "invalid_redirect_uri";
36
+ /** The server does not support revoking the presented token type. */
37
+ readonly UnsupportedTokenType: "unsupported_token_type";
38
+ /** The `request_uri` is unknown, expired, or already consumed. */
39
+ readonly InvalidRequestUri: "invalid_request_uri";
40
+ /** The DPoP proof is missing or fails verification. */
41
+ readonly InvalidDpopProof: "invalid_dpop_proof";
42
+ };
43
+ /** Union of the registered error-code string literals in {@link OAuthError}. */
44
+ export type OAuthErrorCode = (typeof OAuthError)[keyof typeof OAuthError];
45
+ /**
46
+ * Build an OAuth 2.0 error response (RFC 6749 §5.2): a JSON body with `error`
47
+ * and optional `error_description`. `error` is typed to the registry so a
48
+ * non-registered code cannot be emitted by accident.
49
+ *
50
+ * Extra headers (e.g. `WWW-Authenticate` for a `401`) are merged in; callers
51
+ * MUST keep `error_description` free of secrets and untrusted echoes.
52
+ */
53
+ export declare function oauthErrorResponse(error: OAuthErrorCode, description?: string, status?: number, headers?: Record<string, string>): Response;
54
+ //# 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,6DAA6D;AAC7D,eAAO,MAAM,UAAU;IAErB,2EAA2E;;IAE3E,6DAA6D;;IAE7D,0EAA0E;;IAE1E,yEAAyE;;IAEzE,mEAAmE;;IAEnE,4EAA4E;;IAE5E,4EAA4E;;IAE5E,iEAAiE;;IAIjE,gEAAgE;;IAEhE,+CAA+C;;IAI/C,qEAAqE;;IAIrE,kEAAkE;;IAIlE,uDAAuD;;CAE/C,CAAC;AAEX,gFAAgF;AAChF,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,OAAO,UAAU,CAAC,CAAC;AAI1E;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,cAAc,EACrB,WAAW,CAAC,EAAE,MAAM,EACpB,MAAM,SAAM,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,QAAQ,CAQV"}
package/dist/errors.js ADDED
@@ -0,0 +1,66 @@
1
+ /**
2
+ * The shared OAuth 2.0 error registry.
3
+ *
4
+ * OAuth defines a fixed vocabulary of `error` codes across its specs; emitting a
5
+ * non-registered code (the finding in [#39]) makes a server fail conformance and
6
+ * confuses standards-compliant clients, which switch on these exact strings.
7
+ * Centralizing them here lets `@dwk/indieauth` and the eventual Solid-OIDC OP
8
+ * share one audited registry rather than re-spelling literals per call site.
9
+ *
10
+ * @see https://www.rfc-editor.org/rfc/rfc6749#section-5.2 (token endpoint)
11
+ * @see https://www.rfc-editor.org/rfc/rfc7591#section-3.2.2 (registration)
12
+ * @see https://www.rfc-editor.org/rfc/rfc9449#section-12 (DPoP)
13
+ */
14
+ /** The registered OAuth 2.0 `error` codes this lib emits. */
15
+ export const OAuthError = {
16
+ // RFC 6749 §5.2 — token endpoint / general protocol errors.
17
+ /** The request is missing a parameter, malformed, or otherwise invalid. */
18
+ InvalidRequest: "invalid_request",
19
+ /** Client authentication failed or the client is unknown. */
20
+ InvalidClient: "invalid_client",
21
+ /** The grant (code, refresh token, …) is invalid, expired, or revoked. */
22
+ InvalidGrant: "invalid_grant",
23
+ /** The authenticated client is not authorized to use this grant type. */
24
+ UnauthorizedClient: "unauthorized_client",
25
+ /** The grant type is not supported by the authorization server. */
26
+ UnsupportedGrantType: "unsupported_grant_type",
27
+ /** The requested scope is invalid, unknown, or exceeds what was granted. */
28
+ InvalidScope: "invalid_scope",
29
+ /** An unexpected server-side condition prevented fulfilling the request. */
30
+ ServerError: "server_error",
31
+ /** The server is temporarily overloaded or under maintenance. */
32
+ TemporarilyUnavailable: "temporarily_unavailable",
33
+ // RFC 7591 §3.2.2 — dynamic client registration.
34
+ /** One or more submitted client metadata fields are invalid. */
35
+ InvalidClientMetadata: "invalid_client_metadata",
36
+ /** One or more `redirect_uris` are invalid. */
37
+ InvalidRedirectUri: "invalid_redirect_uri",
38
+ // RFC 7009 §2.2.1 — revocation.
39
+ /** The server does not support revoking the presented token type. */
40
+ UnsupportedTokenType: "unsupported_token_type",
41
+ // RFC 9126 §2.3 — pushed authorization requests.
42
+ /** The `request_uri` is unknown, expired, or already consumed. */
43
+ InvalidRequestUri: "invalid_request_uri",
44
+ // RFC 9449 §5 / §12 — DPoP.
45
+ /** The DPoP proof is missing or fails verification. */
46
+ InvalidDpopProof: "invalid_dpop_proof",
47
+ };
48
+ const JSON_HEADERS = { "content-type": "application/json" };
49
+ /**
50
+ * Build an OAuth 2.0 error response (RFC 6749 §5.2): a JSON body with `error`
51
+ * and optional `error_description`. `error` is typed to the registry so a
52
+ * non-registered code cannot be emitted by accident.
53
+ *
54
+ * Extra headers (e.g. `WWW-Authenticate` for a `401`) are merged in; callers
55
+ * MUST keep `error_description` free of secrets and untrusted echoes.
56
+ */
57
+ export function oauthErrorResponse(error, description, status = 400, headers) {
58
+ const body = description
59
+ ? { error, error_description: description }
60
+ : { error };
61
+ return new Response(JSON.stringify(body), {
62
+ status,
63
+ headers: { ...JSON_HEADERS, ...headers },
64
+ });
65
+ }
66
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,6DAA6D;AAC7D,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,4DAA4D;IAC5D,2EAA2E;IAC3E,cAAc,EAAE,iBAAiB;IACjC,6DAA6D;IAC7D,aAAa,EAAE,gBAAgB;IAC/B,0EAA0E;IAC1E,YAAY,EAAE,eAAe;IAC7B,yEAAyE;IACzE,kBAAkB,EAAE,qBAAqB;IACzC,mEAAmE;IACnE,oBAAoB,EAAE,wBAAwB;IAC9C,4EAA4E;IAC5E,YAAY,EAAE,eAAe;IAC7B,4EAA4E;IAC5E,WAAW,EAAE,cAAc;IAC3B,iEAAiE;IACjE,sBAAsB,EAAE,yBAAyB;IAEjD,iDAAiD;IACjD,gEAAgE;IAChE,qBAAqB,EAAE,yBAAyB;IAChD,+CAA+C;IAC/C,kBAAkB,EAAE,sBAAsB;IAE1C,gCAAgC;IAChC,qEAAqE;IACrE,oBAAoB,EAAE,wBAAwB;IAE9C,iDAAiD;IACjD,kEAAkE;IAClE,iBAAiB,EAAE,qBAAqB;IAExC,4BAA4B;IAC5B,uDAAuD;IACvD,gBAAgB,EAAE,oBAAoB;CAC9B,CAAC;AAKX,MAAM,YAAY,GAAG,EAAE,cAAc,EAAE,kBAAkB,EAAW,CAAC;AAErE;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAAqB,EACrB,WAAoB,EACpB,MAAM,GAAG,GAAG,EACZ,OAAgC;IAEhC,MAAM,IAAI,GAAG,WAAW;QACtB,CAAC,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE;QAC3C,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;IACd,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;QACxC,MAAM;QACN,OAAO,EAAE,EAAE,GAAG,YAAY,EAAE,GAAG,OAAO,EAAE;KACzC,CAAC,CAAC;AACL,CAAC"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Small request/response helpers shared by the four endpoint handlers. Web
3
+ * Fetch types only (`Request`/`Response`/`URLSearchParams`) so the handlers run
4
+ * and test under plain Node.
5
+ */
6
+ /** Serialize `body` as a JSON response with the given status. */
7
+ export declare function json(body: unknown, status?: number): Response;
8
+ /** A `405 Method Not Allowed` carrying the permitted methods in `Allow`. */
9
+ export declare function methodNotAllowed(allow: string): Response;
10
+ /**
11
+ * Read an `application/x-www-form-urlencoded` body into `URLSearchParams`. A
12
+ * malformed/empty body or wrong content-type yields empty params (never throws),
13
+ * so the caller's own field validation reports the problem as a `400` rather
14
+ * than the handler crashing.
15
+ */
16
+ export declare function readForm(request: Request): Promise<URLSearchParams>;
17
+ /** Parse a JSON request body, returning `undefined` if it is absent/malformed. */
18
+ export declare function readJson(request: Request): Promise<unknown>;
19
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,iEAAiE;AACjE,wBAAgB,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,SAAM,GAAG,QAAQ,CAK1D;AAED,4EAA4E;AAC5E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAKxD;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC,CAWzE;AAED,kFAAkF;AAClF,wBAAsB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAMjE"}
package/dist/http.js ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Small request/response helpers shared by the four endpoint handlers. Web
3
+ * Fetch types only (`Request`/`Response`/`URLSearchParams`) so the handlers run
4
+ * and test under plain Node.
5
+ */
6
+ const JSON_HEADERS = { "content-type": "application/json" };
7
+ /** Serialize `body` as a JSON response with the given status. */
8
+ export function json(body, status = 200) {
9
+ return new Response(JSON.stringify(body), {
10
+ status,
11
+ headers: JSON_HEADERS,
12
+ });
13
+ }
14
+ /** A `405 Method Not Allowed` carrying the permitted methods in `Allow`. */
15
+ export function methodNotAllowed(allow) {
16
+ return new Response("Method Not Allowed", {
17
+ status: 405,
18
+ headers: { allow },
19
+ });
20
+ }
21
+ /**
22
+ * Read an `application/x-www-form-urlencoded` body into `URLSearchParams`. A
23
+ * malformed/empty body or wrong content-type yields empty params (never throws),
24
+ * so the caller's own field validation reports the problem as a `400` rather
25
+ * than the handler crashing.
26
+ */
27
+ export async function readForm(request) {
28
+ const params = new URLSearchParams();
29
+ try {
30
+ const form = await request.formData();
31
+ for (const [key, value] of form) {
32
+ if (typeof value === "string")
33
+ params.set(key, value);
34
+ }
35
+ }
36
+ catch {
37
+ // Intentionally swallowed — see the doc comment.
38
+ }
39
+ return params;
40
+ }
41
+ /** Parse a JSON request body, returning `undefined` if it is absent/malformed. */
42
+ export async function readJson(request) {
43
+ try {
44
+ return (await request.json());
45
+ }
46
+ catch {
47
+ return undefined;
48
+ }
49
+ }
50
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,YAAY,GAAG,EAAE,cAAc,EAAE,kBAAkB,EAAW,CAAC;AAErE,iEAAiE;AACjE,MAAM,UAAU,IAAI,CAAC,IAAa,EAAE,MAAM,GAAG,GAAG;IAC9C,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;QACxC,MAAM;QACN,OAAO,EAAE,YAAY;KACtB,CAAC,CAAC;AACL,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,IAAI,QAAQ,CAAC,oBAAoB,EAAE;QACxC,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,EAAE,KAAK,EAAE;KACnB,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAgB;IAC7C,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;YAChC,IAAI,OAAO,KAAK,KAAK,QAAQ;gBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,iDAAiD;IACnD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAgB;IAC7C,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,CAAY,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * `@dwk/oauth` — OAuth 2.0 authorization-server building blocks.
3
+ *
4
+ * A cross-standard reusable lib (like `@dwk/dpop`, `@dwk/log`): it provides the
5
+ * shared OAuth 2.0 *server* primitives that `@dwk/indieauth` already partly
6
+ * implements and that the eventual Solid-OIDC OP will need, so they share one
7
+ * audited implementation rather than diverging. It owns the protocol mechanics
8
+ * of four stateful endpoints plus the metadata document and the error registry;
9
+ * it stays **protocol-agnostic** (no IndieWeb/Solid claim handling) and
10
+ * **runtime-agnostic** (plain-data storage seams, Web Fetch + Web Crypto only),
11
+ * so it unit-tests under plain Node without a Workers runtime.
12
+ *
13
+ * What it provides:
14
+ * - **RFC 8414** authorization-server metadata document (config → JSON), the
15
+ * single source of truth shared with the static document Anglesite publishes.
16
+ * - **RFC 7662** token introspection (protected; derives `active`, maps claims,
17
+ * surfaces DPoP `cnf.jkt`).
18
+ * - **RFC 7009** token revocation (idempotent, always `200`).
19
+ * - **RFC 9126** pushed authorization requests (single-use `request_uri`,
20
+ * optional DPoP binding via `@dwk/dpop`).
21
+ * - **RFC 7591** dynamic client registration (strict metadata validation).
22
+ * - A shared, registered **OAuth error registry**.
23
+ *
24
+ * Storage is never owned here: token/client/pushed-request records flow through
25
+ * the plain-data seams in {@link module:store}, which the consuming endpoint
26
+ * package backs with a strongly-consistent store (D1/DO via `@dwk/store`) —
27
+ * never KV.
28
+ *
29
+ * @see spec/packages/oauth.md
30
+ * @packageDocumentation
31
+ */
32
+ export { OAuthError, oauthErrorResponse, type OAuthErrorCode } from "./errors";
33
+ export { buildAuthorizationServerMetadata, type AuthorizationServerMetadata, type AuthorizationServerMetadataConfig, } from "./metadata";
34
+ export { createIntrospectionHandler, isTokenActive, buildIntrospectionResponse, type IntrospectionConfig, type IntrospectionResponse, type EndpointAuthenticator, } from "./introspection";
35
+ export { createRevocationHandler, type RevocationConfig } from "./revocation";
36
+ export { createPushedAuthorizationRequestHandler, parseRequestUri, requestUriFor, PUSHED_REQUEST_URI_PREFIX, type PushedAuthorizationRequestConfig, } from "./par";
37
+ export { createClientRegistrationHandler, validateClientMetadata, type ClientRegistrationConfig, } from "./registration";
38
+ export type { IntrospectionTokenRecord, PushedRequestRecord, PushedAuthorizationStore, ClientRecord, } from "./store";
39
+ export { OAuthLogEvent } from "./log";
40
+ export type { ObservabilityConfig } from "./observability";
41
+ export type { Logger, Metrics } from "@dwk/log";
42
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/E,OAAO,EACL,gCAAgC,EAChC,KAAK,2BAA2B,EAChC,KAAK,iCAAiC,GACvC,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,0BAA0B,EAC1B,aAAa,EACb,0BAA0B,EAC1B,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,GAC3B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,uBAAuB,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAE9E,OAAO,EACL,uCAAuC,EACvC,eAAe,EACf,aAAa,EACb,yBAAyB,EACzB,KAAK,gCAAgC,GACtC,MAAM,OAAO,CAAC;AAEf,OAAO,EACL,+BAA+B,EAC/B,sBAAsB,EACtB,KAAK,wBAAwB,GAC9B,MAAM,gBAAgB,CAAC;AAExB,YAAY,EACV,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,YAAY,GACb,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,YAAY,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * `@dwk/oauth` — OAuth 2.0 authorization-server building blocks.
3
+ *
4
+ * A cross-standard reusable lib (like `@dwk/dpop`, `@dwk/log`): it provides the
5
+ * shared OAuth 2.0 *server* primitives that `@dwk/indieauth` already partly
6
+ * implements and that the eventual Solid-OIDC OP will need, so they share one
7
+ * audited implementation rather than diverging. It owns the protocol mechanics
8
+ * of four stateful endpoints plus the metadata document and the error registry;
9
+ * it stays **protocol-agnostic** (no IndieWeb/Solid claim handling) and
10
+ * **runtime-agnostic** (plain-data storage seams, Web Fetch + Web Crypto only),
11
+ * so it unit-tests under plain Node without a Workers runtime.
12
+ *
13
+ * What it provides:
14
+ * - **RFC 8414** authorization-server metadata document (config → JSON), the
15
+ * single source of truth shared with the static document Anglesite publishes.
16
+ * - **RFC 7662** token introspection (protected; derives `active`, maps claims,
17
+ * surfaces DPoP `cnf.jkt`).
18
+ * - **RFC 7009** token revocation (idempotent, always `200`).
19
+ * - **RFC 9126** pushed authorization requests (single-use `request_uri`,
20
+ * optional DPoP binding via `@dwk/dpop`).
21
+ * - **RFC 7591** dynamic client registration (strict metadata validation).
22
+ * - A shared, registered **OAuth error registry**.
23
+ *
24
+ * Storage is never owned here: token/client/pushed-request records flow through
25
+ * the plain-data seams in {@link module:store}, which the consuming endpoint
26
+ * package backs with a strongly-consistent store (D1/DO via `@dwk/store`) —
27
+ * never KV.
28
+ *
29
+ * @see spec/packages/oauth.md
30
+ * @packageDocumentation
31
+ */
32
+ export { OAuthError, oauthErrorResponse } from "./errors";
33
+ export { buildAuthorizationServerMetadata, } from "./metadata";
34
+ export { createIntrospectionHandler, isTokenActive, buildIntrospectionResponse, } from "./introspection";
35
+ export { createRevocationHandler } from "./revocation";
36
+ export { createPushedAuthorizationRequestHandler, parseRequestUri, requestUriFor, PUSHED_REQUEST_URI_PREFIX, } from "./par";
37
+ export { createClientRegistrationHandler, validateClientMetadata, } from "./registration";
38
+ export { OAuthLogEvent } from "./log";
39
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAuB,MAAM,UAAU,CAAC;AAE/E,OAAO,EACL,gCAAgC,GAGjC,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,0BAA0B,EAC1B,aAAa,EACb,0BAA0B,GAI3B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,uBAAuB,EAAyB,MAAM,cAAc,CAAC;AAE9E,OAAO,EACL,uCAAuC,EACvC,eAAe,EACf,aAAa,EACb,yBAAyB,GAE1B,MAAM,OAAO,CAAC;AAEf,OAAO,EACL,+BAA+B,EAC/B,sBAAsB,GAEvB,MAAM,gBAAgB,CAAC;AASxB,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * OAuth 2.0 Token Introspection (RFC 7662).
3
+ *
4
+ * A protected POST endpoint a Resource Server queries to learn whether a token
5
+ * is currently active and, if so, its metadata. The endpoint **MUST** be
6
+ * protected (RFC 7662 §2.1) so it cannot be used as a token-scanning oracle —
7
+ * hence {@link IntrospectionConfig.authenticate} is required, not optional.
8
+ *
9
+ * The lib owns only the protocol mechanics: it asks the caller to look the token
10
+ * up (storage stays in the consuming package, see `./store`), derives the
11
+ * `active` flag, and maps the record to the snake_case response. A DPoP-bound
12
+ * token surfaces its key thumbprint as `cnf: { jkt }` so the RS can complete the
13
+ * proof-of-possession binding via `@dwk/dpop`.
14
+ *
15
+ * @see https://www.rfc-editor.org/rfc/rfc7662
16
+ */
17
+ import { type ObservabilityConfig } from "./observability";
18
+ import type { IntrospectionTokenRecord } from "./store";
19
+ /**
20
+ * Authenticates the calling Resource Server / client at a protected endpoint.
21
+ *
22
+ * Receives the request and, when the handler could extract one from the body,
23
+ * the requested `client_id`. The handler passes a **pre-parse clone** of the
24
+ * request, so an authenticator that itself reads the body (e.g. a
25
+ * `client_secret_post` credential, or matching the authenticated client against
26
+ * `clientId` per RFC 9126 §2.1) does not disturb the handler's own parse.
27
+ */
28
+ export type EndpointAuthenticator = (request: Request, clientId?: string) => boolean | Promise<boolean>;
29
+ /** Configuration for {@link createIntrospectionHandler}. */
30
+ export interface IntrospectionConfig extends ObservabilityConfig {
31
+ /**
32
+ * Look up the presented token, returning the record to introspect or `null`
33
+ * if it is unknown. The optional `tokenTypeHint` is the client's
34
+ * `token_type_hint` (RFC 7662 §2.1) — a non-binding optimization the store may
35
+ * ignore. Storage is the consuming package's concern; this stays plain-data.
36
+ */
37
+ readonly lookupToken: (token: string, tokenTypeHint?: string) => Promise<IntrospectionTokenRecord | null>;
38
+ /**
39
+ * Authenticate the caller. **Required** — an unprotected introspection
40
+ * endpoint is a token-scanning oracle (RFC 7662 §2.1). Return `false` to
41
+ * reject with `401 invalid_client`.
42
+ */
43
+ readonly authenticate: EndpointAuthenticator;
44
+ /** Current time (seconds since the epoch). Defaults to `Date.now()`. */
45
+ readonly now?: () => number;
46
+ }
47
+ /** The JSON shape of an introspection response (RFC 7662 §2.2). */
48
+ export interface IntrospectionResponse {
49
+ readonly active: boolean;
50
+ readonly scope?: string;
51
+ readonly client_id?: string;
52
+ readonly username?: string;
53
+ readonly token_type?: string;
54
+ readonly exp?: number;
55
+ readonly iat?: number;
56
+ readonly nbf?: number;
57
+ readonly sub?: string;
58
+ readonly aud?: string | readonly string[];
59
+ readonly iss?: string;
60
+ readonly jti?: string;
61
+ readonly cnf?: {
62
+ readonly jkt: string;
63
+ };
64
+ }
65
+ /**
66
+ * Whether `record` represents a currently-active token at `now`. A record is
67
+ * active unless it is `null`, explicitly `active: false`, revoked, past its
68
+ * `expiresAt`, or before its `notBefore`. Pure and side-effect-free.
69
+ */
70
+ export declare function isTokenActive(record: IntrospectionTokenRecord | null, now: number): boolean;
71
+ /**
72
+ * Map an active token record to its RFC 7662 response, emitting only members the
73
+ * record actually carries (so the response never advertises empty claims). The
74
+ * caller MUST have already established the token is active.
75
+ */
76
+ export declare function buildIntrospectionResponse(record: IntrospectionTokenRecord): IntrospectionResponse;
77
+ /**
78
+ * Create the introspection endpoint handler. The returned handler accepts a
79
+ * `POST` request and returns the introspection JSON; route it at whatever path
80
+ * the deployer mounts (it is path-agnostic).
81
+ */
82
+ export declare function createIntrospectionHandler(config: IntrospectionConfig): (request: Request) => Promise<Response>;
83
+ //# sourceMappingURL=introspection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"introspection.d.ts","sourceRoot":"","sources":["../src/introspection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAOH,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AAExD;;;;;;;;GAQG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAClC,OAAO,EAAE,OAAO,EAChB,QAAQ,CAAC,EAAE,MAAM,KACd,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC,4DAA4D;AAC5D,MAAM,WAAW,mBAAoB,SAAQ,mBAAmB;IAC9D;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,EAAE,CACpB,KAAK,EAAE,MAAM,EACb,aAAa,CAAC,EAAE,MAAM,KACnB,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC;IAC7C,wEAAwE;IACxE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED,mEAAmE;AACnE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CACzC;AAKD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,wBAAwB,GAAG,IAAI,EACvC,GAAG,EAAE,MAAM,GACV,OAAO,CAOT;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,wBAAwB,GAC/B,qBAAqB,CAevB;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,mBAAmB,GAC1B,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAmDzC"}