@checkstack/auth-backend 0.4.21 → 0.4.22
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 +21 -0
- package/package.json +5 -4
- package/src/index.ts +3 -13
- package/src/router.ts +21 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @checkstack/auth-backend
|
|
2
2
|
|
|
3
|
+
## 0.4.22
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 32d52c6: Fix and improve password reset flow + email branding:
|
|
8
|
+
|
|
9
|
+
- **Fix**: password reset emails were failing with "Malformed password reset URL: missing token parameter". Better-auth puts the reset token in the URL path (`/reset-password/{token}`), not as a `?token=` query param, so the previous URL-parsing logic always failed. Now uses the `token` argument better-auth passes to `sendResetPassword` directly.
|
|
10
|
+
- **UX**: the reset password page now validates the token on load via a new anonymous `validateResetToken` endpoint, so users see "Invalid Link" / "Link Expired" before typing a password rather than after submitting. Tokens are 24-char nanoid-style values (~143 bits of entropy), so exposing validity does not enable enumeration.
|
|
11
|
+
- **Fix**: transactional notifications were hardcoded to `importance: "critical"`, causing password reset emails to display a misleading "CRITICAL" badge. The `sendTransactional` contract now accepts an optional `importance` field that defaults to `"info"`.
|
|
12
|
+
- **Branding**: redesigned the email layout (`wrapInEmailLayout`) with a Checkstack-style engineering aesthetic — dark header with grid pattern, monospace importance badge, hardened CTA button (Outlook VML fallback + explicit text color), and force-light color scheme to prevent client auto-inversion from breaking text legibility.
|
|
13
|
+
|
|
14
|
+
- Updated dependencies [32d52c6]
|
|
15
|
+
- Updated dependencies [32d52c6]
|
|
16
|
+
- Updated dependencies [32d52c6]
|
|
17
|
+
- Updated dependencies [32d52c6]
|
|
18
|
+
- Updated dependencies [32d52c6]
|
|
19
|
+
- @checkstack/notification-common@1.0.0
|
|
20
|
+
- @checkstack/backend-api@0.14.0
|
|
21
|
+
- @checkstack/auth-common@0.6.4
|
|
22
|
+
- @checkstack/command-backend@0.1.22
|
|
23
|
+
|
|
3
24
|
## 0.4.21
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@checkstack/auth-backend",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.22",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"checkstack": {
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@checkstack/auth-common": "0.6.3",
|
|
18
|
-
"@checkstack/backend-api": "0.13.
|
|
19
|
-
"@checkstack/notification-common": "0.
|
|
20
|
-
"@checkstack/command-backend": "0.1.
|
|
18
|
+
"@checkstack/backend-api": "0.13.1",
|
|
19
|
+
"@checkstack/notification-common": "0.3.0",
|
|
20
|
+
"@checkstack/command-backend": "0.1.21",
|
|
21
21
|
"better-auth": "^1.4.7",
|
|
22
22
|
"drizzle-orm": "^0.45.0",
|
|
23
23
|
"hono": "^4.12.14",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"@checkstack/scripts": "0.1.2",
|
|
32
32
|
"@checkstack/tsconfig": "0.0.5",
|
|
33
33
|
"@types/node": "^20.0.0",
|
|
34
|
+
"drizzle-kit": "^0.31.10",
|
|
34
35
|
"typescript": "^5.0.0"
|
|
35
36
|
}
|
|
36
37
|
}
|
package/src/index.ts
CHANGED
|
@@ -651,22 +651,12 @@ export default createBackendPlugin({
|
|
|
651
651
|
disableSignUp: !registrationAllowed,
|
|
652
652
|
minPasswordLength: 8,
|
|
653
653
|
maxPasswordLength: 128,
|
|
654
|
-
sendResetPassword: async ({ user,
|
|
654
|
+
sendResetPassword: async ({ user, token }) => {
|
|
655
655
|
// Send password reset notification via all enabled strategies
|
|
656
656
|
// Using void to prevent timing attacks revealing email existence
|
|
657
657
|
const notificationClient = rpcClient.forPlugin(NotificationApi);
|
|
658
|
-
const
|
|
659
|
-
|
|
660
|
-
const parsedUrl = new URL(url);
|
|
661
|
-
const resetToken = parsedUrl.searchParams.get("token");
|
|
662
|
-
if (!resetToken) {
|
|
663
|
-
throw new APIError("BAD_REQUEST", {
|
|
664
|
-
message:
|
|
665
|
-
"Malformed password reset URL: missing token parameter",
|
|
666
|
-
});
|
|
667
|
-
}
|
|
668
|
-
const resetUrl = `${frontendUrl}/auth/reset-password?token=${encodeURIComponent(
|
|
669
|
-
resetToken,
|
|
658
|
+
const resetUrl = `${baseUrl}/auth/reset-password?token=${encodeURIComponent(
|
|
659
|
+
token,
|
|
670
660
|
)}`;
|
|
671
661
|
|
|
672
662
|
void notificationClient.sendTransactional({
|
package/src/router.ts
CHANGED
|
@@ -665,6 +665,26 @@ export const createAuthRouter = (
|
|
|
665
665
|
// ONBOARDING ENDPOINTS
|
|
666
666
|
// ==========================================================================
|
|
667
667
|
|
|
668
|
+
const validateResetToken = os.validateResetToken.handler(
|
|
669
|
+
async ({ input }) => {
|
|
670
|
+
const [record] = await internalDb
|
|
671
|
+
.select({ expiresAt: schema.verification.expiresAt })
|
|
672
|
+
.from(schema.verification)
|
|
673
|
+
.where(
|
|
674
|
+
eq(schema.verification.identifier, `reset-password:${input.token}`),
|
|
675
|
+
)
|
|
676
|
+
.limit(1);
|
|
677
|
+
|
|
678
|
+
if (!record) {
|
|
679
|
+
return { valid: false, reason: "invalid" as const };
|
|
680
|
+
}
|
|
681
|
+
if (record.expiresAt.getTime() <= Date.now()) {
|
|
682
|
+
return { valid: false, reason: "expired" as const };
|
|
683
|
+
}
|
|
684
|
+
return { valid: true };
|
|
685
|
+
},
|
|
686
|
+
);
|
|
687
|
+
|
|
668
688
|
const getOnboardingStatus = os.getOnboardingStatus.handler(async () => {
|
|
669
689
|
// Check if any users exist in the database
|
|
670
690
|
const users = await internalDb
|
|
@@ -1942,6 +1962,7 @@ export const createAuthRouter = (
|
|
|
1942
1962
|
setRegistrationStatus,
|
|
1943
1963
|
getOnboardingStatus,
|
|
1944
1964
|
completeOnboarding,
|
|
1965
|
+
validateResetToken,
|
|
1945
1966
|
getCurrentUserProfile,
|
|
1946
1967
|
updateCurrentUser,
|
|
1947
1968
|
getAnonymousAccessRules,
|