Skip to content

Commit ab7a977

Browse files
committed
feat: added refresh token when a token has expired logic
1 parent 38bca77 commit ab7a977

File tree

1 file changed

+67
-1
lines changed

1 file changed

+67
-1
lines changed

app/api/auth/[...nextauth]/route.ts

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,59 @@
11
import NextAuth from 'next-auth';
22
import { NextAuthOptions } from 'next-auth';
3+
import { JWT } from 'next-auth/jwt';
34
import CredentialsProvider from 'next-auth/providers/credentials';
45

6+
// Helper function to refresh the token
7+
async function refreshAccessToken(token: JWT) {
8+
try {
9+
const response = await fetch(
10+
`${process.env.NEXT_PUBLIC_API_URL}/api/auth/refresh-token`,
11+
{
12+
method: 'POST',
13+
headers: { 'Content-Type': 'application/json' },
14+
body: JSON.stringify({
15+
token: token.accessToken,
16+
}),
17+
},
18+
);
19+
20+
if (!response.ok) {
21+
throw new Error('Failed to refresh token');
22+
}
23+
24+
const refreshedTokens = await response.json();
25+
26+
// Update permissions from new token if needed
27+
let permissions = [];
28+
if (refreshedTokens.token) {
29+
try {
30+
const payload = JSON.parse(
31+
Buffer.from(refreshedTokens.token.split('.')[1], 'base64').toString(),
32+
);
33+
permissions = payload.permission || [];
34+
} catch (e) {
35+
console.error('Error decoding refreshed JWT:', e);
36+
}
37+
}
38+
39+
return {
40+
...token,
41+
accessToken: refreshedTokens.token,
42+
expiration: refreshedTokens.expiration,
43+
permissions: permissions,
44+
roles: refreshedTokens.roles || token.roles, // Keep existing roles if not in response
45+
};
46+
} catch (error) {
47+
console.error('Error refreshing token:', error);
48+
49+
// Return the original token with an expired flag
50+
return {
51+
...token,
52+
error: 'RefreshAccessTokenError',
53+
};
54+
}
55+
}
56+
557
export const authOptions: NextAuthOptions = {
658
providers: [
759
CredentialsProvider({
@@ -86,9 +138,17 @@ export const authOptions: NextAuthOptions = {
86138
token.roles = user.roles;
87139
token.permissions = user.permissions;
88140
token.expiration = user.expiration;
141+
142+
return token;
89143
}
90144

91-
return token;
145+
// Return the previous token if the access token has not expired yet
146+
if (token.expiration && new Date(token.expiration) > new Date()) {
147+
return token;
148+
}
149+
150+
// Access token has expired, try to refresh it
151+
return refreshAccessToken(token);
92152
},
93153
async session({ session, token }) {
94154
if (token) {
@@ -104,6 +164,12 @@ export const authOptions: NextAuthOptions = {
104164
session.permissions = token.permissions; // Add this line
105165
// @ts-ignore
106166
session.expiration = token.expiration;
167+
168+
// Add refresh token error to session if it exists
169+
if (token.error) {
170+
// @ts-ignore
171+
session.error = token.error;
172+
}
107173
}
108174

109175
return session;

0 commit comments

Comments
 (0)