-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathauth.ts
95 lines (86 loc) · 3.34 KB
/
auth.ts
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
import { DrizzleAdapter } from '@auth/drizzle-adapter';
import NextAuth, { User } from 'next-auth';
import { JWT } from 'next-auth/jwt';
import authConfig from '@/auth.config';
import { env } from '@/config/env.mjs';
import { db } from '@/lib/db';
import { accounts, users } from '@/lib/db/schema';
export const { handlers, signIn, signOut, auth } = NextAuth({
// You can use this on Vercel to protect preview deployments
redirectProxyUrl: env.AUTH_REDIRECT_PROXY_URL,
...authConfig,
adapter: DrizzleAdapter(db, {
usersTable: users,
accountsTable: accounts,
}),
providers: [...authConfig.providers],
callbacks: {
async jwt({ account, token, user }) {
if (user && user.id) {
// Fetch user's teams, roles, and projects
token = await updateToken(token, user);
}
if (account) {
token.access_token = account.id_token;
token.expires_at = account.expires_at;
token.refresh_token = account.refresh_token;
} else if (Date.now() < (token.expires_at ?? 0) * 1000) {
// If the token is still valid, return it
return token;
} else {
// Subsequent logins, if the `access_token` has expired, try to refresh it
if (!token.refresh_token) throw new Error('Missing refresh token');
try {
// The `token_endpoint` can be found in the provider's documentation. Or if they support OIDC,
// at their `/.well-known/openid-configuration` endpoint.
// i.e. https://accounts.google.com/.well-known/openid-configuration
const response = await fetch('https://oauth2.googleapis.com/token', {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: process.env.AUTH_GOOGLE_ID!,
client_secret: process.env.AUTH_GOOGLE_SECRET!,
grant_type: 'refresh_token',
refresh_token: token.refresh_token!,
}),
method: 'POST',
});
const responseTokens = await response.json();
if (!response.ok) throw responseTokens;
return {
// Keep the previous token properties
...token,
access_token: responseTokens.id_token,
expires_at: Math.floor(
Date.now() / 1000 + (responseTokens.expires_in as number),
),
// Fall back to old refresh token, but note that
// many providers may only allow using a refresh token once.
refresh_token: responseTokens.refresh_token ?? token.refresh_token,
};
} catch (error) {
console.error('Error refreshing access token', error);
// The error property can be used client-side to handle the refresh token error
return { ...token, error: 'RefreshAccessTokenError' as const };
}
}
return token;
},
async session({ session, token }) {
if (session.user) {
session.user.id = token.sub as string;
session.access_token = token.access_token;
}
return session;
},
},
events: {
async createUser({ user }) {
if (!user.email) return;
// TODO - Use this to setup a new Team for the user, or any other "new user" setup tasks.
},
},
});
const updateToken = async (token: JWT, user: User) => {
token.user = user;
return token;
};