@dbx-tools/shared-email 0.6.40 → 0.6.42

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/index.ts CHANGED
@@ -2,6 +2,9 @@
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
6
  export * as email from "./src/email.ts";
7
+ export { authRequestSchema, authRequestResultSchema, authVerifySchema, authVerifyResultSchema, authStatusSchema } from "./src/auth.ts";
8
+ export type { AuthRequest, AuthRequestResult, AuthVerify, AuthVerifyResult, AuthStatus } from "./src/auth.ts";
6
9
  export { emailAttachmentSchema, emailMessageSchema, emailResultSchema, emailSendersSchema } from "./src/email.ts";
7
10
  export type { EmailAttachment, EmailMessage, EmailResult, EmailSenders } from "./src/email.ts";
package/lib/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ export * as auth from "./src/auth.ts";
1
2
  export * as email from "./src/email.ts";
3
+ export { authRequestSchema, authRequestResultSchema, authVerifySchema, authVerifyResultSchema, authStatusSchema } from "./src/auth.ts";
4
+ export type { AuthRequest, AuthRequestResult, AuthVerify, AuthVerifyResult, AuthStatus } from "./src/auth.ts";
2
5
  export { emailAttachmentSchema, emailMessageSchema, emailResultSchema, emailSendersSchema } from "./src/email.ts";
3
6
  export type { EmailAttachment, EmailMessage, EmailResult, EmailSenders } from "./src/email.ts";
package/lib/index.js CHANGED
@@ -1,6 +1,8 @@
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
5
  export * as email from "./src/email.js";
6
+ export { authRequestSchema, authRequestResultSchema, authVerifySchema, authVerifyResultSchema, authStatusSchema } from "./src/auth.js";
5
7
  export { emailAttachmentSchema, emailMessageSchema, emailResultSchema, emailSendersSchema } from "./src/email.js";
6
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxPQUFPLEtBQUssS0FBSyxNQUFNLGdCQUFnQixDQUFDO0FBQ3hDLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxrQkFBa0IsRUFBRSxpQkFBaUIsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGdCQUFnQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gR0VORVJBVEVEIGJ5IHByb2plbiB3YXRjaCAtIERPIE5PVCBFRElULlxuLy8gUmVnZW5lcmF0ZWQgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZXMgaW4gLi9zcmMuXG4vLyBIYW5kIGVkaXRzIGFyZSBvdmVyd3JpdHRlbiBvbiB0aGUgbmV4dCB3YXRjaDsgdGhpcyBmaWxlIGlzIHJlYWQtb25seS5cblxuZXhwb3J0ICogYXMgZW1haWwgZnJvbSBcIi4vc3JjL2VtYWlsLnRzXCI7XG5leHBvcnQgeyBlbWFpbEF0dGFjaG1lbnRTY2hlbWEsIGVtYWlsTWVzc2FnZVNjaGVtYSwgZW1haWxSZXN1bHRTY2hlbWEsIGVtYWlsU2VuZGVyc1NjaGVtYSB9IGZyb20gXCIuL3NyYy9lbWFpbC50c1wiO1xuZXhwb3J0IHR5cGUgeyBFbWFpbEF0dGFjaG1lbnQsIEVtYWlsTWVzc2FnZSwgRW1haWxSZXN1bHQsIEVtYWlsU2VuZGVycyB9IGZyb20gXCIuL3NyYy9lbWFpbC50c1wiO1xuIl19
8
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxPQUFPLEtBQUssSUFBSSxNQUFNLGVBQWUsQ0FBQztBQUN0QyxPQUFPLEtBQUssS0FBSyxNQUFNLGdCQUFnQixDQUFDO0FBQ3hDLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSx1QkFBdUIsRUFBRSxnQkFBZ0IsRUFBRSxzQkFBc0IsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUV2SSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsa0JBQWtCLEVBQUUsaUJBQWlCLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIEdFTkVSQVRFRCBieSBwcm9qZW4gd2F0Y2ggLSBETyBOT1QgRURJVC5cbi8vIFJlZ2VuZXJhdGVkIGZyb20gdGhlIGV4cG9ydGluZyBtb2R1bGVzIGluIC4vc3JjLlxuLy8gSGFuZCBlZGl0cyBhcmUgb3ZlcndyaXR0ZW4gb24gdGhlIG5leHQgd2F0Y2g7IHRoaXMgZmlsZSBpcyByZWFkLW9ubHkuXG5cbmV4cG9ydCAqIGFzIGF1dGggZnJvbSBcIi4vc3JjL2F1dGgudHNcIjtcbmV4cG9ydCAqIGFzIGVtYWlsIGZyb20gXCIuL3NyYy9lbWFpbC50c1wiO1xuZXhwb3J0IHsgYXV0aFJlcXVlc3RTY2hlbWEsIGF1dGhSZXF1ZXN0UmVzdWx0U2NoZW1hLCBhdXRoVmVyaWZ5U2NoZW1hLCBhdXRoVmVyaWZ5UmVzdWx0U2NoZW1hLCBhdXRoU3RhdHVzU2NoZW1hIH0gZnJvbSBcIi4vc3JjL2F1dGgudHNcIjtcbmV4cG9ydCB0eXBlIHsgQXV0aFJlcXVlc3QsIEF1dGhSZXF1ZXN0UmVzdWx0LCBBdXRoVmVyaWZ5LCBBdXRoVmVyaWZ5UmVzdWx0LCBBdXRoU3RhdHVzIH0gZnJvbSBcIi4vc3JjL2F1dGgudHNcIjtcbmV4cG9ydCB7IGVtYWlsQXR0YWNobWVudFNjaGVtYSwgZW1haWxNZXNzYWdlU2NoZW1hLCBlbWFpbFJlc3VsdFNjaGVtYSwgZW1haWxTZW5kZXJzU2NoZW1hIH0gZnJvbSBcIi4vc3JjL2VtYWlsLnRzXCI7XG5leHBvcnQgdHlwZSB7IEVtYWlsQXR0YWNobWVudCwgRW1haWxNZXNzYWdlLCBFbWFpbFJlc3VsdCwgRW1haWxTZW5kZXJzIH0gZnJvbSBcIi4vc3JjL2VtYWlsLnRzXCI7XG4iXX0=
@@ -0,0 +1,64 @@
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
+ /** `POST /api/email/auth/request` body: ask for a code to be emailed. */
20
+ export declare const authRequestSchema: z.ZodObject<{
21
+ email: z.ZodString;
22
+ }, z.core.$strip>;
23
+ /** {@link authRequestSchema} */
24
+ export type AuthRequest = z.infer<typeof authRequestSchema>;
25
+ /**
26
+ * `POST /api/email/auth/request` response. Always `{ ok: true }` on a
27
+ * well-formed request, regardless of whether the address was allowed or a code
28
+ * was actually sent - the client cannot distinguish an allowed address from a
29
+ * rejected one (anti-enumeration). `retryAfter` is set only when the caller is
30
+ * rate-limited, so a UI can show a cooldown without leaking allow-list state.
31
+ */
32
+ export declare const authRequestResultSchema: z.ZodObject<{
33
+ ok: z.ZodLiteral<true>;
34
+ retryAfter: z.ZodOptional<z.ZodNumber>;
35
+ }, z.core.$strip>;
36
+ /** {@link authRequestResultSchema} */
37
+ export type AuthRequestResult = z.infer<typeof authRequestResultSchema>;
38
+ /** `POST /api/email/auth/verify` body: submit the emailed code. */
39
+ export declare const authVerifySchema: z.ZodObject<{
40
+ email: z.ZodString;
41
+ code: z.ZodString;
42
+ }, z.core.$strip>;
43
+ /** {@link authVerifySchema} */
44
+ export type AuthVerify = z.infer<typeof authVerifySchema>;
45
+ /**
46
+ * `POST /api/email/auth/verify` response. `ok` true means the code matched and
47
+ * an HttpOnly session cookie was set; false means it did not (wrong, expired, or
48
+ * too many attempts) - the message is deliberately generic. `retryAfter` is set
49
+ * when the failure was a rate limit.
50
+ */
51
+ export declare const authVerifyResultSchema: z.ZodObject<{
52
+ ok: z.ZodBoolean;
53
+ retryAfter: z.ZodOptional<z.ZodNumber>;
54
+ }, z.core.$strip>;
55
+ /** {@link authVerifyResultSchema} */
56
+ export type AuthVerifyResult = z.infer<typeof authVerifyResultSchema>;
57
+ /** `GET /api/email/auth/status` response: is the caller authenticated, and as whom. */
58
+ export declare const authStatusSchema: z.ZodObject<{
59
+ authenticated: z.ZodBoolean;
60
+ email: z.ZodOptional<z.ZodString>;
61
+ enabled: z.ZodBoolean;
62
+ }, z.core.$strip>;
63
+ /** {@link authStatusSchema} */
64
+ export type AuthStatus = z.infer<typeof authStatusSchema>;
@@ -0,0 +1,60 @@
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
+ /** `POST /api/email/auth/request` body: ask for a code to be emailed. */
20
+ export const authRequestSchema = z.object({
21
+ email: z.string().describe("Address to email a one-time code to, if it is allowed."),
22
+ });
23
+ /**
24
+ * `POST /api/email/auth/request` response. Always `{ ok: true }` on a
25
+ * well-formed request, regardless of whether the address was allowed or a code
26
+ * was actually sent - the client cannot distinguish an allowed address from a
27
+ * rejected one (anti-enumeration). `retryAfter` is set only when the caller is
28
+ * rate-limited, so a UI can show a cooldown without leaking allow-list state.
29
+ */
30
+ export const authRequestResultSchema = z.object({
31
+ ok: z.literal(true).describe("Always true for a well-formed request (anti-enumeration)."),
32
+ retryAfter: z
33
+ .number()
34
+ .optional()
35
+ .describe("Seconds to wait before requesting again, when rate-limited."),
36
+ });
37
+ /** `POST /api/email/auth/verify` body: submit the emailed code. */
38
+ export const authVerifySchema = z.object({
39
+ email: z.string().describe("The address the code was requested for."),
40
+ code: z.string().describe("The one-time code from the email."),
41
+ });
42
+ /**
43
+ * `POST /api/email/auth/verify` response. `ok` true means the code matched and
44
+ * an HttpOnly session cookie was set; false means it did not (wrong, expired, or
45
+ * too many attempts) - the message is deliberately generic. `retryAfter` is set
46
+ * when the failure was a rate limit.
47
+ */
48
+ export const authVerifyResultSchema = z.object({
49
+ ok: z.boolean().describe("True when the code matched and a session was established."),
50
+ retryAfter: z.number().optional().describe("Seconds to wait before retrying, when rate-limited."),
51
+ });
52
+ /** `GET /api/email/auth/status` response: is the caller authenticated, and as whom. */
53
+ export const authStatusSchema = z.object({
54
+ authenticated: z.boolean().describe("True when the request carries a valid session."),
55
+ email: z.string().optional().describe("The authenticated address, when authenticated."),
56
+ enabled: z
57
+ .boolean()
58
+ .describe("True when the gate is active; false means the app is open (no login needed)."),
59
+ });
60
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hdXRoLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7O0dBZ0JHO0FBRUgsT0FBTyxFQUFFLENBQUMsRUFBRSxNQUFNLEtBQUssQ0FBQztBQUV4Qix5RUFBeUU7QUFDekUsTUFBTSxDQUFDLE1BQU0saUJBQWlCLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQztJQUN4QyxLQUFLLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyx3REFBd0QsQ0FBQztDQUNyRixDQUFDLENBQUM7QUFLSDs7Ozs7O0dBTUc7QUFDSCxNQUFNLENBQUMsTUFBTSx1QkFBdUIsR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDO0lBQzlDLEVBQUUsRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLFFBQVEsQ0FBQywyREFBMkQsQ0FBQztJQUN6RixVQUFVLEVBQUUsQ0FBQztTQUNWLE1BQU0sRUFBRTtTQUNSLFFBQVEsRUFBRTtTQUNWLFFBQVEsQ0FBQyw2REFBNkQsQ0FBQztDQUMzRSxDQUFDLENBQUM7QUFLSCxtRUFBbUU7QUFDbkUsTUFBTSxDQUFDLE1BQU0sZ0JBQWdCLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQztJQUN2QyxLQUFLLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyx5Q0FBeUMsQ0FBQztJQUNyRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyxtQ0FBbUMsQ0FBQztDQUMvRCxDQUFDLENBQUM7QUFLSDs7Ozs7R0FLRztBQUNILE1BQU0sQ0FBQyxNQUFNLHNCQUFzQixHQUFHLENBQUMsQ0FBQyxNQUFNLENBQUM7SUFDN0MsRUFBRSxFQUFFLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxRQUFRLENBQUMsMkRBQTJELENBQUM7SUFDckYsVUFBVSxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLENBQUMscURBQXFELENBQUM7Q0FDbEcsQ0FBQyxDQUFDO0FBS0gsdUZBQXVGO0FBQ3ZGLE1BQU0sQ0FBQyxNQUFNLGdCQUFnQixHQUFHLENBQUMsQ0FBQyxNQUFNLENBQUM7SUFDdkMsYUFBYSxFQUFFLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxRQUFRLENBQUMsZ0RBQWdELENBQUM7SUFDckYsS0FBSyxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLENBQUMsZ0RBQWdELENBQUM7SUFDdkYsT0FBTyxFQUFFLENBQUM7U0FDUCxPQUFPLEVBQUU7U0FDVCxRQUFRLENBQUMsOEVBQThFLENBQUM7Q0FDNUYsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBXaXJlLWZvcm1hdCBjb250cmFjdCBmb3IgdGhlIGVtYWlsIG9uZS10aW1lLXBhc3N3b3JkIEFDQ0VTUyBHQVRFLlxuICpcbiAqIEEgY29tcGFuaW9uIHRvIHRoZSBlbWFpbCBhZGQtb24ncyBzZW5kIGNvbnRyYWN0ICh7QGxpbmsgLi9lbWFpbH0pOiB3aGVyZSB0aGF0XG4gKiBsZXRzIGFuIGFnZW50IHNlbmQgbWFpbCwgdGhpcyBsZXRzIGFuIEFwcEtpdCBhcHAgcHV0IGFuIGVtYWlsLU9UUCBnYXRlIGluIGZyb250XG4gKiBvZiBpdHNlbGYgLSB0aGUgbG9naW4gZmxvdyBmb3IgYW4gYXBwIGV4cG9zZWQgcHVibGljbHkgKGUuZy4gdGhyb3VnaCBhIHBvcnRyXG4gKiB0dW5uZWwgdGhhdCBieXBhc3NlcyB0aGUgRGF0YWJyaWNrcyBmcm9udCBkb29yKS4gUHVyZSB6b2Qgc28gdGhlIHNlcnZlciByb3V0ZXMsXG4gKiB0aGUgUmVhY3QgYEF1dGhHYXRlYCwgYW5kIGFueSBjbGllbnQgdmFsaWRhdGUgYWdhaW5zdCBvbmUgZGVmaW5pdGlvbi5cbiAqXG4gKiBGbG93OiBgUE9TVCAvcmVxdWVzdGAgKGVtYWlsKSAtPiBhIDYtZGlnaXQgY29kZSBpcyBlbWFpbGVkIC0+IGBQT1NUIC92ZXJpZnlgXG4gKiAoZW1haWwgKyBjb2RlKSAtPiBhbiBIdHRwT25seSBzZXNzaW9uIGNvb2tpZSBpcyBzZXQgLT4gYEdFVCAvc3RhdHVzYCByZXBvcnRzXG4gKiB3aGV0aGVyIHRoZSBjYWxsZXIgaXMgYXV0aGVudGljYXRlZC4gYHJlcXVlc3RgIEFMV0FZUyByZXBvcnRzIHN1Y2Nlc3MgKHRoZVxuICogc2VydmVyIG5ldmVyIHJldmVhbHMgd2hldGhlciBhbiBhZGRyZXNzIGlzIGFsbG93LWxpc3RlZCBvciB3YXMgYWN0dWFsbHkgc2VudCBhXG4gKiBjb2RlIC0gYW50aS1lbnVtZXJhdGlvbikuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IHogfSBmcm9tIFwiem9kXCI7XG5cbi8qKiBgUE9TVCAvYXBpL2VtYWlsL2F1dGgvcmVxdWVzdGAgYm9keTogYXNrIGZvciBhIGNvZGUgdG8gYmUgZW1haWxlZC4gKi9cbmV4cG9ydCBjb25zdCBhdXRoUmVxdWVzdFNjaGVtYSA9IHoub2JqZWN0KHtcbiAgZW1haWw6IHouc3RyaW5nKCkuZGVzY3JpYmUoXCJBZGRyZXNzIHRvIGVtYWlsIGEgb25lLXRpbWUgY29kZSB0bywgaWYgaXQgaXMgYWxsb3dlZC5cIiksXG59KTtcblxuLyoqIHtAbGluayBhdXRoUmVxdWVzdFNjaGVtYX0gKi9cbmV4cG9ydCB0eXBlIEF1dGhSZXF1ZXN0ID0gei5pbmZlcjx0eXBlb2YgYXV0aFJlcXVlc3RTY2hlbWE+O1xuXG4vKipcbiAqIGBQT1NUIC9hcGkvZW1haWwvYXV0aC9yZXF1ZXN0YCByZXNwb25zZS4gQWx3YXlzIGB7IG9rOiB0cnVlIH1gIG9uIGFcbiAqIHdlbGwtZm9ybWVkIHJlcXVlc3QsIHJlZ2FyZGxlc3Mgb2Ygd2hldGhlciB0aGUgYWRkcmVzcyB3YXMgYWxsb3dlZCBvciBhIGNvZGVcbiAqIHdhcyBhY3R1YWxseSBzZW50IC0gdGhlIGNsaWVudCBjYW5ub3QgZGlzdGluZ3Vpc2ggYW4gYWxsb3dlZCBhZGRyZXNzIGZyb20gYVxuICogcmVqZWN0ZWQgb25lIChhbnRpLWVudW1lcmF0aW9uKS4gYHJldHJ5QWZ0ZXJgIGlzIHNldCBvbmx5IHdoZW4gdGhlIGNhbGxlciBpc1xuICogcmF0ZS1saW1pdGVkLCBzbyBhIFVJIGNhbiBzaG93IGEgY29vbGRvd24gd2l0aG91dCBsZWFraW5nIGFsbG93LWxpc3Qgc3RhdGUuXG4gKi9cbmV4cG9ydCBjb25zdCBhdXRoUmVxdWVzdFJlc3VsdFNjaGVtYSA9IHoub2JqZWN0KHtcbiAgb2s6IHoubGl0ZXJhbCh0cnVlKS5kZXNjcmliZShcIkFsd2F5cyB0cnVlIGZvciBhIHdlbGwtZm9ybWVkIHJlcXVlc3QgKGFudGktZW51bWVyYXRpb24pLlwiKSxcbiAgcmV0cnlBZnRlcjogelxuICAgIC5udW1iZXIoKVxuICAgIC5vcHRpb25hbCgpXG4gICAgLmRlc2NyaWJlKFwiU2Vjb25kcyB0byB3YWl0IGJlZm9yZSByZXF1ZXN0aW5nIGFnYWluLCB3aGVuIHJhdGUtbGltaXRlZC5cIiksXG59KTtcblxuLyoqIHtAbGluayBhdXRoUmVxdWVzdFJlc3VsdFNjaGVtYX0gKi9cbmV4cG9ydCB0eXBlIEF1dGhSZXF1ZXN0UmVzdWx0ID0gei5pbmZlcjx0eXBlb2YgYXV0aFJlcXVlc3RSZXN1bHRTY2hlbWE+O1xuXG4vKiogYFBPU1QgL2FwaS9lbWFpbC9hdXRoL3ZlcmlmeWAgYm9keTogc3VibWl0IHRoZSBlbWFpbGVkIGNvZGUuICovXG5leHBvcnQgY29uc3QgYXV0aFZlcmlmeVNjaGVtYSA9IHoub2JqZWN0KHtcbiAgZW1haWw6IHouc3RyaW5nKCkuZGVzY3JpYmUoXCJUaGUgYWRkcmVzcyB0aGUgY29kZSB3YXMgcmVxdWVzdGVkIGZvci5cIiksXG4gIGNvZGU6IHouc3RyaW5nKCkuZGVzY3JpYmUoXCJUaGUgb25lLXRpbWUgY29kZSBmcm9tIHRoZSBlbWFpbC5cIiksXG59KTtcblxuLyoqIHtAbGluayBhdXRoVmVyaWZ5U2NoZW1hfSAqL1xuZXhwb3J0IHR5cGUgQXV0aFZlcmlmeSA9IHouaW5mZXI8dHlwZW9mIGF1dGhWZXJpZnlTY2hlbWE+O1xuXG4vKipcbiAqIGBQT1NUIC9hcGkvZW1haWwvYXV0aC92ZXJpZnlgIHJlc3BvbnNlLiBgb2tgIHRydWUgbWVhbnMgdGhlIGNvZGUgbWF0Y2hlZCBhbmRcbiAqIGFuIEh0dHBPbmx5IHNlc3Npb24gY29va2llIHdhcyBzZXQ7IGZhbHNlIG1lYW5zIGl0IGRpZCBub3QgKHdyb25nLCBleHBpcmVkLCBvclxuICogdG9vIG1hbnkgYXR0ZW1wdHMpIC0gdGhlIG1lc3NhZ2UgaXMgZGVsaWJlcmF0ZWx5IGdlbmVyaWMuIGByZXRyeUFmdGVyYCBpcyBzZXRcbiAqIHdoZW4gdGhlIGZhaWx1cmUgd2FzIGEgcmF0ZSBsaW1pdC5cbiAqL1xuZXhwb3J0IGNvbnN0IGF1dGhWZXJpZnlSZXN1bHRTY2hlbWEgPSB6Lm9iamVjdCh7XG4gIG9rOiB6LmJvb2xlYW4oKS5kZXNjcmliZShcIlRydWUgd2hlbiB0aGUgY29kZSBtYXRjaGVkIGFuZCBhIHNlc3Npb24gd2FzIGVzdGFibGlzaGVkLlwiKSxcbiAgcmV0cnlBZnRlcjogei5udW1iZXIoKS5vcHRpb25hbCgpLmRlc2NyaWJlKFwiU2Vjb25kcyB0byB3YWl0IGJlZm9yZSByZXRyeWluZywgd2hlbiByYXRlLWxpbWl0ZWQuXCIpLFxufSk7XG5cbi8qKiB7QGxpbmsgYXV0aFZlcmlmeVJlc3VsdFNjaGVtYX0gKi9cbmV4cG9ydCB0eXBlIEF1dGhWZXJpZnlSZXN1bHQgPSB6LmluZmVyPHR5cGVvZiBhdXRoVmVyaWZ5UmVzdWx0U2NoZW1hPjtcblxuLyoqIGBHRVQgL2FwaS9lbWFpbC9hdXRoL3N0YXR1c2AgcmVzcG9uc2U6IGlzIHRoZSBjYWxsZXIgYXV0aGVudGljYXRlZCwgYW5kIGFzIHdob20uICovXG5leHBvcnQgY29uc3QgYXV0aFN0YXR1c1NjaGVtYSA9IHoub2JqZWN0KHtcbiAgYXV0aGVudGljYXRlZDogei5ib29sZWFuKCkuZGVzY3JpYmUoXCJUcnVlIHdoZW4gdGhlIHJlcXVlc3QgY2FycmllcyBhIHZhbGlkIHNlc3Npb24uXCIpLFxuICBlbWFpbDogei5zdHJpbmcoKS5vcHRpb25hbCgpLmRlc2NyaWJlKFwiVGhlIGF1dGhlbnRpY2F0ZWQgYWRkcmVzcywgd2hlbiBhdXRoZW50aWNhdGVkLlwiKSxcbiAgZW5hYmxlZDogelxuICAgIC5ib29sZWFuKClcbiAgICAuZGVzY3JpYmUoXCJUcnVlIHdoZW4gdGhlIGdhdGUgaXMgYWN0aXZlOyBmYWxzZSBtZWFucyB0aGUgYXBwIGlzIG9wZW4gKG5vIGxvZ2luIG5lZWRlZCkuXCIpLFxufSk7XG5cbi8qKiB7QGxpbmsgYXV0aFN0YXR1c1NjaGVtYX0gKi9cbmV4cG9ydCB0eXBlIEF1dGhTdGF0dXMgPSB6LmluZmVyPHR5cGVvZiBhdXRoU3RhdHVzU2NoZW1hPjtcbiJdfQ==
@@ -1 +1 @@
1
- {"root":["../src/email.ts","../index.ts"],"version":"5.9.3"}
1
+ {"root":["../src/auth.ts","../src/email.ts","../index.ts"],"version":"5.9.3"}
package/package.json CHANGED
@@ -5,28 +5,46 @@
5
5
  "url": "git+https://github.com/reggie-db/dbx-tools.git",
6
6
  "directory": "packages/shared/email"
7
7
  },
8
+ "scripts": {
9
+ "build": "projen build",
10
+ "compile": "projen compile",
11
+ "default": "projen default",
12
+ "package": "projen package",
13
+ "post-compile": "projen post-compile",
14
+ "pre-compile": "projen pre-compile",
15
+ "prepack": "projen prepack",
16
+ "test": "projen test",
17
+ "watch": "projen watch",
18
+ "projen": "projen"
19
+ },
8
20
  "devDependencies": {
21
+ "@types/bun": "^1.3.14",
9
22
  "@types/node": "^24.6.0",
10
- "tsx": "^4.23.0",
11
23
  "typescript": "^5.9.3"
12
24
  },
13
25
  "dependencies": {
14
- "zod": "4.3.6",
15
- "@dbx-tools/shared-core": "0.6.40"
26
+ "@dbx-tools/shared-core": "0.6.42",
27
+ "zod": "4.3.6"
16
28
  },
17
- "main": "./lib/index.js",
29
+ "main": "index.ts",
18
30
  "license": "UNLICENSED",
19
31
  "publishConfig": {
20
- "access": "public"
32
+ "access": "public",
33
+ "main": "./lib/index.js",
34
+ "types": "./lib/index.d.ts",
35
+ "exports": {
36
+ ".": {
37
+ "types": "./lib/index.d.ts",
38
+ "default": "./lib/index.js"
39
+ },
40
+ "./package.json": "./package.json"
41
+ }
21
42
  },
22
- "version": "0.6.40",
23
- "types": "./lib/index.d.ts",
43
+ "version": "0.6.42",
44
+ "types": "index.ts",
24
45
  "type": "module",
25
46
  "exports": {
26
- ".": {
27
- "types": "./lib/index.d.ts",
28
- "default": "./lib/index.js"
29
- },
47
+ ".": "./index.ts",
30
48
  "./package.json": "./package.json"
31
49
  },
32
50
  "files": [
@@ -39,16 +57,5 @@
39
57
  "shared"
40
58
  ]
41
59
  },
42
- "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"pnpm exec projen\".",
43
- "scripts": {
44
- "build": "projen build",
45
- "compile": "projen compile",
46
- "default": "projen default",
47
- "package": "projen package",
48
- "post-compile": "projen post-compile",
49
- "pre-compile": "projen pre-compile",
50
- "test": "projen test",
51
- "watch": "projen watch",
52
- "projen": "projen"
53
- }
54
- }
60
+ "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"bunx projen\"."
61
+ }
package/src/auth.ts ADDED
@@ -0,0 +1,80 @@
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
+ /** `POST /api/email/auth/request` body: ask for a code to be emailed. */
22
+ export const authRequestSchema = z.object({
23
+ email: z.string().describe("Address to email a one-time code to, if it is allowed."),
24
+ });
25
+
26
+ /** {@link authRequestSchema} */
27
+ export type AuthRequest = z.infer<typeof authRequestSchema>;
28
+
29
+ /**
30
+ * `POST /api/email/auth/request` response. Always `{ ok: true }` on a
31
+ * well-formed request, regardless of whether the address was allowed or a code
32
+ * was actually sent - the client cannot distinguish an allowed address from a
33
+ * rejected one (anti-enumeration). `retryAfter` is set only when the caller is
34
+ * rate-limited, so a UI can show a cooldown without leaking allow-list state.
35
+ */
36
+ export const authRequestResultSchema = z.object({
37
+ ok: z.literal(true).describe("Always true for a well-formed request (anti-enumeration)."),
38
+ retryAfter: z
39
+ .number()
40
+ .optional()
41
+ .describe("Seconds to wait before requesting again, when rate-limited."),
42
+ });
43
+
44
+ /** {@link authRequestResultSchema} */
45
+ export type AuthRequestResult = z.infer<typeof authRequestResultSchema>;
46
+
47
+ /** `POST /api/email/auth/verify` body: submit the emailed code. */
48
+ export const authVerifySchema = z.object({
49
+ email: z.string().describe("The address the code was requested for."),
50
+ code: z.string().describe("The one-time code from the email."),
51
+ });
52
+
53
+ /** {@link authVerifySchema} */
54
+ export type AuthVerify = z.infer<typeof authVerifySchema>;
55
+
56
+ /**
57
+ * `POST /api/email/auth/verify` response. `ok` true means the code matched and
58
+ * an HttpOnly session cookie was set; false means it did not (wrong, expired, or
59
+ * too many attempts) - the message is deliberately generic. `retryAfter` is set
60
+ * when the failure was a rate limit.
61
+ */
62
+ export const authVerifyResultSchema = z.object({
63
+ ok: z.boolean().describe("True when the code matched and a session was established."),
64
+ retryAfter: z.number().optional().describe("Seconds to wait before retrying, when rate-limited."),
65
+ });
66
+
67
+ /** {@link authVerifyResultSchema} */
68
+ export type AuthVerifyResult = z.infer<typeof authVerifyResultSchema>;
69
+
70
+ /** `GET /api/email/auth/status` response: is the caller authenticated, and as whom. */
71
+ export const authStatusSchema = z.object({
72
+ authenticated: z.boolean().describe("True when the request carries a valid session."),
73
+ email: z.string().optional().describe("The authenticated address, when authenticated."),
74
+ enabled: z
75
+ .boolean()
76
+ .describe("True when the gate is active; false means the app is open (no login needed)."),
77
+ });
78
+
79
+ /** {@link authStatusSchema} */
80
+ export type AuthStatus = z.infer<typeof authStatusSchema>;