@lunora/auth 1.0.0-alpha.106 → 1.0.0-alpha.108

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.
@@ -33,13 +33,6 @@ interface EmailGateConfig {
33
33
  * top of the built-in list. Wildcard/subdomain aware.
34
34
  */
35
35
  denyDomains?: ReadonlyArray<string>;
36
- /**
37
- * Reserved for callers that branch on free-vs-business (e.g. gate a feature
38
- * behind a business email). Purely advisory — {@link EmailClassification}'s
39
- * `emailClass` already reports `free`, so this flag exists for symmetry/intent
40
- * and never blocks. Defaults to `false`.
41
- */
42
- flagFreeEmail?: boolean;
43
36
  /**
44
37
  * Opt-in MX deliverability verification. Off by default because it needs DNS
45
38
  * (`@visulima/email-verifier/checks/mx` → `node:dns`), which is not available
@@ -60,10 +53,18 @@ interface EmailGateConfig {
60
53
  /** Options for {@link emailGateMiddleware}: the base gate config plus how to read the email from `ctx`. */
61
54
  interface EmailGateMiddlewareOptions<Context> extends EmailGateConfig {
62
55
  /**
63
- * Selector that pulls the signup email from `ctx`. The procedure context
64
- * carries only the resolved identity, not the raw request body, so route the
65
- * email through the function `args` and read it out here (mirrors
66
- * `verifyTurnstileMiddleware`'s `token` selector).
56
+ * Selector that pulls the signup email off `ctx`. The procedure context
57
+ * carries only the resolved identity, not the raw request body, so the email
58
+ * travels in the function `args` — which the builder surfaces to middleware
59
+ * as `ctx.args` (validated, frozen). Mirrors `verifyTurnstileMiddleware`'s
60
+ * `token` selector:
61
+ *
62
+ * ```ts
63
+ * export const signUp = mutation
64
+ * .input({ email: v.string() })
65
+ * .use(emailGateMiddleware({ email: (ctx) => ctx.args.email }))
66
+ * .mutation(async ({ args, ctx }) => { … });
67
+ * ```
67
68
  */
68
69
  email: (context: Context) => string | undefined;
69
70
  /**
@@ -111,7 +112,8 @@ declare const assertEmailAllowed: (email: string, config?: EmailGateConfig) => P
111
112
  /**
112
113
  * Lunora procedure middleware that gates a non-auth, signup-shaped
113
114
  * `mutation`/`action` on the email-domain policy. Attach it with `.use()`; it
114
- * reads the email from `ctx` via the `email` selector (route it through `args`)
115
+ * reads the email from `ctx` via the `email` selector (declare it with
116
+ * `.input(...)` and read it back as `ctx.args.email`)
115
117
  * and runs {@link assertEmailAllowed}, which throws a coded {@link LunoraError}
116
118
  * (`EMAIL_DOMAIN_BLOCKED` / `EMAIL_UNDELIVERABLE` / `VALIDATION_ERROR`) the
117
119
  * runtime maps to the matching status.
@@ -33,13 +33,6 @@ interface EmailGateConfig {
33
33
  * top of the built-in list. Wildcard/subdomain aware.
34
34
  */
35
35
  denyDomains?: ReadonlyArray<string>;
36
- /**
37
- * Reserved for callers that branch on free-vs-business (e.g. gate a feature
38
- * behind a business email). Purely advisory — {@link EmailClassification}'s
39
- * `emailClass` already reports `free`, so this flag exists for symmetry/intent
40
- * and never blocks. Defaults to `false`.
41
- */
42
- flagFreeEmail?: boolean;
43
36
  /**
44
37
  * Opt-in MX deliverability verification. Off by default because it needs DNS
45
38
  * (`@visulima/email-verifier/checks/mx` → `node:dns`), which is not available
@@ -60,10 +53,18 @@ interface EmailGateConfig {
60
53
  /** Options for {@link emailGateMiddleware}: the base gate config plus how to read the email from `ctx`. */
61
54
  interface EmailGateMiddlewareOptions<Context> extends EmailGateConfig {
62
55
  /**
63
- * Selector that pulls the signup email from `ctx`. The procedure context
64
- * carries only the resolved identity, not the raw request body, so route the
65
- * email through the function `args` and read it out here (mirrors
66
- * `verifyTurnstileMiddleware`'s `token` selector).
56
+ * Selector that pulls the signup email off `ctx`. The procedure context
57
+ * carries only the resolved identity, not the raw request body, so the email
58
+ * travels in the function `args` — which the builder surfaces to middleware
59
+ * as `ctx.args` (validated, frozen). Mirrors `verifyTurnstileMiddleware`'s
60
+ * `token` selector:
61
+ *
62
+ * ```ts
63
+ * export const signUp = mutation
64
+ * .input({ email: v.string() })
65
+ * .use(emailGateMiddleware({ email: (ctx) => ctx.args.email }))
66
+ * .mutation(async ({ args, ctx }) => { … });
67
+ * ```
67
68
  */
68
69
  email: (context: Context) => string | undefined;
69
70
  /**
@@ -111,7 +112,8 @@ declare const assertEmailAllowed: (email: string, config?: EmailGateConfig) => P
111
112
  /**
112
113
  * Lunora procedure middleware that gates a non-auth, signup-shaped
113
114
  * `mutation`/`action` on the email-domain policy. Attach it with `.use()`; it
114
- * reads the email from `ctx` via the `email` selector (route it through `args`)
115
+ * reads the email from `ctx` via the `email` selector (declare it with
116
+ * `.input(...)` and read it back as `ctx.args.email`)
115
117
  * and runs {@link assertEmailAllowed}, which throws a coded {@link LunoraError}
116
118
  * (`EMAIL_DOMAIN_BLOCKED` / `EMAIL_UNDELIVERABLE` / `VALIDATION_ERROR`) the
117
119
  * runtime maps to the matching status.
@@ -148,23 +148,33 @@ interface LunoraAuthApiContext<Auth extends LunoraAuth> {
148
148
  * Lunora's procedure context does not currently carry the raw request headers
149
149
  * (only the resolved identity — see `AuthState` in `@lunora/server`), so
150
150
  * this middleware **cannot** pre-bind headers for you and does **not** do so.
151
- * You MUST pass the inbound `Headers` explicitly into **every** `ctx.authApi.*`
152
- * call, from a transport that has them — typically an HTTP action:
151
+ * You MUST pass `Headers` explicitly into **every** `ctx.authApi.*` call. A
152
+ * procedure has no inbound `Headers` object, so rebuild one from what the
153
+ * transport forwarded in `args`:
153
154
  *
154
155
  * ```ts
155
156
  * // lunora/orgs.ts
156
- * import { httpAction } from "@lunora/server";
157
- * import { withAuthPlugins } from "@lunora/auth/middleware";
158
- * import { auth } from "./auth.js";
157
+ * export const createOrg = mutation
158
+ * .input({ cookie: v.string(), name: v.string() })
159
+ * .use(withAuthPlugins(auth))
160
+ * .mutation(async ({ args, ctx }) =>
161
+ * ctx.authApi.createOrganization({
162
+ * body: { name: args.name },
163
+ * // Rebuilt from the transport — the middleware cannot pre-bind them.
164
+ * headers: new Headers({ cookie: args.cookie }),
165
+ * }),
166
+ * );
167
+ * ```
159
168
  *
160
- * export const createOrg = httpAction(async (ctx, request) => {
161
- * const { name } = await request.json();
169
+ * `ctx.authApi` is a PROCEDURE-context surface — this middleware installs it
170
+ * through the builder's `.use()` chain, which `httpAction` / `httpRoute` do not
171
+ * have (see `HttpActionCtx` in `@lunora/server`). An HTTP action already holds
172
+ * the real inbound `Headers`, so call the auth instance there directly:
162
173
  *
163
- * // ctx.authApi is installed by withAuthPlugins(auth) on the builder.
164
- * const org = await ctx.authApi.createOrganization({
165
- * body: { name },
166
- * headers: request.headers,
167
- * });
174
+ * ```ts
175
+ * export const createOrgHttp = httpAction(async (ctx, request) => {
176
+ * const { name } = await request.json();
177
+ * const org = await auth.api.createOrganization({ body: { name }, headers: request.headers });
168
178
  *
169
179
  * return Response.json(org);
170
180
  * });
@@ -148,23 +148,33 @@ interface LunoraAuthApiContext<Auth extends LunoraAuth> {
148
148
  * Lunora's procedure context does not currently carry the raw request headers
149
149
  * (only the resolved identity — see `AuthState` in `@lunora/server`), so
150
150
  * this middleware **cannot** pre-bind headers for you and does **not** do so.
151
- * You MUST pass the inbound `Headers` explicitly into **every** `ctx.authApi.*`
152
- * call, from a transport that has them — typically an HTTP action:
151
+ * You MUST pass `Headers` explicitly into **every** `ctx.authApi.*` call. A
152
+ * procedure has no inbound `Headers` object, so rebuild one from what the
153
+ * transport forwarded in `args`:
153
154
  *
154
155
  * ```ts
155
156
  * // lunora/orgs.ts
156
- * import { httpAction } from "@lunora/server";
157
- * import { withAuthPlugins } from "@lunora/auth/middleware";
158
- * import { auth } from "./auth.js";
157
+ * export const createOrg = mutation
158
+ * .input({ cookie: v.string(), name: v.string() })
159
+ * .use(withAuthPlugins(auth))
160
+ * .mutation(async ({ args, ctx }) =>
161
+ * ctx.authApi.createOrganization({
162
+ * body: { name: args.name },
163
+ * // Rebuilt from the transport — the middleware cannot pre-bind them.
164
+ * headers: new Headers({ cookie: args.cookie }),
165
+ * }),
166
+ * );
167
+ * ```
159
168
  *
160
- * export const createOrg = httpAction(async (ctx, request) => {
161
- * const { name } = await request.json();
169
+ * `ctx.authApi` is a PROCEDURE-context surface — this middleware installs it
170
+ * through the builder's `.use()` chain, which `httpAction` / `httpRoute` do not
171
+ * have (see `HttpActionCtx` in `@lunora/server`). An HTTP action already holds
172
+ * the real inbound `Headers`, so call the auth instance there directly:
162
173
  *
163
- * // ctx.authApi is installed by withAuthPlugins(auth) on the builder.
164
- * const org = await ctx.authApi.createOrganization({
165
- * body: { name },
166
- * headers: request.headers,
167
- * });
174
+ * ```ts
175
+ * export const createOrgHttp = httpAction(async (ctx, request) => {
176
+ * const { name } = await request.json();
177
+ * const org = await auth.api.createOrganization({ body: { name }, headers: request.headers });
168
178
  *
169
179
  * return Response.json(org);
170
180
  * });
@@ -31,17 +31,26 @@ interface VerifyTurnstileMiddlewareOptions<Context> {
31
31
  /** Override the error message thrown on a failed verdict. */
32
32
  message?: string;
33
33
  /**
34
- * Selector that pulls the visitor IP from `ctx` (the procedure context has
35
- * no raw `Headers`, so this must come from `args`/ctx). Optional.
34
+ * Selector that pulls the visitor IP off `ctx` — typically `ctx.ip`, or
35
+ * `ctx.args.ip` when the caller sends it (the procedure context has no raw
36
+ * `Headers`). Optional.
36
37
  */
37
38
  remoteip?: (context: Context) => string | undefined;
38
39
  /** Your Turnstile secret key (the `TURNSTILE_SECRET_KEY` env var). */
39
40
  secret: string;
40
41
  /**
41
- * Selector that pulls the `cf-turnstile-response` token from `ctx`. The
42
+ * Selector that pulls the `cf-turnstile-response` token off `ctx`. The
42
43
  * procedure context carries only the resolved identity, **not** the raw
43
44
  * inbound `Headers` (see `withAuthPlugins` in `./middleware`), so the token
44
- * must travel in the function `args` and be read out here.
45
+ * travels in the function `args` — which the builder surfaces to middleware
46
+ * as `ctx.args` (validated, frozen):
47
+ *
48
+ * ```ts
49
+ * export const submit = mutation
50
+ * .input({ message: v.string(), turnstileToken: v.string() })
51
+ * .use(verifyTurnstileMiddleware({ secret: env.TURNSTILE_SECRET_KEY, token: (ctx) => ctx.args.turnstileToken }))
52
+ * .mutation(async ({ args, ctx }) => { … });
53
+ * ```
45
54
  */
46
55
  token: (context: Context) => string | undefined;
47
56
  /**
@@ -56,8 +65,10 @@ interface VerifyTurnstileMiddlewareOptions<Context> {
56
65
  * Procedure middleware that enforces a Turnstile (CAPTCHA) check before the
57
66
  * handler runs. Attach it with `.use()`. It reads the token (and optional IP)
58
67
  * from `ctx` via the provided selectors — because the procedure context does
59
- * not carry raw request headers, the token must be passed through the function
60
- * `args`. To gate the better-auth sign-in/sign-up flow itself, prefer
68
+ * not carry raw request headers, the token is passed through the function
69
+ * `args` and read back as `ctx.args.<field>` (the builder surfaces the
70
+ * VALIDATED args on the middleware context, frozen). To gate the better-auth
71
+ * sign-in/sign-up flow itself, prefer
61
72
  * better-auth's native `captcha` plugin (re-exported from `@lunora/auth/plugins`)
62
73
  * — this middleware is for non-auth Lunora procedures.
63
74
  *
@@ -31,17 +31,26 @@ interface VerifyTurnstileMiddlewareOptions<Context> {
31
31
  /** Override the error message thrown on a failed verdict. */
32
32
  message?: string;
33
33
  /**
34
- * Selector that pulls the visitor IP from `ctx` (the procedure context has
35
- * no raw `Headers`, so this must come from `args`/ctx). Optional.
34
+ * Selector that pulls the visitor IP off `ctx` — typically `ctx.ip`, or
35
+ * `ctx.args.ip` when the caller sends it (the procedure context has no raw
36
+ * `Headers`). Optional.
36
37
  */
37
38
  remoteip?: (context: Context) => string | undefined;
38
39
  /** Your Turnstile secret key (the `TURNSTILE_SECRET_KEY` env var). */
39
40
  secret: string;
40
41
  /**
41
- * Selector that pulls the `cf-turnstile-response` token from `ctx`. The
42
+ * Selector that pulls the `cf-turnstile-response` token off `ctx`. The
42
43
  * procedure context carries only the resolved identity, **not** the raw
43
44
  * inbound `Headers` (see `withAuthPlugins` in `./middleware`), so the token
44
- * must travel in the function `args` and be read out here.
45
+ * travels in the function `args` — which the builder surfaces to middleware
46
+ * as `ctx.args` (validated, frozen):
47
+ *
48
+ * ```ts
49
+ * export const submit = mutation
50
+ * .input({ message: v.string(), turnstileToken: v.string() })
51
+ * .use(verifyTurnstileMiddleware({ secret: env.TURNSTILE_SECRET_KEY, token: (ctx) => ctx.args.turnstileToken }))
52
+ * .mutation(async ({ args, ctx }) => { … });
53
+ * ```
45
54
  */
46
55
  token: (context: Context) => string | undefined;
47
56
  /**
@@ -56,8 +65,10 @@ interface VerifyTurnstileMiddlewareOptions<Context> {
56
65
  * Procedure middleware that enforces a Turnstile (CAPTCHA) check before the
57
66
  * handler runs. Attach it with `.use()`. It reads the token (and optional IP)
58
67
  * from `ctx` via the provided selectors — because the procedure context does
59
- * not carry raw request headers, the token must be passed through the function
60
- * `args`. To gate the better-auth sign-in/sign-up flow itself, prefer
68
+ * not carry raw request headers, the token is passed through the function
69
+ * `args` and read back as `ctx.args.<field>` (the builder surfaces the
70
+ * VALIDATED args on the middleware context, frozen). To gate the better-auth
71
+ * sign-in/sign-up flow itself, prefer
61
72
  * better-auth's native `captcha` plugin (re-exported from `@lunora/auth/plugins`)
62
73
  * — this middleware is for non-auth Lunora procedures.
63
74
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/auth",
3
- "version": "1.0.0-alpha.106",
3
+ "version": "1.0.0-alpha.108",
4
4
  "description": "Auth for Lunora — a thin better-auth wrapper: email/password, OAuth, plugins, D1-backed",
5
5
  "keywords": [
6
6
  "auth",
@@ -103,8 +103,8 @@
103
103
  "@better-auth/oauth-provider": "1.7.1",
104
104
  "@better-auth/passkey": "1.7.1",
105
105
  "@better-auth/scim": "1.7.1",
106
- "@lunora/errors": "1.0.0-alpha.26",
107
- "@lunora/values": "1.0.0-alpha.34",
106
+ "@lunora/errors": "1.0.0-alpha.27",
107
+ "@lunora/values": "1.0.0-alpha.35",
108
108
  "@visulima/disposable-email-domains": "1.1.0",
109
109
  "@visulima/email-verifier": "1.0.2",
110
110
  "@visulima/free-email-domains": "1.0.1",