@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 +6 -36
- package/index.ts +1 -3
- package/lib/index.d.ts +1 -3
- package/lib/index.js +2 -3
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/lib/src/auth.d.ts +0 -69
- package/lib/src/auth.js +0 -65
- package/src/auth.ts +0 -86
package/README.md
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
# @dbx-tools/shared-email
|
|
2
2
|
|
|
3
|
-
Browser-safe email schemas and inferred types
|
|
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
|
|
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
|
-
|
|
92
|
-
|
|
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
|
-
|
|
96
|
-
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
7
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRyx5QkFBeUIsQ0FBQztBQUM1RCxPQUFPLEtBQUssS0FBSyxNQUFNLGdCQUFnQixDQUFDO0FBQ3hDLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxrQkFBa0IsRUFBRSxpQkFBaUIsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGdCQUFnQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gR0VORVJBVEVEIGJ5IHByb2plbiB3YXRjaCAtIERPIE5PVCBFRElULlxuLy8gUmVnZW5lcmF0ZWQgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZXMgaW4gLi9zcmMuXG4vLyBIYW5kIGVkaXRzIGFyZSBvdmVyd3JpdHRlbiBvbiB0aGUgbmV4dCB3YXRjaDsgdGhpcyBmaWxlIGlzIHJlYWQtb25seS5cblxuZXhwb3J0IGNvbnN0IFBBQ0tBR0VfSURFTlRJRklFUiA9IFwiQGRieC10b29scy9zaGFyZWQtZW1haWxcIjtcbmV4cG9ydCAqIGFzIGVtYWlsIGZyb20gXCIuL3NyYy9lbWFpbC50c1wiO1xuZXhwb3J0IHsgZW1haWxBdHRhY2htZW50U2NoZW1hLCBlbWFpbE1lc3NhZ2VTY2hlbWEsIGVtYWlsUmVzdWx0U2NoZW1hLCBlbWFpbFNlbmRlcnNTY2hlbWEgfSBmcm9tIFwiLi9zcmMvZW1haWwudHNcIjtcbmV4cG9ydCB0eXBlIHsgRW1haWxBdHRhY2htZW50LCBFbWFpbE1lc3NhZ2UsIEVtYWlsUmVzdWx0LCBFbWFpbFNlbmRlcnMgfSBmcm9tIFwiLi9zcmMvZW1haWwudHNcIjtcbiJdfQ==
|
package/lib/tsconfig.tsbuildinfo
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/
|
|
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.
|
|
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.
|
|
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>;
|