-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathauth.ts
More file actions
192 lines (181 loc) · 5.21 KB
/
auth.ts
File metadata and controls
192 lines (181 loc) · 5.21 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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
import NextAuth from 'next-auth';
import { DrizzleAdapter } from '@auth/drizzle-adapter';
import { db } from '@/db';
import { compare } from 'bcryptjs';
import { users } from '@/db/schema';
import { eq } from 'drizzle-orm';
import { DefaultSession } from 'next-auth';
import { DefaultUser } from '@auth/core/types';
import Credentials from 'next-auth/providers/credentials';
import type { AdapterUser } from '@auth/core/adapters';
import type { Adapter, AdapterAccount } from 'next-auth/adapters';
// Extend the built-in session types
declare module 'next-auth' {
interface Session {
user: {
id: string;
role: string;
} & DefaultSession["user"];
}
interface User extends DefaultUser {
id?: string;
role?: string;
}
}
// Extend adapter types
declare module '@auth/core/adapters' {
interface AdapterUser {
id: string;
role: string;
email: string;
emailVerified: Date | null;
}
}
// Create a custom adapter that extends the default one
function customAdapter(): Adapter {
return {
async createUser(data) {
const { email, name } = data;
const user = await db.insert(users)
.values({
email,
name: name ?? '',
password: '', // Will be updated later
role: 'admin', // Set default role for new users
})
.returning()
.then((res) => res[0] ?? null);
if (!user) throw new Error('Failed to create user');
return {
...user,
id: user.id.toString(),
emailVerified: null,
};
},
async getUser(id) {
const user = await db.query.users.findFirst({
where: (users) => eq(users.id, parseInt(id, 10)),
});
if (!user) return null;
return {
...user,
id: user.id.toString(),
emailVerified: null,
};
},
async getUserByEmail(email) {
const user = await db.query.users.findFirst({
where: (users) => eq(users.email, email),
});
if (!user) return null;
return {
...user,
id: user.id.toString(),
emailVerified: null,
};
},
async getUserByAccount({ providerAccountId, provider }): Promise<AdapterUser | null> {
return null; // We're not using OAuth providers yet
},
async updateUser(user): Promise<AdapterUser> {
const updated = await db.update(users)
.set({
name: user.name ?? '',
email: user.email ?? '',
// Don't update password here
})
.where(eq(users.id, parseInt(user.id, 10)))
.returning()
.then((res) => res[0] ?? null);
if (!updated) throw new Error('Failed to update user');
return {
...updated,
id: updated.id.toString(),
emailVerified: null,
};
},
async deleteUser(userId): Promise<void> {
await db.delete(users).where(eq(users.id, parseInt(userId, 10)));
},
async linkAccount(account): Promise<AdapterAccount> {
// We're not using OAuth providers yet
return account;
},
async unlinkAccount({ providerAccountId, provider }): Promise<void> {
// We're not using OAuth providers yet
},
async createSession({ sessionToken, userId, expires }) {
// Using JWT strategy, so this isn't needed
return { sessionToken, userId, expires };
},
async getSessionAndUser(sessionToken) {
// Using JWT strategy, so this isn't needed
return null;
},
async updateSession({ sessionToken }) {
// Using JWT strategy, so this isn't needed
return null;
},
async deleteSession(sessionToken) {
// Using JWT strategy, so this isn't needed
},
async createVerificationToken({ identifier, expires, token }) {
// Not implementing email verification yet
return null;
},
async useVerificationToken({ identifier, token }) {
// Not implementing email verification yet
return null;
},
};
}
export const {
handlers: { GET, POST },
auth,
signIn,
signOut,
} = NextAuth({
adapter: DrizzleAdapter(db) as any, // Type assertion needed until adapter types are fixed
session: { strategy: 'jwt' },
pages: {
signIn: '/login',
},
providers: [
Credentials({
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
if (!credentials?.email || !credentials?.password) return null;
const user = await db.query.users.findFirst({
where: eq(users.email, credentials.email as string),
});
if (!user) return null;
const isValidPassword = await compare(credentials.password as string, user.password);
if (!isValidPassword) return null;
return {
id: user.id.toString(),
email: user.email,
role: user.role,
};
}
})
],
callbacks: {
async jwt({ token, user }) {
if (user) {
token.id = user.id;
token.role = user.role;
}
return token;
},
async session({ session, token }) {
if (token) {
session.user.id = token.id as string;
session.user.role = token.role as string;
}
return session;
},
},
});