You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改GitLab AccessToken过期时长?解决NextAuth令牌频繁变更问题

GitLab登录AccessToken长期有效配置(NextAuth)

核心问题分析

GitLab默认返回的access_token有效期为2小时,要实现长期有效,必须:

  • 授权时请求offline_access scope以获取refresh_token
  • 利用refresh_token定期刷新access_token
  • 将Token持久化存储到数据库,避免丢失

具体实现步骤

1. 更新GitLab授权Scope

修改GitLabProvider的authorization参数,添加offline_access scope,这样才能拿到用于刷新Token的refresh_token:

GitLabProvider({
  clientId: process.env.CLIENT_ID,
  clientSecret: process.env.CLIENT_SECRET,
  authorization: { params: { scope: 'api offline_access' } }, // 新增offline_access
})

2. 配置数据库适配器

NextAuth需要通过适配器将用户会话和Token存储到数据库,以Prisma为例,先安装依赖并配置:

npm install @next-auth/prisma-adapter prisma

然后在NextAuth配置中添加适配器:

import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export default NextAuth({
  adapter: PrismaAdapter(prisma), // 添加数据库适配器
  // ...其他配置
})

3. 完善JWT与Session回调

在jwt回调中处理Token刷新逻辑,当access_token过期时,调用GitLab的刷新接口获取新的Token,并更新存储:

callbacks: {
  async jwt({ token, user, account, trigger, session }) {
    // 首次登录时保存account信息(包含access_token、refresh_token、过期时间)
    if (user && account) {
      return {
        ...token,
        accessToken: account.access_token,
        refreshToken: account.refresh_token,
        expiresAt: account.expires_at * 1000, // 转换为毫秒
        userId: user.id,
      };
    }

    // 如果Token未过期,直接返回
    if (Date.now() < token.expiresAt) {
      return token;
    }

    // Token过期,用refresh_token刷新
    try {
      const response = await axios.post('https://gitlab.com/oauth/token', new URLSearchParams({
        client_id: process.env.CLIENT_ID,
        client_secret: process.env.CLIENT_SECRET,
        grant_type: 'refresh_token',
        refresh_token: token.refreshToken,
      }));

      const newToken = response.data;
      return {
        ...token,
        accessToken: newToken.access_token,
        refreshToken: newToken.refresh_token ?? token.refreshToken, // 如果返回新的refresh_token就替换,否则用旧的
        expiresAt: Date.now() + newToken.expires_in * 1000,
      };
    } catch (error) {
      console.error('Token刷新失败:', error);
      // 刷新失败时返回原始token,后续请求会触发重新登录
      return { ...token, error: 'RefreshAccessTokenError' };
    }
  },
  async session({ session, token }) {
    // 将Token信息传递到session中,供业务逻辑使用
    session.user.id = token.userId;
    session.accessToken = token.accessToken;
    session.error = token.error;
    return session;
  },
},

4. 完整配置代码

import NextAuth from 'next-auth/next';
import GitLabProvider from 'next-auth/providers/gitlab';
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { PrismaClient } from "@prisma/client";
import axios from 'axios';

const prisma = new PrismaClient();

export default NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [
    GitLabProvider({
      clientId: process.env.CLIENT_ID,
      clientSecret: process.env.CLIENT_SECRET,
      authorization: { params: { scope: 'api offline_access' } },
    }),
  ],
  callbacks: {
    async jwt({ token, user, account }) {
      if (user && account) {
        return {
          ...token,
          accessToken: account.access_token,
          refreshToken: account.refresh_token,
          expiresAt: account.expires_at * 1000,
          userId: user.id,
        };
      }

      if (Date.now() < token.expiresAt) {
        return token;
      }

      try {
        const response = await axios.post('https://gitlab.com/oauth/token', new URLSearchParams({
          client_id: process.env.CLIENT_ID,
          client_secret: process.env.CLIENT_SECRET,
          grant_type: 'refresh_token',
          refresh_token: token.refreshToken,
        }));

        const newToken = response.data;
        return {
          ...token,
          accessToken: newToken.access_token,
          refreshToken: newToken.refresh_token ?? token.refreshToken,
          expiresAt: Date.now() + newToken.expires_in * 1000,
        };
      } catch (error) {
        console.error('Token刷新失败:', error);
        return { ...token, error: 'RefreshAccessTokenError' };
      }
    },
    async session({ session, token }) {
      session.user.id = token.userId;
      session.accessToken = token.accessToken;
      session.error = token.error;
      return session;
    },
  },
  secret: process.env.SECRET,
  session: {
    strategy: 'jwt', // 使用JWT策略配合数据库存储
  },
});

关键注意事项

  • 确保GitLab应用配置中允许refresh_token(默认是允许的)
  • 数据库适配器需要根据你使用的数据库类型调整(如MongoDB、PostgreSQL等)
  • 刷新Token失败时,建议在前端捕获RefreshAccessTokenError并引导用户重新登录

内容的提问来源于stack exchange,提问作者estevan426

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 00:10:36