Skip to content

Commit 34c47f7

Browse files
authored
chore(auth): upgrade better-auth 1.3.12 → 1.6.11 (#4766)
* chore(auth): upgrade better-auth 1.3.12 → 1.6.11 * chore(auth): address Greptile review — broaden change-email type + migration newline * fix(auth): correct oneTimeToken expiresIn unit (minutes, not seconds) Better-auth's oneTimeToken expiresIn is in minutes (multiplied by 60_000ms internally). Sim's existing 24*60*60 evaluated to ~60 days of token lifetime instead of the intended 24 hours. Tokens are one-time-use and typically consumed within seconds of generation (Socket.IO handshake), so this tightens an unused security window without affecting UX.
1 parent a693a1e commit 34c47f7

14 files changed

Lines changed: 17328 additions & 209 deletions

File tree

apps/sim/app/api/auth/forget-password/route.test.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import { createMockRequest } from '@sim/testing'
77
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
88

9-
const { mockForgetPassword, mockLogger } = vi.hoisted(() => {
9+
const { mockRequestPasswordReset, mockLogger } = vi.hoisted(() => {
1010
const logger = {
1111
info: vi.fn(),
1212
warn: vi.fn(),
@@ -17,7 +17,7 @@ const { mockForgetPassword, mockLogger } = vi.hoisted(() => {
1717
child: vi.fn(),
1818
}
1919
return {
20-
mockForgetPassword: vi.fn(),
20+
mockRequestPasswordReset: vi.fn(),
2121
mockLogger: logger,
2222
}
2323
})
@@ -28,7 +28,7 @@ vi.mock('@/lib/core/utils/urls', () => ({
2828
vi.mock('@/lib/auth', () => ({
2929
auth: {
3030
api: {
31-
forgetPassword: mockForgetPassword,
31+
requestPasswordReset: mockRequestPasswordReset,
3232
},
3333
},
3434
}))
@@ -43,7 +43,7 @@ import { POST } from '@/app/api/auth/forget-password/route'
4343
describe('Forget Password API Route', () => {
4444
beforeEach(() => {
4545
vi.clearAllMocks()
46-
mockForgetPassword.mockResolvedValue(undefined)
46+
mockRequestPasswordReset.mockResolvedValue(undefined)
4747
})
4848

4949
afterEach(() => {
@@ -62,7 +62,7 @@ describe('Forget Password API Route', () => {
6262
expect(response.status).toBe(200)
6363
expect(data.success).toBe(true)
6464

65-
expect(mockForgetPassword).toHaveBeenCalledWith({
65+
expect(mockRequestPasswordReset).toHaveBeenCalledWith({
6666
body: {
6767
email: 'test@example.com',
6868
redirectTo: 'https://app.example.com/reset',
@@ -83,7 +83,7 @@ describe('Forget Password API Route', () => {
8383
expect(response.status).toBe(400)
8484
expect(data.message).toBe('Redirect URL must be a valid same-origin URL')
8585

86-
expect(mockForgetPassword).not.toHaveBeenCalled()
86+
expect(mockRequestPasswordReset).not.toHaveBeenCalled()
8787
})
8888

8989
it('should send password reset email without redirectTo', async () => {
@@ -97,7 +97,7 @@ describe('Forget Password API Route', () => {
9797
expect(response.status).toBe(200)
9898
expect(data.success).toBe(true)
9999

100-
expect(mockForgetPassword).toHaveBeenCalledWith({
100+
expect(mockRequestPasswordReset).toHaveBeenCalledWith({
101101
body: {
102102
email: 'test@example.com',
103103
redirectTo: undefined,
@@ -115,7 +115,7 @@ describe('Forget Password API Route', () => {
115115
expect(response.status).toBe(400)
116116
expect(data.message).toBe('Email is required')
117117

118-
expect(mockForgetPassword).not.toHaveBeenCalled()
118+
expect(mockRequestPasswordReset).not.toHaveBeenCalled()
119119
})
120120

121121
it('should handle empty email', async () => {
@@ -129,13 +129,13 @@ describe('Forget Password API Route', () => {
129129
expect(response.status).toBe(400)
130130
expect(data.message).toBe('Please provide a valid email address')
131131

132-
expect(mockForgetPassword).not.toHaveBeenCalled()
132+
expect(mockRequestPasswordReset).not.toHaveBeenCalled()
133133
})
134134

135135
it('should handle auth service error with message', async () => {
136136
const errorMessage = 'User not found'
137137

138-
mockForgetPassword.mockRejectedValue(new Error(errorMessage))
138+
mockRequestPasswordReset.mockRejectedValue(new Error(errorMessage))
139139

140140
const req = createMockRequest('POST', {
141141
email: 'nonexistent@example.com',
@@ -153,7 +153,7 @@ describe('Forget Password API Route', () => {
153153
})
154154

155155
it('should handle unknown error', async () => {
156-
mockForgetPassword.mockRejectedValue('Unknown error')
156+
mockRequestPasswordReset.mockRejectedValue('Unknown error')
157157

158158
const req = createMockRequest('POST', {
159159
email: 'test@example.com',

apps/sim/app/api/auth/forget-password/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
3333

3434
const { email, redirectTo } = parsed.data.body
3535

36-
await auth.api.forgetPassword({
36+
await auth.api.requestPasswordReset({
3737
body: {
3838
email,
3939
redirectTo,

apps/sim/components/emails/auth/otp-verification-email.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { getBrandConfig } from '@/ee/whitelabeling'
66
interface OTPVerificationEmailProps {
77
otp: string
88
email?: string
9-
type?: 'sign-in' | 'email-verification' | 'forget-password' | 'chat-access'
9+
type?: 'sign-in' | 'email-verification' | 'change-email' | 'forget-password' | 'chat-access'
1010
chatTitle?: string
1111
}
1212

@@ -16,6 +16,8 @@ const getSubjectByType = (type: string, brandName: string, chatTitle?: string) =
1616
return `Sign in to ${brandName}`
1717
case 'email-verification':
1818
return `Verify your email for ${brandName}`
19+
case 'change-email':
20+
return `Verify your new email for ${brandName}`
1921
case 'forget-password':
2022
return `Reset your ${brandName} password`
2123
case 'chat-access':

apps/sim/components/emails/render.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ interface WorkspaceInvitation {
3939
export async function renderOTPEmail(
4040
otp: string,
4141
email: string,
42-
type: 'sign-in' | 'email-verification' | 'forget-password' = 'email-verification',
42+
type:
43+
| 'sign-in'
44+
| 'email-verification'
45+
| 'change-email'
46+
| 'forget-password' = 'email-verification',
4347
chatTitle?: string
4448
): Promise<string> {
4549
return await render(OTPVerificationEmail({ otp, email, type, chatTitle }))

apps/sim/components/emails/subjects.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { getBrandConfig } from '@/ee/whitelabeling'
44
export type EmailSubjectType =
55
| 'sign-in'
66
| 'email-verification'
7+
| 'change-email'
78
| 'forget-password'
89
| 'reset-password'
910
| 'invitation'
@@ -34,6 +35,8 @@ export function getEmailSubject(type: EmailSubjectType): string {
3435
return `Sign in to ${brandName}`
3536
case 'email-verification':
3637
return `Verify your email for ${brandName}`
38+
case 'change-email':
39+
return `Verify your new email for ${brandName}`
3740
case 'forget-password':
3841
return `Reset your ${brandName} password`
3942
case 'reset-password':

0 commit comments

Comments
 (0)