@kaiz11/stack-client 0.0.14
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/LICENSE +32 -0
- package/README.md +586 -0
- package/dist/accounts/accounts-client.d.ts +188 -0
- package/dist/accounts/accounts-client.d.ts.map +1 -0
- package/dist/accounts/accounts-client.js +264 -0
- package/dist/accounts/accounts-client.js.map +1 -0
- package/dist/accounts/index.d.ts +8 -0
- package/dist/accounts/index.d.ts.map +1 -0
- package/dist/accounts/index.js +8 -0
- package/dist/accounts/index.js.map +1 -0
- package/dist/accounts/mock-accounts.d.ts +90 -0
- package/dist/accounts/mock-accounts.d.ts.map +1 -0
- package/dist/accounts/mock-accounts.js +434 -0
- package/dist/accounts/mock-accounts.js.map +1 -0
- package/dist/accounts/types.d.ts +180 -0
- package/dist/accounts/types.d.ts.map +1 -0
- package/dist/accounts/types.js +59 -0
- package/dist/accounts/types.js.map +1 -0
- package/dist/auth/auth-client.d.ts +224 -0
- package/dist/auth/auth-client.d.ts.map +1 -0
- package/dist/auth/auth-client.js +230 -0
- package/dist/auth/auth-client.js.map +1 -0
- package/dist/auth/base-auth.d.ts +44 -0
- package/dist/auth/base-auth.d.ts.map +1 -0
- package/dist/auth/base-auth.js +55 -0
- package/dist/auth/base-auth.js.map +1 -0
- package/dist/auth/index.d.ts +11 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +11 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/methods/admin.d.ts +59 -0
- package/dist/auth/methods/admin.d.ts.map +1 -0
- package/dist/auth/methods/admin.js +55 -0
- package/dist/auth/methods/admin.js.map +1 -0
- package/dist/auth/methods/index.d.ts +9 -0
- package/dist/auth/methods/index.d.ts.map +1 -0
- package/dist/auth/methods/index.js +8 -0
- package/dist/auth/methods/index.js.map +1 -0
- package/dist/auth/methods/magic-link.d.ts +27 -0
- package/dist/auth/methods/magic-link.d.ts.map +1 -0
- package/dist/auth/methods/magic-link.js +37 -0
- package/dist/auth/methods/magic-link.js.map +1 -0
- package/dist/auth/methods/mfa.d.ts +92 -0
- package/dist/auth/methods/mfa.d.ts.map +1 -0
- package/dist/auth/methods/mfa.js +153 -0
- package/dist/auth/methods/mfa.js.map +1 -0
- package/dist/auth/methods/oauth.d.ts +62 -0
- package/dist/auth/methods/oauth.d.ts.map +1 -0
- package/dist/auth/methods/oauth.js +165 -0
- package/dist/auth/methods/oauth.js.map +1 -0
- package/dist/auth/methods/otp.d.ts +43 -0
- package/dist/auth/methods/otp.d.ts.map +1 -0
- package/dist/auth/methods/otp.js +66 -0
- package/dist/auth/methods/otp.js.map +1 -0
- package/dist/auth/methods/password.d.ts +64 -0
- package/dist/auth/methods/password.d.ts.map +1 -0
- package/dist/auth/methods/password.js +116 -0
- package/dist/auth/methods/password.js.map +1 -0
- package/dist/auth/methods/recovery.d.ts +62 -0
- package/dist/auth/methods/recovery.d.ts.map +1 -0
- package/dist/auth/methods/recovery.js +100 -0
- package/dist/auth/methods/recovery.js.map +1 -0
- package/dist/auth/mock-auth.d.ts +135 -0
- package/dist/auth/mock-auth.d.ts.map +1 -0
- package/dist/auth/mock-auth.js +417 -0
- package/dist/auth/mock-auth.js.map +1 -0
- package/dist/auth/server/helpers.d.ts +215 -0
- package/dist/auth/server/helpers.d.ts.map +1 -0
- package/dist/auth/server/helpers.js +241 -0
- package/dist/auth/server/helpers.js.map +1 -0
- package/dist/auth/server/index.d.ts +24 -0
- package/dist/auth/server/index.d.ts.map +1 -0
- package/dist/auth/server/index.js +40 -0
- package/dist/auth/server/index.js.map +1 -0
- package/dist/auth/server/middleware.d.ts +305 -0
- package/dist/auth/server/middleware.d.ts.map +1 -0
- package/dist/auth/server/middleware.js +405 -0
- package/dist/auth/server/middleware.js.map +1 -0
- package/dist/auth/server/verify.d.ts +184 -0
- package/dist/auth/server/verify.d.ts.map +1 -0
- package/dist/auth/server/verify.js +222 -0
- package/dist/auth/server/verify.js.map +1 -0
- package/dist/auth/token-manager.d.ts +94 -0
- package/dist/auth/token-manager.d.ts.map +1 -0
- package/dist/auth/token-manager.js +231 -0
- package/dist/auth/token-manager.js.map +1 -0
- package/dist/auth/types.d.ts +412 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +66 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/auth/user/identities.d.ts +62 -0
- package/dist/auth/user/identities.d.ts.map +1 -0
- package/dist/auth/user/identities.js +88 -0
- package/dist/auth/user/identities.js.map +1 -0
- package/dist/auth/user/index.d.ts +4 -0
- package/dist/auth/user/index.d.ts.map +1 -0
- package/dist/auth/user/index.js +4 -0
- package/dist/auth/user/index.js.map +1 -0
- package/dist/auth/user/user.d.ts +64 -0
- package/dist/auth/user/user.d.ts.map +1 -0
- package/dist/auth/user/user.js +105 -0
- package/dist/auth/user/user.js.map +1 -0
- package/dist/auth/user/verification.d.ts +49 -0
- package/dist/auth/user/verification.d.ts.map +1 -0
- package/dist/auth/user/verification.js +71 -0
- package/dist/auth/user/verification.js.map +1 -0
- package/dist/cli/browser.d.ts +11 -0
- package/dist/cli/browser.d.ts.map +1 -0
- package/dist/cli/browser.js +35 -0
- package/dist/cli/browser.js.map +1 -0
- package/dist/cli/callback-server.d.ts +30 -0
- package/dist/cli/callback-server.d.ts.map +1 -0
- package/dist/cli/callback-server.js +100 -0
- package/dist/cli/callback-server.js.map +1 -0
- package/dist/cli/file-token-store.d.ts +79 -0
- package/dist/cli/file-token-store.d.ts.map +1 -0
- package/dist/cli/file-token-store.js +138 -0
- package/dist/cli/file-token-store.js.map +1 -0
- package/dist/cli/index.d.ts +33 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +38 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/oauth.d.ts +67 -0
- package/dist/cli/oauth.d.ts.map +1 -0
- package/dist/cli/oauth.js +101 -0
- package/dist/cli/oauth.js.map +1 -0
- package/dist/cli/pkce.d.ts +35 -0
- package/dist/cli/pkce.d.ts.map +1 -0
- package/dist/cli/pkce.js +43 -0
- package/dist/cli/pkce.js.map +1 -0
- package/dist/client.d.ts +22 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +99 -0
- package/dist/client.js.map +1 -0
- package/dist/db/client.d.ts +9 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +19 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/errors.d.ts +19 -0
- package/dist/db/errors.d.ts.map +1 -0
- package/dist/db/errors.js +57 -0
- package/dist/db/errors.js.map +1 -0
- package/dist/db/index.d.ts +7 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +5 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/mock.d.ts +28 -0
- package/dist/db/mock.d.ts.map +1 -0
- package/dist/db/mock.js +459 -0
- package/dist/db/mock.js.map +1 -0
- package/dist/db/types.d.ts +73 -0
- package/dist/db/types.d.ts.map +1 -0
- package/dist/db/types.js +2 -0
- package/dist/db/types.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/errors.d.ts +33 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +76 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/http.d.ts +81 -0
- package/dist/lib/http.d.ts.map +1 -0
- package/dist/lib/http.js +163 -0
- package/dist/lib/http.js.map +1 -0
- package/dist/lib/keys.d.ts +87 -0
- package/dist/lib/keys.d.ts.map +1 -0
- package/dist/lib/keys.js +147 -0
- package/dist/lib/keys.js.map +1 -0
- package/dist/lib/paths.d.ts +37 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +49 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/token-store.d.ts +42 -0
- package/dist/lib/token-store.d.ts.map +1 -0
- package/dist/lib/token-store.js +75 -0
- package/dist/lib/token-store.js.map +1 -0
- package/dist/mocks/handlers.d.ts +29 -0
- package/dist/mocks/handlers.d.ts.map +1 -0
- package/dist/mocks/handlers.js +79 -0
- package/dist/mocks/handlers.js.map +1 -0
- package/dist/mocks/index.d.ts +5 -0
- package/dist/mocks/index.d.ts.map +1 -0
- package/dist/mocks/index.js +9 -0
- package/dist/mocks/index.js.map +1 -0
- package/dist/mocks/responses.d.ts +76 -0
- package/dist/mocks/responses.d.ts.map +1 -0
- package/dist/mocks/responses.js +91 -0
- package/dist/mocks/responses.js.map +1 -0
- package/dist/mocks/server.d.ts +7 -0
- package/dist/mocks/server.d.ts.map +1 -0
- package/dist/mocks/server.js +9 -0
- package/dist/mocks/server.js.map +1 -0
- package/dist/mocks/state.d.ts +86 -0
- package/dist/mocks/state.d.ts.map +1 -0
- package/dist/mocks/state.js +77 -0
- package/dist/mocks/state.js.map +1 -0
- package/dist/storage/bucket-ref.d.ts +183 -0
- package/dist/storage/bucket-ref.d.ts.map +1 -0
- package/dist/storage/bucket-ref.js +529 -0
- package/dist/storage/bucket-ref.js.map +1 -0
- package/dist/storage/errors.d.ts +27 -0
- package/dist/storage/errors.d.ts.map +1 -0
- package/dist/storage/errors.js +89 -0
- package/dist/storage/errors.js.map +1 -0
- package/dist/storage/index.d.ts +13 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +11 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/interface.d.ts +245 -0
- package/dist/storage/interface.d.ts.map +1 -0
- package/dist/storage/interface.js +2 -0
- package/dist/storage/interface.js.map +1 -0
- package/dist/storage/mock-storage.d.ts +67 -0
- package/dist/storage/mock-storage.d.ts.map +1 -0
- package/dist/storage/mock-storage.js +478 -0
- package/dist/storage/mock-storage.js.map +1 -0
- package/dist/storage/policies-client.d.ts +77 -0
- package/dist/storage/policies-client.d.ts.map +1 -0
- package/dist/storage/policies-client.js +115 -0
- package/dist/storage/policies-client.js.map +1 -0
- package/dist/storage/policy-templates.d.ts +6 -0
- package/dist/storage/policy-templates.d.ts.map +1 -0
- package/dist/storage/policy-templates.js +290 -0
- package/dist/storage/policy-templates.js.map +1 -0
- package/dist/storage/policy-types.d.ts +98 -0
- package/dist/storage/policy-types.d.ts.map +1 -0
- package/dist/storage/policy-types.js +20 -0
- package/dist/storage/policy-types.js.map +1 -0
- package/dist/storage/storage-client.d.ts +32 -0
- package/dist/storage/storage-client.d.ts.map +1 -0
- package/dist/storage/storage-client.js +94 -0
- package/dist/storage/storage-client.js.map +1 -0
- package/dist/storage/tus-upload.d.ts +56 -0
- package/dist/storage/tus-upload.d.ts.map +1 -0
- package/dist/storage/tus-upload.js +236 -0
- package/dist/storage/tus-upload.js.map +1 -0
- package/dist/storage/types.d.ts +335 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +39 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/test/auth/helpers.d.ts +33 -0
- package/dist/test/auth/helpers.d.ts.map +1 -0
- package/dist/test/auth/helpers.js +80 -0
- package/dist/test/auth/helpers.js.map +1 -0
- package/dist/test/helpers/jwt.d.ts +61 -0
- package/dist/test/helpers/jwt.d.ts.map +1 -0
- package/dist/test/helpers/jwt.js +132 -0
- package/dist/test/helpers/jwt.js.map +1 -0
- package/dist/test/helpers/mailpit.d.ts +61 -0
- package/dist/test/helpers/mailpit.d.ts.map +1 -0
- package/dist/test/helpers/mailpit.js +107 -0
- package/dist/test/helpers/mailpit.js.map +1 -0
- package/dist/test/setup.d.ts +2 -0
- package/dist/test/setup.d.ts.map +1 -0
- package/dist/test/setup.js +17 -0
- package/dist/test/setup.js.map +1 -0
- package/dist/types.d.ts +96 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import { verifyToken, extractBearerToken, createJWKS, VerifyError, } from "./verify.js";
|
|
2
|
+
import { PLATFORM_TENANT_ID } from "../../types.js";
|
|
3
|
+
import { fetchAnonKeyCached } from "../../lib/keys.js";
|
|
4
|
+
/**
|
|
5
|
+
* Create auth middleware for Hono
|
|
6
|
+
*
|
|
7
|
+
* Automatically constructs the JWKS URL from the base URL and optional tenant ID,
|
|
8
|
+
* and verifies JWT tokens signed by the GoTrue instance.
|
|
9
|
+
*
|
|
10
|
+
* ## Token Handling
|
|
11
|
+
*
|
|
12
|
+
* | Token Type | Result |
|
|
13
|
+
* |---------------|-------------------------------------------------|
|
|
14
|
+
* | No token | 401 Unauthorized |
|
|
15
|
+
* | Invalid token | 401 Unauthorized |
|
|
16
|
+
* | Anon JWT | ✅ Passes, `user.role = "anon"` |
|
|
17
|
+
* | Auth JWT | ✅ Passes, `user.role = "authenticated"` |
|
|
18
|
+
* | Service JWT | ✅ Passes, `user.role = "service_role"` |
|
|
19
|
+
*
|
|
20
|
+
* **Note:** This middleware requires a valid JWT. In Supabase's model, even
|
|
21
|
+
* "public" requests use the anon key. If you want to allow requests without
|
|
22
|
+
* any token, use `optionalStackAuthMiddleware` instead.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* import { Hono } from "hono";
|
|
27
|
+
* import { createStackAuthMiddleware } from "@kaiz11/stack-client/auth/server";
|
|
28
|
+
*
|
|
29
|
+
* const app = new Hono();
|
|
30
|
+
*
|
|
31
|
+
* // Platform (default)
|
|
32
|
+
* app.use("*", createStackAuthMiddleware({
|
|
33
|
+
* baseUrl: "https://stack.zenku.app",
|
|
34
|
+
* excludePaths: [/^\/health$/],
|
|
35
|
+
* }));
|
|
36
|
+
*
|
|
37
|
+
* // Tenant
|
|
38
|
+
* app.use("*", createStackAuthMiddleware({
|
|
39
|
+
* baseUrl: "https://stack.zenku.app",
|
|
40
|
+
* tenantId: "acme-corp",
|
|
41
|
+
* excludePaths: [/^\/health$/],
|
|
42
|
+
* }));
|
|
43
|
+
*
|
|
44
|
+
* app.get("/api/me", (c) => {
|
|
45
|
+
* const user = c.get("user") as AuthUser;
|
|
46
|
+
* return c.json({ user });
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export function createStackAuthMiddleware(config) {
|
|
51
|
+
const tenantId = config.tenantId ?? PLATFORM_TENANT_ID;
|
|
52
|
+
const keyGetter = createJWKS(config.baseUrl, tenantId);
|
|
53
|
+
return createAuthMiddleware({
|
|
54
|
+
keyGetter,
|
|
55
|
+
options: config.options,
|
|
56
|
+
excludePaths: config.excludePaths,
|
|
57
|
+
onError: config.onError,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Create auth middleware for Hono (advanced)
|
|
62
|
+
*
|
|
63
|
+
* Use this when you need to provide a custom JWKS key getter.
|
|
64
|
+
* For most cases, prefer `createStackAuthMiddleware`.
|
|
65
|
+
*
|
|
66
|
+
* ## Token Handling
|
|
67
|
+
*
|
|
68
|
+
* | Token Type | Result |
|
|
69
|
+
* |---------------|-------------------------------------------------|
|
|
70
|
+
* | No token | 401 Unauthorized |
|
|
71
|
+
* | Invalid token | 401 Unauthorized |
|
|
72
|
+
* | Anon JWT | ✅ Passes, `user.role = "anon"` |
|
|
73
|
+
* | Auth JWT | ✅ Passes, `user.role = "authenticated"` |
|
|
74
|
+
* | Service JWT | ✅ Passes, `user.role = "service_role"` |
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* import { Hono } from "hono";
|
|
79
|
+
* import { createAuthMiddleware, createLocalJWKS } from "@kaiz11/stack-client/auth/server";
|
|
80
|
+
*
|
|
81
|
+
* const app = new Hono();
|
|
82
|
+
*
|
|
83
|
+
* // For testing with local keys
|
|
84
|
+
* const keyGetter = createLocalJWKS(testJWKS);
|
|
85
|
+
*
|
|
86
|
+
* app.use("*", createAuthMiddleware({
|
|
87
|
+
* keyGetter,
|
|
88
|
+
* excludePaths: [/^\/health$/],
|
|
89
|
+
* }));
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export function createAuthMiddleware(config) {
|
|
93
|
+
const { keyGetter, options, excludePaths = [], onError } = config;
|
|
94
|
+
return async (c, next) => {
|
|
95
|
+
// Check if path is excluded
|
|
96
|
+
const path = c.req.path;
|
|
97
|
+
for (const pattern of excludePaths) {
|
|
98
|
+
if (pattern.test(path)) {
|
|
99
|
+
return next();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Extract and verify token
|
|
103
|
+
const authHeader = c.req.raw.headers.get("authorization");
|
|
104
|
+
const token = extractBearerToken(authHeader);
|
|
105
|
+
if (!token) {
|
|
106
|
+
const error = VerifyError.missingToken();
|
|
107
|
+
if (onError) {
|
|
108
|
+
return onError(error);
|
|
109
|
+
}
|
|
110
|
+
return c.json({ error: error.message, code: error.code }, 401);
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const payload = await verifyToken(token, keyGetter, options);
|
|
114
|
+
const user = {
|
|
115
|
+
id: payload.sub,
|
|
116
|
+
email: payload.email ?? null,
|
|
117
|
+
phone: payload.phone ?? null,
|
|
118
|
+
role: payload.role,
|
|
119
|
+
aal: payload.aal || "aal1",
|
|
120
|
+
appMetadata: payload.app_metadata ?? {},
|
|
121
|
+
userMetadata: payload.user_metadata ?? {},
|
|
122
|
+
};
|
|
123
|
+
// Attach to context
|
|
124
|
+
c.set("user", user);
|
|
125
|
+
c.set("tokenPayload", payload);
|
|
126
|
+
c.set("accessToken", token);
|
|
127
|
+
return next();
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
if (error instanceof VerifyError) {
|
|
131
|
+
if (onError) {
|
|
132
|
+
return onError(error);
|
|
133
|
+
}
|
|
134
|
+
return c.json({ error: error.message, code: error.code }, error.statusCode);
|
|
135
|
+
}
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Create role guard middleware for Hono
|
|
142
|
+
*
|
|
143
|
+
* Use after `createStackAuthMiddleware` to require specific roles.
|
|
144
|
+
*
|
|
145
|
+
* ## Token Handling (assuming auth middleware already ran)
|
|
146
|
+
*
|
|
147
|
+
* | Token Type | `requireRoleMiddleware("authenticated")` | `requireRoleMiddleware("service_role")` |
|
|
148
|
+
* |---------------|------------------------------------------|----------------------------------------|
|
|
149
|
+
* | Anon JWT | 403 Forbidden | 403 Forbidden |
|
|
150
|
+
* | Auth JWT | ✅ Passes | 403 Forbidden |
|
|
151
|
+
* | Service JWT | 403 Forbidden | ✅ Passes |
|
|
152
|
+
*
|
|
153
|
+
* **Common patterns:**
|
|
154
|
+
* - `requireRoleMiddleware("authenticated")` — Reject anon, allow logged-in users
|
|
155
|
+
* - `requireRoleMiddleware("service_role")` — Admin/server-only endpoints
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```typescript
|
|
159
|
+
* import { createStackAuthMiddleware, requireRoleMiddleware } from "@kaiz11/stack-client/auth/server";
|
|
160
|
+
*
|
|
161
|
+
* app.use("*", createStackAuthMiddleware({ baseUrl: "https://stack.zenku.app" }));
|
|
162
|
+
*
|
|
163
|
+
* // Reject anon tokens, require actual login
|
|
164
|
+
* app.use("/api/user/*", requireRoleMiddleware("authenticated"));
|
|
165
|
+
*
|
|
166
|
+
* // Admin routes require service_role
|
|
167
|
+
* app.use("/admin/*", requireRoleMiddleware("service_role"));
|
|
168
|
+
* ```
|
|
169
|
+
*/
|
|
170
|
+
export function requireRoleMiddleware(role) {
|
|
171
|
+
return async (c, next) => {
|
|
172
|
+
const user = c.get("user");
|
|
173
|
+
if (!user) {
|
|
174
|
+
return c.json({ error: "Authentication required", code: "missing_auth" }, 401);
|
|
175
|
+
}
|
|
176
|
+
if (user.role !== role) {
|
|
177
|
+
return c.json({
|
|
178
|
+
error: `Insufficient role: required ${role}, got ${user.role}`,
|
|
179
|
+
code: "insufficient_role",
|
|
180
|
+
}, 403);
|
|
181
|
+
}
|
|
182
|
+
return next();
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Create MFA guard middleware for Hono
|
|
187
|
+
*
|
|
188
|
+
* Requires AAL2 (two-factor authentication verified in current session).
|
|
189
|
+
*
|
|
190
|
+
* ## Token Handling (assuming auth middleware already ran)
|
|
191
|
+
*
|
|
192
|
+
* | Token Type | AAL Level | Result |
|
|
193
|
+
* |------------------|-----------|-------------------------------------|
|
|
194
|
+
* | Anon JWT | aal1 | 403 MFA required |
|
|
195
|
+
* | Auth JWT | aal1 | 403 MFA required |
|
|
196
|
+
* | Auth JWT + MFA | aal2 | ✅ Passes |
|
|
197
|
+
* | Service JWT | aal1 | 403 MFA required |
|
|
198
|
+
*
|
|
199
|
+
* **Note:** Service role tokens typically have aal1. If you need service role
|
|
200
|
+
* access to MFA-protected routes, check the role explicitly instead.
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```typescript
|
|
204
|
+
* // Sensitive routes require MFA
|
|
205
|
+
* app.use("/settings/security/*", requireMfaMiddleware());
|
|
206
|
+
*
|
|
207
|
+
* // Or combine with role check for admin bypass
|
|
208
|
+
* app.use("/settings/security/*", async (c, next) => {
|
|
209
|
+
* const user = c.get("user") as AuthUser;
|
|
210
|
+
* if (user.role === "service_role") return next(); // Admin bypass
|
|
211
|
+
* return requireMfaMiddleware()(c, next);
|
|
212
|
+
* });
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
export function requireMfaMiddleware() {
|
|
216
|
+
return async (c, next) => {
|
|
217
|
+
const user = c.get("user");
|
|
218
|
+
const payload = c.get("tokenPayload");
|
|
219
|
+
if (!user || !payload) {
|
|
220
|
+
return c.json({ error: "Authentication required", code: "missing_auth" }, 401);
|
|
221
|
+
}
|
|
222
|
+
const aal = payload.aal || "aal1";
|
|
223
|
+
if (aal !== "aal2") {
|
|
224
|
+
return c.json({
|
|
225
|
+
error: "MFA verification required",
|
|
226
|
+
code: "mfa_required",
|
|
227
|
+
currentAal: aal,
|
|
228
|
+
requiredAal: "aal2",
|
|
229
|
+
}, 403);
|
|
230
|
+
}
|
|
231
|
+
return next();
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Optional auth middleware for Hono
|
|
236
|
+
*
|
|
237
|
+
* Attaches user info if token is present. When no token is provided,
|
|
238
|
+
* automatically fetches the tenant's anon key (default behavior).
|
|
239
|
+
*
|
|
240
|
+
* ## Token Handling (default, autoFetchAnon = true)
|
|
241
|
+
*
|
|
242
|
+
* | Token Type | Result |
|
|
243
|
+
* |---------------|-------------------------------------------------|
|
|
244
|
+
* | No token | ✅ Auto-fetches anon key, `user.role = "anon"` |
|
|
245
|
+
* | Invalid token | ✅ Falls back to anon key, `user.role = "anon"` |
|
|
246
|
+
* | Anon JWT | ✅ Continues, `user.role = "anon"` |
|
|
247
|
+
* | Auth JWT | ✅ Continues, `user.role = "authenticated"` |
|
|
248
|
+
* | Service JWT | ✅ Continues, `user.role = "service_role"` |
|
|
249
|
+
*
|
|
250
|
+
* ## Token Handling (autoFetchAnon = false)
|
|
251
|
+
*
|
|
252
|
+
* | Token Type | Result |
|
|
253
|
+
* |---------------|-------------------------------------------------|
|
|
254
|
+
* | No token | ✅ Continues, `user = undefined` |
|
|
255
|
+
* | Invalid token | ✅ Continues, `user = undefined` |
|
|
256
|
+
* | Anon JWT | ✅ Continues, `user.role = "anon"` |
|
|
257
|
+
* | Auth JWT | ✅ Continues, `user.role = "authenticated"` |
|
|
258
|
+
* | Service JWT | ✅ Continues, `user.role = "service_role"` |
|
|
259
|
+
*
|
|
260
|
+
* **Note:** Unlike `createStackAuthMiddleware`, this never returns 401.
|
|
261
|
+
* Invalid tokens fall back to anon (default) or are silently ignored.
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* ```typescript
|
|
265
|
+
* import { optionalStackAuthMiddleware } from "@kaiz11/stack-client/auth/server";
|
|
266
|
+
*
|
|
267
|
+
* // Default: user is always set (auto-fetches anon if no token)
|
|
268
|
+
* app.use("*", optionalStackAuthMiddleware({
|
|
269
|
+
* baseUrl: "https://stack.zenku.app",
|
|
270
|
+
* tenantId: "acme-corp",
|
|
271
|
+
* }));
|
|
272
|
+
*
|
|
273
|
+
* // Disable auto-fetch: user may be undefined if no token
|
|
274
|
+
* app.use("*", optionalStackAuthMiddleware({
|
|
275
|
+
* baseUrl: "https://stack.zenku.app",
|
|
276
|
+
* tenantId: "acme-corp",
|
|
277
|
+
* autoFetchAnon: false,
|
|
278
|
+
* }));
|
|
279
|
+
*
|
|
280
|
+
* app.get("/api/feed", (c) => {
|
|
281
|
+
* const user = c.get("user") as AuthUser;
|
|
282
|
+
* if (user.role === "authenticated") {
|
|
283
|
+
* // Logged in user
|
|
284
|
+
* } else {
|
|
285
|
+
* // Anonymous access (anon token or auto-fetched)
|
|
286
|
+
* }
|
|
287
|
+
* });
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
export function optionalStackAuthMiddleware(config) {
|
|
291
|
+
const tenantId = config.tenantId ?? PLATFORM_TENANT_ID;
|
|
292
|
+
const keyGetter = createJWKS(config.baseUrl, tenantId);
|
|
293
|
+
const { baseUrl } = config;
|
|
294
|
+
const autoFetchAnon = config.autoFetchAnon ?? true;
|
|
295
|
+
return async (c, next) => {
|
|
296
|
+
const authHeader = c.req.raw.headers.get("authorization");
|
|
297
|
+
let token = extractBearerToken(authHeader);
|
|
298
|
+
// Auto-fetch anon key if no token and autoFetchAnon is enabled
|
|
299
|
+
if (!token && autoFetchAnon) {
|
|
300
|
+
try {
|
|
301
|
+
token = await fetchAnonKeyCached(baseUrl, tenantId);
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
// Failed to fetch anon key, continue without auth
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (token) {
|
|
308
|
+
try {
|
|
309
|
+
const payload = await verifyToken(token, keyGetter, config.options);
|
|
310
|
+
const user = {
|
|
311
|
+
id: payload.sub,
|
|
312
|
+
email: payload.email ?? null,
|
|
313
|
+
phone: payload.phone ?? null,
|
|
314
|
+
role: payload.role,
|
|
315
|
+
aal: payload.aal || "aal1",
|
|
316
|
+
appMetadata: payload.app_metadata ?? {},
|
|
317
|
+
userMetadata: payload.user_metadata ?? {},
|
|
318
|
+
};
|
|
319
|
+
c.set("user", user);
|
|
320
|
+
c.set("tokenPayload", payload);
|
|
321
|
+
c.set("accessToken", token);
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
// Invalid token - try to fall back to anon if autoFetchAnon
|
|
325
|
+
if (autoFetchAnon) {
|
|
326
|
+
try {
|
|
327
|
+
const anonToken = await fetchAnonKeyCached(baseUrl, tenantId);
|
|
328
|
+
const payload = await verifyToken(anonToken, keyGetter, config.options);
|
|
329
|
+
const user = {
|
|
330
|
+
id: payload.sub,
|
|
331
|
+
email: payload.email ?? null,
|
|
332
|
+
phone: payload.phone ?? null,
|
|
333
|
+
role: payload.role,
|
|
334
|
+
aal: payload.aal || "aal1",
|
|
335
|
+
appMetadata: payload.app_metadata ?? {},
|
|
336
|
+
userMetadata: payload.user_metadata ?? {},
|
|
337
|
+
};
|
|
338
|
+
c.set("user", user);
|
|
339
|
+
c.set("tokenPayload", payload);
|
|
340
|
+
c.set("accessToken", anonToken);
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
// Failed to use anon fallback, continue without auth
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return next();
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Optional auth middleware for Hono (advanced)
|
|
353
|
+
*
|
|
354
|
+
* Attaches user info if token is present, but doesn't require it.
|
|
355
|
+
* Use this when you need a custom JWKS key getter.
|
|
356
|
+
*
|
|
357
|
+
* ## Token Handling
|
|
358
|
+
*
|
|
359
|
+
* | Token Type | Result |
|
|
360
|
+
* |---------------|-------------------------------------------------|
|
|
361
|
+
* | No token | ✅ Continues, `user = undefined` |
|
|
362
|
+
* | Invalid token | ✅ Continues, `user = undefined` |
|
|
363
|
+
* | Anon JWT | ✅ Continues, `user.role = "anon"` |
|
|
364
|
+
* | Auth JWT | ✅ Continues, `user.role = "authenticated"` |
|
|
365
|
+
* | Service JWT | ✅ Continues, `user.role = "service_role"` |
|
|
366
|
+
*
|
|
367
|
+
* @example
|
|
368
|
+
* ```typescript
|
|
369
|
+
* import { optionalAuthMiddleware, createLocalJWKS } from "@kaiz11/stack-client/auth/server";
|
|
370
|
+
*
|
|
371
|
+
* // For testing with local keys
|
|
372
|
+
* const keyGetter = createLocalJWKS(testJWKS);
|
|
373
|
+
*
|
|
374
|
+
* app.use("*", optionalAuthMiddleware({ keyGetter }));
|
|
375
|
+
* ```
|
|
376
|
+
*/
|
|
377
|
+
export function optionalAuthMiddleware(config) {
|
|
378
|
+
const { keyGetter, options } = config;
|
|
379
|
+
return async (c, next) => {
|
|
380
|
+
const authHeader = c.req.raw.headers.get("authorization");
|
|
381
|
+
const token = extractBearerToken(authHeader);
|
|
382
|
+
if (token) {
|
|
383
|
+
try {
|
|
384
|
+
const payload = await verifyToken(token, keyGetter, options);
|
|
385
|
+
const user = {
|
|
386
|
+
id: payload.sub,
|
|
387
|
+
email: payload.email ?? null,
|
|
388
|
+
phone: payload.phone ?? null,
|
|
389
|
+
role: payload.role,
|
|
390
|
+
aal: payload.aal || "aal1",
|
|
391
|
+
appMetadata: payload.app_metadata ?? {},
|
|
392
|
+
userMetadata: payload.user_metadata ?? {},
|
|
393
|
+
};
|
|
394
|
+
c.set("user", user);
|
|
395
|
+
c.set("tokenPayload", payload);
|
|
396
|
+
c.set("accessToken", token);
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
// Invalid token, continue without auth
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return next();
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../../src/auth/server/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import * as jose from "jose";
|
|
2
|
+
/**
|
|
3
|
+
* JWT payload structure from GoTrue
|
|
4
|
+
*/
|
|
5
|
+
export interface TokenPayload {
|
|
6
|
+
/** User ID */
|
|
7
|
+
sub: string;
|
|
8
|
+
/** Email address */
|
|
9
|
+
email?: string;
|
|
10
|
+
/** Phone number */
|
|
11
|
+
phone?: string;
|
|
12
|
+
/** User role (authenticated, anon, service_role) */
|
|
13
|
+
role: string;
|
|
14
|
+
/** Audience */
|
|
15
|
+
aud?: string;
|
|
16
|
+
/** Expiration time (Unix timestamp) */
|
|
17
|
+
exp?: number;
|
|
18
|
+
/** Issued at (Unix timestamp) */
|
|
19
|
+
iat?: number;
|
|
20
|
+
/** Authenticator assurance level */
|
|
21
|
+
aal?: "aal1" | "aal2";
|
|
22
|
+
/** App metadata */
|
|
23
|
+
app_metadata?: {
|
|
24
|
+
provider?: string;
|
|
25
|
+
providers?: string[];
|
|
26
|
+
};
|
|
27
|
+
/** User metadata */
|
|
28
|
+
user_metadata?: Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Verification options
|
|
32
|
+
*/
|
|
33
|
+
export interface VerifyOptions {
|
|
34
|
+
/** Expected audience (optional) */
|
|
35
|
+
audience?: string;
|
|
36
|
+
/** Clock tolerance in seconds (default: 0) */
|
|
37
|
+
clockTolerance?: number;
|
|
38
|
+
/** Required role (optional) */
|
|
39
|
+
requiredRole?: string;
|
|
40
|
+
/** Required AAL level (optional) */
|
|
41
|
+
requiredAal?: "aal1" | "aal2";
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Key getter function type (returned by createRemoteJWKS or createLocalJWKS)
|
|
45
|
+
*/
|
|
46
|
+
export type JWKSKeyGetter = ReturnType<typeof jose.createRemoteJWKSet>;
|
|
47
|
+
/**
|
|
48
|
+
* Verification error
|
|
49
|
+
*/
|
|
50
|
+
export declare class VerifyError extends Error {
|
|
51
|
+
readonly code: string;
|
|
52
|
+
readonly statusCode: number;
|
|
53
|
+
constructor(message: string, code: string, statusCode?: number);
|
|
54
|
+
static invalidToken(reason?: string): VerifyError;
|
|
55
|
+
static tokenExpired(): VerifyError;
|
|
56
|
+
static insufficientRole(required: string, actual: string): VerifyError;
|
|
57
|
+
static insufficientAal(required: string, actual: string): VerifyError;
|
|
58
|
+
static missingToken(): VerifyError;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get the JWKS URL for a tenant
|
|
62
|
+
*
|
|
63
|
+
* @param baseUrl - The stack base URL (e.g., "https://stack.zenku.app")
|
|
64
|
+
* @param tenantId - The tenant identifier (default: "_platform")
|
|
65
|
+
* @returns The JWKS URL
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* getJWKSUrl("https://stack.zenku.app")
|
|
70
|
+
* // → "https://stack.zenku.app/auth/_platform/.well-known/jwks.json"
|
|
71
|
+
*
|
|
72
|
+
* getJWKSUrl("https://stack.zenku.app", "acme-corp")
|
|
73
|
+
* // → "https://stack.zenku.app/auth/acme-corp/.well-known/jwks.json"
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export declare function getJWKSUrl(baseUrl: string, tenantId?: string): string;
|
|
77
|
+
/**
|
|
78
|
+
* Create a JWKS key getter
|
|
79
|
+
*
|
|
80
|
+
* Automatically constructs the JWKS URL from the base URL and optional tenant ID.
|
|
81
|
+
*
|
|
82
|
+
* @param baseUrl - The stack base URL (e.g., "https://stack.zenku.app")
|
|
83
|
+
* @param tenantId - The tenant identifier (default: "_platform")
|
|
84
|
+
* @returns A key getter function for use with verifyToken
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* import { createJWKS, verifyToken } from "@kaiz11/stack-client/auth/server";
|
|
89
|
+
*
|
|
90
|
+
* // Platform (default)
|
|
91
|
+
* const getKey = createJWKS("https://stack.zenku.app");
|
|
92
|
+
*
|
|
93
|
+
* // Tenant
|
|
94
|
+
* const getKey = createJWKS("https://stack.zenku.app", "acme-corp");
|
|
95
|
+
*
|
|
96
|
+
* const payload = await verifyToken(token, getKey);
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export declare function createJWKS(baseUrl: string, tenantId?: string): JWKSKeyGetter;
|
|
100
|
+
/**
|
|
101
|
+
* Create a JWKS key getter from a URL
|
|
102
|
+
*
|
|
103
|
+
* Fetches and caches the JWKS from the GoTrue /.well-known/jwks.json endpoint.
|
|
104
|
+
* Uses jose's built-in caching and refresh logic.
|
|
105
|
+
*
|
|
106
|
+
* For most use cases, prefer `createPlatformJWKS` or `createTenantJWKS` which
|
|
107
|
+
* automatically construct the URL.
|
|
108
|
+
*
|
|
109
|
+
* @param jwksUrl - The JWKS endpoint URL
|
|
110
|
+
* @returns A key getter function for use with verifyToken
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* import { createRemoteJWKS, verifyToken } from "@kaiz11/stack-client/auth/server";
|
|
115
|
+
*
|
|
116
|
+
* const getKey = createRemoteJWKS("https://stack.zenku.app/auth/_platform/.well-known/jwks.json");
|
|
117
|
+
* const payload = await verifyToken(token, getKey);
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
export declare function createRemoteJWKS(jwksUrl: string): JWKSKeyGetter;
|
|
121
|
+
/**
|
|
122
|
+
* Create a JWKS key getter from a local JWKS object
|
|
123
|
+
*
|
|
124
|
+
* Useful for testing without making network requests.
|
|
125
|
+
*
|
|
126
|
+
* @param jwks - The JWKS object containing keys
|
|
127
|
+
* @returns A key getter function for use with verifyToken
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* import { createLocalJWKS, verifyToken } from "@kaiz11/stack-client/auth/server";
|
|
132
|
+
*
|
|
133
|
+
* const jwks = { keys: [{ kty: "EC", crv: "P-256", ... }] };
|
|
134
|
+
* const getKey = createLocalJWKS(jwks);
|
|
135
|
+
* const payload = await verifyToken(token, getKey);
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
export declare function createLocalJWKS(jwks: jose.JSONWebKeySet): JWKSKeyGetter;
|
|
139
|
+
/**
|
|
140
|
+
* Clear the JWKS cache (useful for testing or key rotation)
|
|
141
|
+
*/
|
|
142
|
+
export declare function clearJWKSCache(): void;
|
|
143
|
+
/**
|
|
144
|
+
* Verify a JWT and extract the payload using ES256 (ECDSA P-256)
|
|
145
|
+
*
|
|
146
|
+
* @param token - The JWT access token to verify
|
|
147
|
+
* @param keyGetter - JWKS key getter (from createRemoteJWKS or createLocalJWKS)
|
|
148
|
+
* @param options - Optional verification options
|
|
149
|
+
* @returns The token payload if valid
|
|
150
|
+
* @throws VerifyError if the token is invalid or expired
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* import { createRemoteJWKS, verifyToken } from "@kaiz11/stack-client/auth/server";
|
|
155
|
+
*
|
|
156
|
+
* // Create key getter once (cached)
|
|
157
|
+
* const getKey = createRemoteJWKS(process.env.JWKS_URL!);
|
|
158
|
+
*
|
|
159
|
+
* // Verify tokens
|
|
160
|
+
* const payload = await verifyToken(token, getKey);
|
|
161
|
+
* console.log("User ID:", payload.sub);
|
|
162
|
+
* console.log("Email:", payload.email);
|
|
163
|
+
* console.log("Role:", payload.role);
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
export declare function verifyToken(token: string, keyGetter: JWKSKeyGetter, options?: VerifyOptions): Promise<TokenPayload>;
|
|
167
|
+
/**
|
|
168
|
+
* Extract bearer token from Authorization header
|
|
169
|
+
*
|
|
170
|
+
* @param authHeader - The Authorization header value
|
|
171
|
+
* @returns The token string or null if not a valid Bearer token
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```typescript
|
|
175
|
+
* import { extractBearerToken } from "@kaiz11/stack-client/auth/server";
|
|
176
|
+
*
|
|
177
|
+
* const token = extractBearerToken(request.headers.get("Authorization"));
|
|
178
|
+
* if (token) {
|
|
179
|
+
* const payload = await verifyToken(token, secret);
|
|
180
|
+
* }
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
export declare function extractBearerToken(authHeader: string | null): string | null;
|
|
184
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/auth/server/verify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,cAAc;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,oBAAoB;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,eAAe;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,mBAAmB;IACnB,YAAY,CAAC,EAAE;QACb,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC;IACF,oBAAoB;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,mCAAmC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+BAA+B;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,kBAAkB,CAAC,CAAC;AAEvE;;GAEG;AACH,qBAAa,WAAY,SAAQ,KAAK;aAGlB,IAAI,EAAE,MAAM;aACZ,UAAU,EAAE,MAAM;gBAFlC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,UAAU,GAAE,MAAY;IAM1C,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW;IAQjD,MAAM,CAAC,YAAY,IAAI,WAAW;IAIlC,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW;IAQtE,MAAM,CAAC,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW;IAQrE,MAAM,CAAC,YAAY,IAAI,WAAW;CAGnC;AAKD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAA2B,GACpC,MAAM,CAER;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAA2B,GACpC,aAAa,CAEf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAgB/D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,aAAa,GAAG,aAAa,CAEvE;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,aAAa,EACxB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CA6CvB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAK3E"}
|