better-auth-evp 1.0.1 → 1.0.2
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/CHANGELOG.md +7 -0
- package/README.md +4 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -2
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [1.0.2](https://github.com/qamarq/better-auth-evp/compare/v1.0.1...v1.0.2) (2026-08-31)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* restrict verification surface and revoke stale sessions on promotion ([61bc289](https://github.com/qamarq/better-auth-evp/commit/61bc289f1c265be5f7301e1d13a8c526ae628dd1))
|
|
7
|
+
|
|
1
8
|
## [1.0.1](https://github.com/qamarq/better-auth-evp/compare/v1.0.0...v1.0.1) (2026-08-31)
|
|
2
9
|
|
|
3
10
|
|
package/README.md
CHANGED
|
@@ -38,6 +38,7 @@ export const auth = betterAuth({
|
|
|
38
38
|
// Must match the origin your Chrome origin-trial token, DNS
|
|
39
39
|
// `_email-verification` record, etc. were issued for.
|
|
40
40
|
origin: "https://example.com",
|
|
41
|
+
allowedEmailDomains: ["example.com"],
|
|
41
42
|
disableSignUp: false,
|
|
42
43
|
userFields: (verified) => ({
|
|
43
44
|
// any additional fields for a newly created user
|
|
@@ -116,8 +117,8 @@ async function handleEmailSubmit(email: string, form: HTMLFormElement) {
|
|
|
116
117
|
|
|
117
118
|
### Server (`auth.api`)
|
|
118
119
|
|
|
119
|
-
- `
|
|
120
|
-
- `evpVerify({ email, token, nonce })` - `POST /evp/verify` - verifies the token and, on success, creates a session (and a user, unless `disableSignUp` is set). Returns `{ verified: false, reason }` instead of throwing on any expected failure (invalid/expired nonce, verification failure, email mismatch, sign-up disabled).
|
|
120
|
+
- `evpGetNonce()` - `GET /evp/get-nonce` - issues a single-use nonce, valid for `nonceExpiresIn` seconds (default 120).
|
|
121
|
+
- `evpVerify({ email, token, nonce })` - `POST /evp/verify` - verifies the token and, on success, creates a session (and a user, unless `disableSignUp` is set). Returns `{ verified: false, reason }` instead of throwing on any expected failure (invalid/expired nonce, disallowed email domain, verification failure, email mismatch, sign-up disabled).
|
|
121
122
|
|
|
122
123
|
### Client (`authClient.evp`)
|
|
123
124
|
|
|
@@ -130,6 +131,7 @@ async function handleEmailSubmit(email: string, form: HTMLFormElement) {
|
|
|
130
131
|
| ---------------- | ------------------------------------------------ | ---------- | --------------------------------------------------------- |
|
|
131
132
|
| `origin` | `string` | (required) | This relying party's absolute origin, used as `audience`. |
|
|
132
133
|
| `nonceExpiresIn` | `number` | `120` | Seconds a nonce stays valid. |
|
|
134
|
+
| `allowedEmailDomains` | `string[]` | optional, unrestricted if omitted | Restricts which email domains `/evp/verify` will even attempt to verify. The email field in your own form is client-side validation only and can be bypassed by calling the API directly - without this option, a caller can make the server perform a DNS lookup + issuer JWKS fetch against any domain they choose (SSRF/abuse surface). Strongly recommended whenever your app only expects a fixed set of domains. |
|
|
133
135
|
| `disableSignUp` | `boolean` | `false` | Reject verified emails with no existing account. |
|
|
134
136
|
| `userFields` | `(verified) => T` | - | Extra fields for a newly created user. |
|
|
135
137
|
| `onVerified` | `(verified & { userId }) => void \| Promise<void>` | - | Side-effect hook after a session is created. |
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export declare function emailVerificationProtocol<T extends Record<string, any>
|
|
|
17
17
|
token: z.ZodString;
|
|
18
18
|
nonce: z.ZodString;
|
|
19
19
|
}, z.core.$strip>;
|
|
20
|
+
use: import("better-auth").Middleware<import("better-auth").MiddlewareOptions, (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void>>[];
|
|
20
21
|
}, {
|
|
21
22
|
verified: boolean;
|
|
22
23
|
reason: string;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAEzB,OAAO,KAAK,EACV,gBAAgB,EAGjB,MAAM,SAAS,CAAC;AAEjB,cAAc,SAAS,CAAC;AAUxB,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE,EAC1E,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAoIJ,MAAM;;;;EAQ/B"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { APIError, createAuthEndpoint } from "better-auth/api";
|
|
1
|
+
import { APIError, createAuthEndpoint, formCsrfMiddleware, } from "better-auth/api";
|
|
2
2
|
import { setSessionCookie } from "better-auth/cookies";
|
|
3
3
|
import { generateRandomString } from "better-auth/crypto";
|
|
4
4
|
import { verifyEmailToken } from "email-verification-api";
|
|
@@ -28,12 +28,25 @@ export function emailVerificationProtocol(options) {
|
|
|
28
28
|
});
|
|
29
29
|
return ctx.json({ nonce, expiresIn: nonceExpiresIn });
|
|
30
30
|
}),
|
|
31
|
-
evpVerify: createAuthEndpoint("/evp/verify", {
|
|
31
|
+
evpVerify: createAuthEndpoint("/evp/verify", {
|
|
32
|
+
method: "POST",
|
|
33
|
+
body: evpVerifyBodySchema,
|
|
34
|
+
use: [formCsrfMiddleware],
|
|
35
|
+
}, async (ctx) => {
|
|
32
36
|
const email = ctx.body.email.trim().toLowerCase();
|
|
33
37
|
const { token, nonce } = ctx.body;
|
|
34
38
|
if (!z.email().safeParse(email).success) {
|
|
35
39
|
throw new APIError("BAD_REQUEST", { message: "Invalid email" });
|
|
36
40
|
}
|
|
41
|
+
if (options.allowedEmailDomains) {
|
|
42
|
+
const domain = email.split("@")[1];
|
|
43
|
+
if (!options.allowedEmailDomains.includes(domain)) {
|
|
44
|
+
return ctx.json({
|
|
45
|
+
verified: false,
|
|
46
|
+
reason: "domain_not_allowed",
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
37
50
|
const nonceRecord = await ctx.context.internalAdapter.consumeVerificationValue(`${NONCE_IDENTIFIER_PREFIX}${nonce}`);
|
|
38
51
|
if (!nonceRecord) {
|
|
39
52
|
return ctx.json({ verified: false, reason: "nonce_invalid" });
|
|
@@ -75,6 +88,7 @@ export function emailVerificationProtocol(options) {
|
|
|
75
88
|
else {
|
|
76
89
|
user = existing.user;
|
|
77
90
|
if (!user.emailVerified) {
|
|
91
|
+
await ctx.context.internalAdapter.deleteUserSessions(user.id);
|
|
78
92
|
user = await ctx.context.internalAdapter.updateUser(user.id, {
|
|
79
93
|
emailVerified: true,
|
|
80
94
|
});
|
package/dist/types.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export interface EvpPluginOptions<T extends Record<string, any> = {}> {
|
|
|
28
28
|
origin: string;
|
|
29
29
|
nonceExpiresIn?: number;
|
|
30
30
|
disableSignUp?: boolean;
|
|
31
|
+
allowedEmailDomains?: string[];
|
|
31
32
|
userFields?: (verified: EvpVerifiedEmail) => T;
|
|
32
33
|
onVerified?: (verified: EvpVerifiedEmail & {
|
|
33
34
|
userId: string;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAA;CAAE,GACrC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,oBAAoB,CAAA;CAAE,CAAC;AAE/C,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,CAAC,CAAC;IAC/C,UAAU,CAAC,EAAE,CACX,QAAQ,EAAE,gBAAgB,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,KAC5C,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACtE;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAA;CAAE,GACrC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,oBAAoB,CAAA;CAAE,CAAC;AAE/C,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,CAAC,CAAC;IAC/C,UAAU,CAAC,EAAE,CACX,QAAQ,EAAE,gBAAgB,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,KAC5C,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACtE;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "better-auth-evp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Email Verification Protocol (Chrome origin trial) plugin for Better Auth, with automatic fallback to any other sign-in method",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|