admin管理员组

文章数量:1356815

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_token cookie (a JWT with a validity of 7 minutes), which contains their userId, 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_token cookie (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 the access_token (JWT) and the refresh_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.


UPDATE

I've manage to make it work changing a little bit, the logic on the Refresh-cookies.js (above), now it looks like this:

//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 (refreshToken || !Updatedcookies && jwt)) { // *CHANGED HERE!
        await updateCookies(jwt, refreshToken);
      }
    }

    update();
  }, [jwt, refreshToken, Updatedcookies]);
}

Now it updates the cookies: 1. if it received a refreshToken (in which case the 'access_token' was invalidated or expired) OR 2. if it received a JWT and the cookies aren't yet updated. This way it doesn't get on a loop. What do you guys think?

本文标签: javascriptRefresh Token Rotation on Nextjs 15Stack Overflow