-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathreset-password.ts
More file actions
120 lines (101 loc) · 3.06 KB
/
reset-password.ts
File metadata and controls
120 lines (101 loc) · 3.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import z from 'zod'
import { type ApiRouteHandler, type ApiRouteSchema, createEndpoint } from '../../endpoint'
import { type AuthContext } from '../context'
import { hashPassword } from '../utils'
interface InternalRouteOptions {
prefix?: string
}
export function resetPasswordEmail<const TOptions extends InternalRouteOptions>(options: TOptions) {
const schema = {
method: 'POST',
path: '/api/auth/reset-password',
query: z.object({
token: z.string(),
}),
body: z.object({
password: z.string(),
}),
responses: {
200: z.object({
status: z.string(),
}),
400: z.object({
status: z.string(),
}),
},
} as const satisfies ApiRouteSchema
const handler: ApiRouteHandler<AuthContext, typeof schema> = async (args) => {
if (!args.context.authConfig.resetPassword?.enabled) {
// TODO: Log not enabled
return {
status: 400,
body: { status: 'reset password not enabled' },
}
}
const identifier = `reset-password:${args.query.token}`
const verification =
await args.context.internalHandlers.verification.findByIdentifier(identifier)
if (!verification || !verification.value) {
return {
status: 400,
body: { status: 'invalid reset password value' },
}
}
const user = await args.context.internalHandlers.user.findById(verification.value)
if (!user) {
return {
status: 400,
body: { status: 'user not found' },
}
}
if (verification.expiresAt < new Date()) {
return {
status: 400,
body: { status: 'reset password token expired' },
}
}
// delete the verification token
await args.context.internalHandlers.verification.delete(verification.id)
const hashedPassword = await hashPassword(args.body.password)
await args.context.internalHandlers.account.updatePassword(user.id, hashedPassword)
const redirectTo = `${args.context.authConfig.resetPassword?.redirectTo ?? '/admin/auth/login'}`
const responseHeaders = {
Location: redirectTo,
}
return {
status: 200,
headers: responseHeaders,
body: { status: 'ok' },
}
}
return createEndpoint(schema, handler)
}
export function validateResetToken<const TOptions extends InternalRouteOptions>(options: TOptions) {
const schema = {
method: 'POST',
path: '/api/auth/validate-reset-password-token',
body: z.object({
token: z.string(),
}),
responses: {
200: z.object({
verification: z.object({
id: z.string(),
identifier: z.string(),
value: z.string().nullable(),
expiresAt: z.date(),
}),
}),
},
} as const satisfies ApiRouteSchema
const handler: ApiRouteHandler<AuthContext, typeof schema> = async (args) => {
const verification = await args.context.internalHandlers.verification.findByIdentifier(
`reset-password:${args.body.token}`
)
return {
status: 200,
body: { verification },
}
}
return createEndpoint(schema, handler)
}