@dbx-tools/shared-email 0.6.90 → 0.6.92

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,13 +1,10 @@
1
1
  # @dbx-tools/shared-email
2
2
 
3
- Browser-safe email schemas and inferred types, for both things email is used for
4
- here: SENDING a message, and using an emailed code to LOG IN.
3
+ Browser-safe email sending schemas and inferred types.
5
4
 
6
5
  Import this package when a UI, Mastra tool schema, server route, or test needs
7
6
  to validate the same email payloads that
8
- [`@dbx-tools/email`](../../node/email) sends, or the same one-time-code login
9
- payloads that [`@dbx-tools/tunnel`](../../node/tunnel) and
10
- [`@dbx-tools/cli-tunnel`](../../cli/tunnel) gate traffic with.
7
+ [`@dbx-tools/email`](../../node/email) sends.
11
8
 
12
9
  Key features:
13
10
 
@@ -19,9 +16,6 @@ Key features:
19
16
  a browser client.
20
17
  - Model/tool-friendly schemas that avoid JSON Schema constraints known to cause
21
18
  problems with some serving endpoints.
22
- - Email one-time-code ACCESS GATE contract: the request/verify/status payloads
23
- and the one session cookie name, so the two server paths that implement the
24
- gate and the React `AuthGate` that drives it validate against one definition.
25
19
 
26
20
  ## Validate A Drafted Message
27
21
 
@@ -60,37 +54,13 @@ const senders = email.emailSendersSchema.parse(
60
54
  The sender schema describes the concrete `From` choices for the current user,
61
55
  the default sender, and whether the list is restricted by policy.
62
56
 
63
- ## Gate An App Behind An Emailed Code
64
-
65
- ```ts
66
- import { auth, SESSION_COOKIE_NAME } from "@dbx-tools/shared-email";
67
-
68
- const { email } = auth.authRequestSchema.parse(await request.json());
69
- const status = auth.authStatusSchema.parse(await (await fetch("/api/email/auth/status")).json());
70
- ```
71
-
72
- The flow is `POST /request` (email) → a 6-digit code is emailed → `POST /verify`
73
- (email + code) → an HttpOnly session cookie named `SESSION_COOKIE_NAME` is set →
74
- `GET /status` reports whether the caller is authenticated.
75
-
76
- `request` ALWAYS reports success. That is deliberate anti-enumeration: the
77
- response never reveals whether an address is allow-listed or was actually sent a
78
- code, so a client cannot use the login form to discover who has access. Treat a
79
- `{ ok: true }` from `request` as "the request was accepted", never as "that
80
- address exists".
81
-
82
- `SESSION_COOKIE_NAME` lives here rather than in either server package because
83
- both the in-process gate and the CLI reverse proxy SET it, and browser code
84
- reads it — three places that must agree on one string.
85
-
86
57
  ## Modules
87
58
 
88
59
  - `email` - `emailAttachmentSchema`, `emailMessageSchema`,
89
60
  `emailResultSchema`, `emailSendersSchema`, and flat inferred types:
90
61
  `EmailAttachment`, `EmailMessage`, `EmailResult`, and `EmailSenders`.
91
- - `auth` - the OTP gate wire format: `authRequestSchema`,
92
- `authRequestResultSchema`, `authVerifySchema`, `authVerifyResultSchema`,
93
- `authStatusSchema`, `SESSION_COOKIE_NAME`, and their inferred types.
62
+ The schemas intentionally avoid array `.min()` constraints so they can be reused
63
+ as model/tool JSON schemas for serving endpoints that reject `minItems`.
94
64
 
95
- The schemas intentionally avoid array `.min()` constraints so they can be reused
96
- as model/tool JSON schemas for serving endpoints that reject `minItems`.
65
+ Passwordless authentication contracts live in
66
+ [`@dbx-tools/shared-auth`](../auth).
package/index.ts CHANGED
@@ -2,9 +2,7 @@
2
2
  // Regenerated from the exporting modules in ./src.
3
3
  // Hand edits are overwritten on the next watch; this file is read-only.
4
4
 
5
- export * as auth from "./src/auth.ts";
5
+ export const PACKAGE_IDENTIFIER = "@dbx-tools/shared-email";
6
6
  export * as email from "./src/email.ts";
7
- export { SESSION_COOKIE_NAME, authRequestSchema, authRequestResultSchema, authVerifySchema, authVerifyResultSchema, authStatusSchema } from "./src/auth.ts";
8
- export type { AuthRequest, AuthRequestResult, AuthVerify, AuthVerifyResult, AuthStatus } from "./src/auth.ts";
9
7
  export { emailAttachmentSchema, emailMessageSchema, emailResultSchema, emailSendersSchema } from "./src/email.ts";
10
8
  export type { EmailAttachment, EmailMessage, EmailResult, EmailSenders } from "./src/email.ts";
package/lib/index.d.ts CHANGED
@@ -1,6 +1,4 @@
1
- export * as auth from "./src/auth.ts";
1
+ export declare const PACKAGE_IDENTIFIER = "@dbx-tools/shared-email";
2
2
  export * as email from "./src/email.ts";
3
- export { SESSION_COOKIE_NAME, authRequestSchema, authRequestResultSchema, authVerifySchema, authVerifyResultSchema, authStatusSchema } from "./src/auth.ts";
4
- export type { AuthRequest, AuthRequestResult, AuthVerify, AuthVerifyResult, AuthStatus } from "./src/auth.ts";
5
3
  export { emailAttachmentSchema, emailMessageSchema, emailResultSchema, emailSendersSchema } from "./src/email.ts";
6
4
  export type { EmailAttachment, EmailMessage, EmailResult, EmailSenders } from "./src/email.ts";
package/lib/index.js CHANGED
@@ -1,8 +1,7 @@
1
1
  // GENERATED by projen watch - DO NOT EDIT.
2
2
  // Regenerated from the exporting modules in ./src.
3
3
  // Hand edits are overwritten on the next watch; this file is read-only.
4
- export * as auth from "./src/auth.js";
4
+ export const PACKAGE_IDENTIFIER = "@dbx-tools/shared-email";
5
5
  export * as email from "./src/email.js";
6
- export { SESSION_COOKIE_NAME, authRequestSchema, authRequestResultSchema, authVerifySchema, authVerifyResultSchema, authStatusSchema } from "./src/auth.js";
7
6
  export { emailAttachmentSchema, emailMessageSchema, emailResultSchema, emailSendersSchema } from "./src/email.js";
8
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxPQUFPLEtBQUssSUFBSSxNQUFNLGVBQWUsQ0FBQztBQUN0QyxPQUFPLEtBQUssS0FBSyxNQUFNLGdCQUFnQixDQUFDO0FBQ3hDLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxpQkFBaUIsRUFBRSx1QkFBdUIsRUFBRSxnQkFBZ0IsRUFBRSxzQkFBc0IsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUU1SixPQUFPLEVBQUUscUJBQXFCLEVBQUUsa0JBQWtCLEVBQUUsaUJBQWlCLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIEdFTkVSQVRFRCBieSBwcm9qZW4gd2F0Y2ggLSBETyBOT1QgRURJVC5cbi8vIFJlZ2VuZXJhdGVkIGZyb20gdGhlIGV4cG9ydGluZyBtb2R1bGVzIGluIC4vc3JjLlxuLy8gSGFuZCBlZGl0cyBhcmUgb3ZlcndyaXR0ZW4gb24gdGhlIG5leHQgd2F0Y2g7IHRoaXMgZmlsZSBpcyByZWFkLW9ubHkuXG5cbmV4cG9ydCAqIGFzIGF1dGggZnJvbSBcIi4vc3JjL2F1dGgudHNcIjtcbmV4cG9ydCAqIGFzIGVtYWlsIGZyb20gXCIuL3NyYy9lbWFpbC50c1wiO1xuZXhwb3J0IHsgU0VTU0lPTl9DT09LSUVfTkFNRSwgYXV0aFJlcXVlc3RTY2hlbWEsIGF1dGhSZXF1ZXN0UmVzdWx0U2NoZW1hLCBhdXRoVmVyaWZ5U2NoZW1hLCBhdXRoVmVyaWZ5UmVzdWx0U2NoZW1hLCBhdXRoU3RhdHVzU2NoZW1hIH0gZnJvbSBcIi4vc3JjL2F1dGgudHNcIjtcbmV4cG9ydCB0eXBlIHsgQXV0aFJlcXVlc3QsIEF1dGhSZXF1ZXN0UmVzdWx0LCBBdXRoVmVyaWZ5LCBBdXRoVmVyaWZ5UmVzdWx0LCBBdXRoU3RhdHVzIH0gZnJvbSBcIi4vc3JjL2F1dGgudHNcIjtcbmV4cG9ydCB7IGVtYWlsQXR0YWNobWVudFNjaGVtYSwgZW1haWxNZXNzYWdlU2NoZW1hLCBlbWFpbFJlc3VsdFNjaGVtYSwgZW1haWxTZW5kZXJzU2NoZW1hIH0gZnJvbSBcIi4vc3JjL2VtYWlsLnRzXCI7XG5leHBvcnQgdHlwZSB7IEVtYWlsQXR0YWNobWVudCwgRW1haWxNZXNzYWdlLCBFbWFpbFJlc3VsdCwgRW1haWxTZW5kZXJzIH0gZnJvbSBcIi4vc3JjL2VtYWlsLnRzXCI7XG4iXX0=
7
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRyx5QkFBeUIsQ0FBQztBQUM1RCxPQUFPLEtBQUssS0FBSyxNQUFNLGdCQUFnQixDQUFDO0FBQ3hDLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxrQkFBa0IsRUFBRSxpQkFBaUIsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGdCQUFnQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gR0VORVJBVEVEIGJ5IHByb2plbiB3YXRjaCAtIERPIE5PVCBFRElULlxuLy8gUmVnZW5lcmF0ZWQgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZXMgaW4gLi9zcmMuXG4vLyBIYW5kIGVkaXRzIGFyZSBvdmVyd3JpdHRlbiBvbiB0aGUgbmV4dCB3YXRjaDsgdGhpcyBmaWxlIGlzIHJlYWQtb25seS5cblxuZXhwb3J0IGNvbnN0IFBBQ0tBR0VfSURFTlRJRklFUiA9IFwiQGRieC10b29scy9zaGFyZWQtZW1haWxcIjtcbmV4cG9ydCAqIGFzIGVtYWlsIGZyb20gXCIuL3NyYy9lbWFpbC50c1wiO1xuZXhwb3J0IHsgZW1haWxBdHRhY2htZW50U2NoZW1hLCBlbWFpbE1lc3NhZ2VTY2hlbWEsIGVtYWlsUmVzdWx0U2NoZW1hLCBlbWFpbFNlbmRlcnNTY2hlbWEgfSBmcm9tIFwiLi9zcmMvZW1haWwudHNcIjtcbmV4cG9ydCB0eXBlIHsgRW1haWxBdHRhY2htZW50LCBFbWFpbE1lc3NhZ2UsIEVtYWlsUmVzdWx0LCBFbWFpbFNlbmRlcnMgfSBmcm9tIFwiLi9zcmMvZW1haWwudHNcIjtcbiJdfQ==
@@ -1 +1 @@
1
- {"root":["../src/auth.ts","../src/email.ts","../index.ts"],"version":"5.9.3"}
1
+ {"root":["../src/email.ts","../index.ts"],"version":"5.9.3"}
package/package.json CHANGED
@@ -23,7 +23,7 @@
23
23
  "typescript": "^5.9.3"
24
24
  },
25
25
  "dependencies": {
26
- "@dbx-tools/shared-core": "0.6.90",
26
+ "@dbx-tools/shared-core": "0.6.92",
27
27
  "zod": "4.3.6"
28
28
  },
29
29
  "main": "./lib/index.js",
@@ -40,7 +40,7 @@
40
40
  "./package.json": "./package.json"
41
41
  }
42
42
  },
43
- "version": "0.6.90",
43
+ "version": "0.6.92",
44
44
  "types": "./lib/index.d.ts",
45
45
  "type": "module",
46
46
  "exports": {
package/lib/src/auth.d.ts DELETED
@@ -1,69 +0,0 @@
1
- /**
2
- * Wire-format contract for the email one-time-password ACCESS GATE.
3
- *
4
- * A companion to the email add-on's send contract ({@link ./email}): where that
5
- * lets an agent send mail, this lets an AppKit app put an email-OTP gate in front
6
- * of itself - the login flow for an app exposed publicly (e.g. through a portr
7
- * tunnel that bypasses the Databricks front door). Pure zod so the server routes,
8
- * the React `AuthGate`, and any client validate against one definition.
9
- *
10
- * Flow: `POST /request` (email) -> a 6-digit code is emailed -> `POST /verify`
11
- * (email + code) -> an HttpOnly session cookie is set -> `GET /status` reports
12
- * whether the caller is authenticated. `request` ALWAYS reports success (the
13
- * server never reveals whether an address is allow-listed or was actually sent a
14
- * code - anti-enumeration).
15
- *
16
- * @module
17
- */
18
- import { z } from "zod";
19
- /**
20
- * Name of the HttpOnly cookie the tunnel gate stores its session JWT in. Shared
21
- * so the proxy (which sets it) and any client-side code agree on one name.
22
- */
23
- export declare const SESSION_COOKIE_NAME = "dbx-tools-auth";
24
- /** `POST /api/email/auth/request` body: ask for a code to be emailed. */
25
- export declare const authRequestSchema: z.ZodObject<{
26
- email: z.ZodString;
27
- }, z.core.$strip>;
28
- /** {@link authRequestSchema} */
29
- export type AuthRequest = z.infer<typeof authRequestSchema>;
30
- /**
31
- * `POST /api/email/auth/request` response. Always `{ ok: true }` on a
32
- * well-formed request, regardless of whether the address was allowed or a code
33
- * was actually sent - the client cannot distinguish an allowed address from a
34
- * rejected one (anti-enumeration). `retryAfter` is set only when the caller is
35
- * rate-limited, so a UI can show a cooldown without leaking allow-list state.
36
- */
37
- export declare const authRequestResultSchema: z.ZodObject<{
38
- ok: z.ZodLiteral<true>;
39
- retryAfter: z.ZodOptional<z.ZodNumber>;
40
- }, z.core.$strip>;
41
- /** {@link authRequestResultSchema} */
42
- export type AuthRequestResult = z.infer<typeof authRequestResultSchema>;
43
- /** `POST /api/email/auth/verify` body: submit the emailed code. */
44
- export declare const authVerifySchema: z.ZodObject<{
45
- email: z.ZodString;
46
- code: z.ZodString;
47
- }, z.core.$strip>;
48
- /** {@link authVerifySchema} */
49
- export type AuthVerify = z.infer<typeof authVerifySchema>;
50
- /**
51
- * `POST /api/email/auth/verify` response. `ok` true means the code matched and
52
- * an HttpOnly session cookie was set; false means it did not (wrong, expired, or
53
- * too many attempts) - the message is deliberately generic. `retryAfter` is set
54
- * when the failure was a rate limit.
55
- */
56
- export declare const authVerifyResultSchema: z.ZodObject<{
57
- ok: z.ZodBoolean;
58
- retryAfter: z.ZodOptional<z.ZodNumber>;
59
- }, z.core.$strip>;
60
- /** {@link authVerifyResultSchema} */
61
- export type AuthVerifyResult = z.infer<typeof authVerifyResultSchema>;
62
- /** `GET /api/email/auth/status` response: is the caller authenticated, and as whom. */
63
- export declare const authStatusSchema: z.ZodObject<{
64
- authenticated: z.ZodBoolean;
65
- email: z.ZodOptional<z.ZodString>;
66
- enabled: z.ZodBoolean;
67
- }, z.core.$strip>;
68
- /** {@link authStatusSchema} */
69
- export type AuthStatus = z.infer<typeof authStatusSchema>;
package/lib/src/auth.js DELETED
@@ -1,65 +0,0 @@
1
- /**
2
- * Wire-format contract for the email one-time-password ACCESS GATE.
3
- *
4
- * A companion to the email add-on's send contract ({@link ./email}): where that
5
- * lets an agent send mail, this lets an AppKit app put an email-OTP gate in front
6
- * of itself - the login flow for an app exposed publicly (e.g. through a portr
7
- * tunnel that bypasses the Databricks front door). Pure zod so the server routes,
8
- * the React `AuthGate`, and any client validate against one definition.
9
- *
10
- * Flow: `POST /request` (email) -> a 6-digit code is emailed -> `POST /verify`
11
- * (email + code) -> an HttpOnly session cookie is set -> `GET /status` reports
12
- * whether the caller is authenticated. `request` ALWAYS reports success (the
13
- * server never reveals whether an address is allow-listed or was actually sent a
14
- * code - anti-enumeration).
15
- *
16
- * @module
17
- */
18
- import { z } from "zod";
19
- /**
20
- * Name of the HttpOnly cookie the tunnel gate stores its session JWT in. Shared
21
- * so the proxy (which sets it) and any client-side code agree on one name.
22
- */
23
- export const SESSION_COOKIE_NAME = "dbx-tools-auth";
24
- /** `POST /api/email/auth/request` body: ask for a code to be emailed. */
25
- export const authRequestSchema = z.object({
26
- email: z.string().describe("Address to email a one-time code to, if it is allowed."),
27
- });
28
- /**
29
- * `POST /api/email/auth/request` response. Always `{ ok: true }` on a
30
- * well-formed request, regardless of whether the address was allowed or a code
31
- * was actually sent - the client cannot distinguish an allowed address from a
32
- * rejected one (anti-enumeration). `retryAfter` is set only when the caller is
33
- * rate-limited, so a UI can show a cooldown without leaking allow-list state.
34
- */
35
- export const authRequestResultSchema = z.object({
36
- ok: z.literal(true).describe("Always true for a well-formed request (anti-enumeration)."),
37
- retryAfter: z
38
- .number()
39
- .optional()
40
- .describe("Seconds to wait before requesting again, when rate-limited."),
41
- });
42
- /** `POST /api/email/auth/verify` body: submit the emailed code. */
43
- export const authVerifySchema = z.object({
44
- email: z.string().describe("The address the code was requested for."),
45
- code: z.string().describe("The one-time code from the email."),
46
- });
47
- /**
48
- * `POST /api/email/auth/verify` response. `ok` true means the code matched and
49
- * an HttpOnly session cookie was set; false means it did not (wrong, expired, or
50
- * too many attempts) - the message is deliberately generic. `retryAfter` is set
51
- * when the failure was a rate limit.
52
- */
53
- export const authVerifyResultSchema = z.object({
54
- ok: z.boolean().describe("True when the code matched and a session was established."),
55
- retryAfter: z.number().optional().describe("Seconds to wait before retrying, when rate-limited."),
56
- });
57
- /** `GET /api/email/auth/status` response: is the caller authenticated, and as whom. */
58
- export const authStatusSchema = z.object({
59
- authenticated: z.boolean().describe("True when the request carries a valid session."),
60
- email: z.string().optional().describe("The authenticated address, when authenticated."),
61
- enabled: z
62
- .boolean()
63
- .describe("True when the gate is active; false means the app is open (no login needed)."),
64
- });
65
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hdXRoLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7O0dBZ0JHO0FBRUgsT0FBTyxFQUFFLENBQUMsRUFBRSxNQUFNLEtBQUssQ0FBQztBQUV4Qjs7O0dBR0c7QUFDSCxNQUFNLENBQUMsTUFBTSxtQkFBbUIsR0FBRyxnQkFBZ0IsQ0FBQztBQUVwRCx5RUFBeUU7QUFDekUsTUFBTSxDQUFDLE1BQU0saUJBQWlCLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQztJQUN4QyxLQUFLLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyx3REFBd0QsQ0FBQztDQUNyRixDQUFDLENBQUM7QUFLSDs7Ozs7O0dBTUc7QUFDSCxNQUFNLENBQUMsTUFBTSx1QkFBdUIsR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDO0lBQzlDLEVBQUUsRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLFFBQVEsQ0FBQywyREFBMkQsQ0FBQztJQUN6RixVQUFVLEVBQUUsQ0FBQztTQUNWLE1BQU0sRUFBRTtTQUNSLFFBQVEsRUFBRTtTQUNWLFFBQVEsQ0FBQyw2REFBNkQsQ0FBQztDQUMzRSxDQUFDLENBQUM7QUFLSCxtRUFBbUU7QUFDbkUsTUFBTSxDQUFDLE1BQU0sZ0JBQWdCLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQztJQUN2QyxLQUFLLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyx5Q0FBeUMsQ0FBQztJQUNyRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyxtQ0FBbUMsQ0FBQztDQUMvRCxDQUFDLENBQUM7QUFLSDs7Ozs7R0FLRztBQUNILE1BQU0sQ0FBQyxNQUFNLHNCQUFzQixHQUFHLENBQUMsQ0FBQyxNQUFNLENBQUM7SUFDN0MsRUFBRSxFQUFFLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxRQUFRLENBQUMsMkRBQTJELENBQUM7SUFDckYsVUFBVSxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLENBQUMscURBQXFELENBQUM7Q0FDbEcsQ0FBQyxDQUFDO0FBS0gsdUZBQXVGO0FBQ3ZGLE1BQU0sQ0FBQyxNQUFNLGdCQUFnQixHQUFHLENBQUMsQ0FBQyxNQUFNLENBQUM7SUFDdkMsYUFBYSxFQUFFLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxRQUFRLENBQUMsZ0RBQWdELENBQUM7SUFDckYsS0FBSyxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLENBQUMsZ0RBQWdELENBQUM7SUFDdkYsT0FBTyxFQUFFLENBQUM7U0FDUCxPQUFPLEVBQUU7U0FDVCxRQUFRLENBQUMsOEVBQThFLENBQUM7Q0FDNUYsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBXaXJlLWZvcm1hdCBjb250cmFjdCBmb3IgdGhlIGVtYWlsIG9uZS10aW1lLXBhc3N3b3JkIEFDQ0VTUyBHQVRFLlxuICpcbiAqIEEgY29tcGFuaW9uIHRvIHRoZSBlbWFpbCBhZGQtb24ncyBzZW5kIGNvbnRyYWN0ICh7QGxpbmsgLi9lbWFpbH0pOiB3aGVyZSB0aGF0XG4gKiBsZXRzIGFuIGFnZW50IHNlbmQgbWFpbCwgdGhpcyBsZXRzIGFuIEFwcEtpdCBhcHAgcHV0IGFuIGVtYWlsLU9UUCBnYXRlIGluIGZyb250XG4gKiBvZiBpdHNlbGYgLSB0aGUgbG9naW4gZmxvdyBmb3IgYW4gYXBwIGV4cG9zZWQgcHVibGljbHkgKGUuZy4gdGhyb3VnaCBhIHBvcnRyXG4gKiB0dW5uZWwgdGhhdCBieXBhc3NlcyB0aGUgRGF0YWJyaWNrcyBmcm9udCBkb29yKS4gUHVyZSB6b2Qgc28gdGhlIHNlcnZlciByb3V0ZXMsXG4gKiB0aGUgUmVhY3QgYEF1dGhHYXRlYCwgYW5kIGFueSBjbGllbnQgdmFsaWRhdGUgYWdhaW5zdCBvbmUgZGVmaW5pdGlvbi5cbiAqXG4gKiBGbG93OiBgUE9TVCAvcmVxdWVzdGAgKGVtYWlsKSAtPiBhIDYtZGlnaXQgY29kZSBpcyBlbWFpbGVkIC0+IGBQT1NUIC92ZXJpZnlgXG4gKiAoZW1haWwgKyBjb2RlKSAtPiBhbiBIdHRwT25seSBzZXNzaW9uIGNvb2tpZSBpcyBzZXQgLT4gYEdFVCAvc3RhdHVzYCByZXBvcnRzXG4gKiB3aGV0aGVyIHRoZSBjYWxsZXIgaXMgYXV0aGVudGljYXRlZC4gYHJlcXVlc3RgIEFMV0FZUyByZXBvcnRzIHN1Y2Nlc3MgKHRoZVxuICogc2VydmVyIG5ldmVyIHJldmVhbHMgd2hldGhlciBhbiBhZGRyZXNzIGlzIGFsbG93LWxpc3RlZCBvciB3YXMgYWN0dWFsbHkgc2VudCBhXG4gKiBjb2RlIC0gYW50aS1lbnVtZXJhdGlvbikuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IHogfSBmcm9tIFwiem9kXCI7XG5cbi8qKlxuICogTmFtZSBvZiB0aGUgSHR0cE9ubHkgY29va2llIHRoZSB0dW5uZWwgZ2F0ZSBzdG9yZXMgaXRzIHNlc3Npb24gSldUIGluLiBTaGFyZWRcbiAqIHNvIHRoZSBwcm94eSAod2hpY2ggc2V0cyBpdCkgYW5kIGFueSBjbGllbnQtc2lkZSBjb2RlIGFncmVlIG9uIG9uZSBuYW1lLlxuICovXG5leHBvcnQgY29uc3QgU0VTU0lPTl9DT09LSUVfTkFNRSA9IFwiZGJ4LXRvb2xzLWF1dGhcIjtcblxuLyoqIGBQT1NUIC9hcGkvZW1haWwvYXV0aC9yZXF1ZXN0YCBib2R5OiBhc2sgZm9yIGEgY29kZSB0byBiZSBlbWFpbGVkLiAqL1xuZXhwb3J0IGNvbnN0IGF1dGhSZXF1ZXN0U2NoZW1hID0gei5vYmplY3Qoe1xuICBlbWFpbDogei5zdHJpbmcoKS5kZXNjcmliZShcIkFkZHJlc3MgdG8gZW1haWwgYSBvbmUtdGltZSBjb2RlIHRvLCBpZiBpdCBpcyBhbGxvd2VkLlwiKSxcbn0pO1xuXG4vKioge0BsaW5rIGF1dGhSZXF1ZXN0U2NoZW1hfSAqL1xuZXhwb3J0IHR5cGUgQXV0aFJlcXVlc3QgPSB6LmluZmVyPHR5cGVvZiBhdXRoUmVxdWVzdFNjaGVtYT47XG5cbi8qKlxuICogYFBPU1QgL2FwaS9lbWFpbC9hdXRoL3JlcXVlc3RgIHJlc3BvbnNlLiBBbHdheXMgYHsgb2s6IHRydWUgfWAgb24gYVxuICogd2VsbC1mb3JtZWQgcmVxdWVzdCwgcmVnYXJkbGVzcyBvZiB3aGV0aGVyIHRoZSBhZGRyZXNzIHdhcyBhbGxvd2VkIG9yIGEgY29kZVxuICogd2FzIGFjdHVhbGx5IHNlbnQgLSB0aGUgY2xpZW50IGNhbm5vdCBkaXN0aW5ndWlzaCBhbiBhbGxvd2VkIGFkZHJlc3MgZnJvbSBhXG4gKiByZWplY3RlZCBvbmUgKGFudGktZW51bWVyYXRpb24pLiBgcmV0cnlBZnRlcmAgaXMgc2V0IG9ubHkgd2hlbiB0aGUgY2FsbGVyIGlzXG4gKiByYXRlLWxpbWl0ZWQsIHNvIGEgVUkgY2FuIHNob3cgYSBjb29sZG93biB3aXRob3V0IGxlYWtpbmcgYWxsb3ctbGlzdCBzdGF0ZS5cbiAqL1xuZXhwb3J0IGNvbnN0IGF1dGhSZXF1ZXN0UmVzdWx0U2NoZW1hID0gei5vYmplY3Qoe1xuICBvazogei5saXRlcmFsKHRydWUpLmRlc2NyaWJlKFwiQWx3YXlzIHRydWUgZm9yIGEgd2VsbC1mb3JtZWQgcmVxdWVzdCAoYW50aS1lbnVtZXJhdGlvbikuXCIpLFxuICByZXRyeUFmdGVyOiB6XG4gICAgLm51bWJlcigpXG4gICAgLm9wdGlvbmFsKClcbiAgICAuZGVzY3JpYmUoXCJTZWNvbmRzIHRvIHdhaXQgYmVmb3JlIHJlcXVlc3RpbmcgYWdhaW4sIHdoZW4gcmF0ZS1saW1pdGVkLlwiKSxcbn0pO1xuXG4vKioge0BsaW5rIGF1dGhSZXF1ZXN0UmVzdWx0U2NoZW1hfSAqL1xuZXhwb3J0IHR5cGUgQXV0aFJlcXVlc3RSZXN1bHQgPSB6LmluZmVyPHR5cGVvZiBhdXRoUmVxdWVzdFJlc3VsdFNjaGVtYT47XG5cbi8qKiBgUE9TVCAvYXBpL2VtYWlsL2F1dGgvdmVyaWZ5YCBib2R5OiBzdWJtaXQgdGhlIGVtYWlsZWQgY29kZS4gKi9cbmV4cG9ydCBjb25zdCBhdXRoVmVyaWZ5U2NoZW1hID0gei5vYmplY3Qoe1xuICBlbWFpbDogei5zdHJpbmcoKS5kZXNjcmliZShcIlRoZSBhZGRyZXNzIHRoZSBjb2RlIHdhcyByZXF1ZXN0ZWQgZm9yLlwiKSxcbiAgY29kZTogei5zdHJpbmcoKS5kZXNjcmliZShcIlRoZSBvbmUtdGltZSBjb2RlIGZyb20gdGhlIGVtYWlsLlwiKSxcbn0pO1xuXG4vKioge0BsaW5rIGF1dGhWZXJpZnlTY2hlbWF9ICovXG5leHBvcnQgdHlwZSBBdXRoVmVyaWZ5ID0gei5pbmZlcjx0eXBlb2YgYXV0aFZlcmlmeVNjaGVtYT47XG5cbi8qKlxuICogYFBPU1QgL2FwaS9lbWFpbC9hdXRoL3ZlcmlmeWAgcmVzcG9uc2UuIGBva2AgdHJ1ZSBtZWFucyB0aGUgY29kZSBtYXRjaGVkIGFuZFxuICogYW4gSHR0cE9ubHkgc2Vzc2lvbiBjb29raWUgd2FzIHNldDsgZmFsc2UgbWVhbnMgaXQgZGlkIG5vdCAod3JvbmcsIGV4cGlyZWQsIG9yXG4gKiB0b28gbWFueSBhdHRlbXB0cykgLSB0aGUgbWVzc2FnZSBpcyBkZWxpYmVyYXRlbHkgZ2VuZXJpYy4gYHJldHJ5QWZ0ZXJgIGlzIHNldFxuICogd2hlbiB0aGUgZmFpbHVyZSB3YXMgYSByYXRlIGxpbWl0LlxuICovXG5leHBvcnQgY29uc3QgYXV0aFZlcmlmeVJlc3VsdFNjaGVtYSA9IHoub2JqZWN0KHtcbiAgb2s6IHouYm9vbGVhbigpLmRlc2NyaWJlKFwiVHJ1ZSB3aGVuIHRoZSBjb2RlIG1hdGNoZWQgYW5kIGEgc2Vzc2lvbiB3YXMgZXN0YWJsaXNoZWQuXCIpLFxuICByZXRyeUFmdGVyOiB6Lm51bWJlcigpLm9wdGlvbmFsKCkuZGVzY3JpYmUoXCJTZWNvbmRzIHRvIHdhaXQgYmVmb3JlIHJldHJ5aW5nLCB3aGVuIHJhdGUtbGltaXRlZC5cIiksXG59KTtcblxuLyoqIHtAbGluayBhdXRoVmVyaWZ5UmVzdWx0U2NoZW1hfSAqL1xuZXhwb3J0IHR5cGUgQXV0aFZlcmlmeVJlc3VsdCA9IHouaW5mZXI8dHlwZW9mIGF1dGhWZXJpZnlSZXN1bHRTY2hlbWE+O1xuXG4vKiogYEdFVCAvYXBpL2VtYWlsL2F1dGgvc3RhdHVzYCByZXNwb25zZTogaXMgdGhlIGNhbGxlciBhdXRoZW50aWNhdGVkLCBhbmQgYXMgd2hvbS4gKi9cbmV4cG9ydCBjb25zdCBhdXRoU3RhdHVzU2NoZW1hID0gei5vYmplY3Qoe1xuICBhdXRoZW50aWNhdGVkOiB6LmJvb2xlYW4oKS5kZXNjcmliZShcIlRydWUgd2hlbiB0aGUgcmVxdWVzdCBjYXJyaWVzIGEgdmFsaWQgc2Vzc2lvbi5cIiksXG4gIGVtYWlsOiB6LnN0cmluZygpLm9wdGlvbmFsKCkuZGVzY3JpYmUoXCJUaGUgYXV0aGVudGljYXRlZCBhZGRyZXNzLCB3aGVuIGF1dGhlbnRpY2F0ZWQuXCIpLFxuICBlbmFibGVkOiB6XG4gICAgLmJvb2xlYW4oKVxuICAgIC5kZXNjcmliZShcIlRydWUgd2hlbiB0aGUgZ2F0ZSBpcyBhY3RpdmU7IGZhbHNlIG1lYW5zIHRoZSBhcHAgaXMgb3BlbiAobm8gbG9naW4gbmVlZGVkKS5cIiksXG59KTtcblxuLyoqIHtAbGluayBhdXRoU3RhdHVzU2NoZW1hfSAqL1xuZXhwb3J0IHR5cGUgQXV0aFN0YXR1cyA9IHouaW5mZXI8dHlwZW9mIGF1dGhTdGF0dXNTY2hlbWE+O1xuIl19
package/src/auth.ts DELETED
@@ -1,86 +0,0 @@
1
- /**
2
- * Wire-format contract for the email one-time-password ACCESS GATE.
3
- *
4
- * A companion to the email add-on's send contract ({@link ./email}): where that
5
- * lets an agent send mail, this lets an AppKit app put an email-OTP gate in front
6
- * of itself - the login flow for an app exposed publicly (e.g. through a portr
7
- * tunnel that bypasses the Databricks front door). Pure zod so the server routes,
8
- * the React `AuthGate`, and any client validate against one definition.
9
- *
10
- * Flow: `POST /request` (email) -> a 6-digit code is emailed -> `POST /verify`
11
- * (email + code) -> an HttpOnly session cookie is set -> `GET /status` reports
12
- * whether the caller is authenticated. `request` ALWAYS reports success (the
13
- * server never reveals whether an address is allow-listed or was actually sent a
14
- * code - anti-enumeration).
15
- *
16
- * @module
17
- */
18
-
19
- import { z } from "zod";
20
-
21
- /**
22
- * Name of the HttpOnly cookie the tunnel gate stores its session JWT in. Shared
23
- * so the proxy (which sets it) and any client-side code agree on one name.
24
- */
25
- export const SESSION_COOKIE_NAME = "dbx-tools-auth";
26
-
27
- /** `POST /api/email/auth/request` body: ask for a code to be emailed. */
28
- export const authRequestSchema = z.object({
29
- email: z.string().describe("Address to email a one-time code to, if it is allowed."),
30
- });
31
-
32
- /** {@link authRequestSchema} */
33
- export type AuthRequest = z.infer<typeof authRequestSchema>;
34
-
35
- /**
36
- * `POST /api/email/auth/request` response. Always `{ ok: true }` on a
37
- * well-formed request, regardless of whether the address was allowed or a code
38
- * was actually sent - the client cannot distinguish an allowed address from a
39
- * rejected one (anti-enumeration). `retryAfter` is set only when the caller is
40
- * rate-limited, so a UI can show a cooldown without leaking allow-list state.
41
- */
42
- export const authRequestResultSchema = z.object({
43
- ok: z.literal(true).describe("Always true for a well-formed request (anti-enumeration)."),
44
- retryAfter: z
45
- .number()
46
- .optional()
47
- .describe("Seconds to wait before requesting again, when rate-limited."),
48
- });
49
-
50
- /** {@link authRequestResultSchema} */
51
- export type AuthRequestResult = z.infer<typeof authRequestResultSchema>;
52
-
53
- /** `POST /api/email/auth/verify` body: submit the emailed code. */
54
- export const authVerifySchema = z.object({
55
- email: z.string().describe("The address the code was requested for."),
56
- code: z.string().describe("The one-time code from the email."),
57
- });
58
-
59
- /** {@link authVerifySchema} */
60
- export type AuthVerify = z.infer<typeof authVerifySchema>;
61
-
62
- /**
63
- * `POST /api/email/auth/verify` response. `ok` true means the code matched and
64
- * an HttpOnly session cookie was set; false means it did not (wrong, expired, or
65
- * too many attempts) - the message is deliberately generic. `retryAfter` is set
66
- * when the failure was a rate limit.
67
- */
68
- export const authVerifyResultSchema = z.object({
69
- ok: z.boolean().describe("True when the code matched and a session was established."),
70
- retryAfter: z.number().optional().describe("Seconds to wait before retrying, when rate-limited."),
71
- });
72
-
73
- /** {@link authVerifyResultSchema} */
74
- export type AuthVerifyResult = z.infer<typeof authVerifyResultSchema>;
75
-
76
- /** `GET /api/email/auth/status` response: is the caller authenticated, and as whom. */
77
- export const authStatusSchema = z.object({
78
- authenticated: z.boolean().describe("True when the request carries a valid session."),
79
- email: z.string().optional().describe("The authenticated address, when authenticated."),
80
- enabled: z
81
- .boolean()
82
- .describe("True when the gate is active; false means the app is open (no login needed)."),
83
- });
84
-
85
- /** {@link authStatusSchema} */
86
- export type AuthStatus = z.infer<typeof authStatusSchema>;