What I want
I am developing an app in Next.js 15, and for authorization, I want to use JWT and Refresh Token, following this process:
- When a user visits a restricted page, the system checks whether they have an
access_tokencookie (a JWT with a validity of 7 minutes), which contains theiruserId, and verifies if it is valid.- If valid, it recreates this JWT, renewing its validity for another 7 minutes, sends it back to the client as a cookie, retrieves the user session (for subsequent validation) e continue.
- If invalid, it checks whether they have a
refresh_tokencookie (just a token with a validity of 7 days) and:- If not, redirects them to the login page.
- If they do, it validates in the database (in my case, KV or Redis) whether this token exists, retrieves the
userId, recreates both theaccess_token(JWT) and therefresh_token, updates the cookies, retrieves the user session, and continue.
The problem
The issue I’m facing is with this Refresh Token Rotation:
When accessing the page (e.g., app/dashboard/page.js), I can read the cookies for verification, but I cannot update the cookies because they can only be “written” via Server Actions or Route Handlers.
- I’ve tried creating a Server Component (
UpdateAccessCookies) to place on the page that would solely be responsible for sending the cookies, but doing this causes the system to enter a loop. - I’ve also tried using an API, but when making a POST request, the system didn’t receive the cookies—and I’m not sure if this would be the best option. I’d prefer to find a way to use Server Actions.
Here are some example codes
// Register Page
import { getCurrentSession } from '@/services/session';
import { RegisterForm } from './Form';
export default async function Register() {
const { session, data } = await getCurrentSession();
return (
<>
<h1>Register</h1>
<RegisterForm/>
</>
);
}
//session.js
'use server'
//...imports...
export async function getCurrentSession() {
const accessToken = await getCookie('access_token');
let data = {};
if (accessToken) {
const jwtValidation = await validarJWT(accessToken);
if (jwtValidation) {
data.userId = jwtValidation.userId;
data.jwt = {
token: await createJWT({ userId: jwtValidation.userId }),
expiraEm: JWT_EXPIRES_IN,
};
}
}
if (!data.userId) {
const refreshToken = await getCookie('refresh_token');
if (!refreshToken) {
return { session: null, data: null };
}
data.userId = await getStorageAdapterData('refresh_token', refreshToken);
if (!data.userId) {
return { session: null, data: null };
}
data.jwt = {
token: await createJWT({ userId: data.userId }),
expiraEm: JWT_EXPIRES_IN,
};
data.refreshToken = {
token: await createRefreshToken(data.userId, refreshToken),
expiraEm: SESSION_EXPIRES_IN,
};
}
const session = await getStorageAdapterData('session', data.userId);
if (!session) {
return { session: null, data: null };
}
return { session, data };
}
As I mentioned, I have already tried updating the cookies:
- On the page, right after
getCurrentSession(would be ideal) - In session.js, right after renewing the tokens
- In a Refresh-cookies component (like the code below)
// Register Page 2
import { getCurrentSession } from '@/services/session';
import { RegisterForm } from './Form';
import RefreshCookies from '@/components/refresh-cookies';
export default async function Register() {
const { session, data } = await getCurrentSession();
return (
<>
<h1>Register</h1>
<RegisterForm/>
<RefreshCookies
jwt={data?.jwt}
refreshToken={data?.refreshToken}
/>
</>
);
}
//Refresh-cookies.js
'use client';
import { useEffect, useState } from 'react';
import { createCookie } from '@/services/cookies';
async function updateCookies(jwt, refreshToken) {
if (jwt) {
await createCookie('access_token', jwt.token, jwt.expires);
}
if (refreshToken) {
await createCookie(
'refresh_token',
refreshToken.token,
refreshToken.expires
);
}
}
export default function RefreshCookies({ jwt, refreshToken }) {
const [Updatedcookies, setUpdatedCookies] = useState(false);
useEffect(() => {
async function update() {
if (!Updatedcookies && (jwt || refreshToken)) {
await updateCookies(jwt, refreshToken);
}
}
update();
}, [jwt, refreshToken, Updatedcookies]);
}
What would you suggest?
Note: I don’t want to use an external library. I also don’t want to perform verification using middleware because it would check on every request to restricted pages, which would significantly increase request time.
