【问题标题】:NextAuth - getToken returns null on production, but returns token in dev environmentNextAuth - getToken 在生产环境中返回 null,但在开发环境中返回令牌
【发布时间】:2022-01-17 20:13:24
【问题描述】:

我有一个简单的应用程序,我在其中使用 NextAuth 进行身份验证。我使用 spotify 提供程序,并且在我的开发环境中一切正常。但是,当我在服务器上的 docker 中运行我的应用程序时,它不会。

这是我的 _app.jsx:

import "../styles/globals.css"
import { SessionProvider } from "next-auth/react"
import { RecoilRoot } from "recoil"

function MyApp({ Component, pageProps }) {
    return (
        <SessionProvider session={pageProps.session}>
            <RecoilRoot>
                <Component {...pageProps} />
            </RecoilRoot>
        </SessionProvider>
    )
}

export default MyApp

_middleware.jsx:

import { getToken } from "next-auth/jwt"
import { getSession } from "next-auth/react"
import { NextResponse } from "next/server"

export async function middleware(req) {
    const session = await getToken({
        req,
        secret: process.env.SECRET,
        secureCookie: true
    })

    const { pathname } = req.nextUrl

    if (pathname.includes("/api/auth") || session) {
        return NextResponse.next()
    }

    if (!session && pathname !== "/login") {
        return NextResponse.redirect("/login")
    }
}

index.jsx

export default function Home() {
    return (
        <div className="bg-black h-screen overflow-hidden">
            <main className="flex">
                <Sidebar />
                <Center />
            </main>

            <div className="sticky bottom-0">
                <Player />
            </div>
        </div>
    )
}

export async function getServerSideProps(ctx) {
    return {
        props: {
            session: await getSession(ctx)
        }
    }
}

[...nextauth].js

import NextAuth from "next-auth"
import SpotifyProvider from "next-auth/providers/spotify"
import spotifyApi, { LOGIN_URL } from "../../../lib/spotify"

async function refreshAccessToken(token) {
    try {
        spotifyApi.setAccessToken(token.accessToken)
        spotifyApi.setRefreshToken(token.refreshToken)

        const { body: refreshedToken } = await spotifyApi.refreshAccessToken()
        console.log("REFRESHED TOKEN IS", refreshedToken)

        return {
            ...token,
            accessToken: refreshedToken.access_token,
            accessTokenExpires: Date.now() + refreshedToken.expires_in * 1000, // = 1 hour as 3600 returns from spotify API
            refreshToken: refreshedToken.refresh_token ?? token.refreshToken
        }
    } catch (error) {
        console.error(error)

        return {
            ...token,
            error: "RefreshAccessTokenError"
        }
    }
}

export default NextAuth({
    // Configure one or more authentication providers
    providers: [
        SpotifyProvider({
            clientId: process.env.NEXT_PUBLIC_CLIENT_ID ?? "NO_CLIENT_ID",
            clientSecret: process.env.NEXT_PUBLIC_CLIENT_SECRET ?? "NO_CLIENT_SECRET",
            authorization: LOGIN_URL
        })
        // ...add more providers here
    ],
    session: {
        strategy: "jwt"
    },
    secret: process.env.JWT_SECRET,
    pages: {
        signIn: "/login"
    },
    callbacks: {
        async jwt({ token, account, user }) {
            // initial sign in
            if (account && user) {
                return {
                    ...token,
                    accessToken: account.access_token,
                    refreshToken: account.refresh_token,
                    username: account.providerAccountId,
                    accessTokenExpires: account.expires_at * 1000
                }
            }

            if (Date.now() < token.accessTokenExpires) {
                console.log("EXISTING ACCESS TOKEN IS VALID!")
                return token
            }

            // refresh access token (imagine having a expired token pfff)
            console.log("ACCESS TOKEN HAS EXPIRED! REFRESHING...")
            return await refreshAccessToken(token)
        },

        async session({ session, token }) {
            session.user.accessToken = token.accessToken
            session.user.refreshToken = token.refreshToken
            session.user.username = token.username
            return session
        }
    }
})

然后我在本地使用这个 .env.local 文件:

NEXTAUTH_URL=http://localhost:3000
NEXT_PUBLIC_CLIENT_SECRET=[MY_SECRET]
NEXT_PUBLIC_CLIENT_ID=[MY_CLIENT_ID]
JWT_SECRET=[MY_JWT_SECRET]

在生产中我使用这个 Dockerfile:

FROM node:16-alpine AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json ./
RUN yarn install --frozen-lockfile

FROM node:16-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN yarn build

# Production image, copy all the files and run next
FROM node:16-alpine AS runner
WORKDIR /app

ENV NODE_ENV production

RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

COPY --from=1 /app/next.config.js ./
COPY --from=1 /app/public ./public
COPY --from=1 --chown=nextjs:nodejs /app/.next ./.next
COPY --from=1 /app/node_modules ./node_modules
COPY --from=1 /app/package.json ./package.json

USER nextjs

EXPOSE 3000

CMD ["yarn", "start"]

连同这个 docker-compose.yml 文件:

version: "3.3"
services:
  next-app:
    image: ghcr.io/my-username/my-app:latest
    ports:
      - "3000:3000"
    restart: unless-stopped
    environment:
      - NEXT_PUBLIC_CLIENT_SECRET=[...]
      - NEXT_PUBLIC_CLIENT_ID=...]
      - NEXTAUTH_URL=https://mydomain.dev
      - JWT_SECRET=[...]

所以,上面的代码在我的 localhost 环境中运行良好,但在我的服务器上却无法运行。当我登录时,我确实收到以下控制台消息:“现有访问令牌有效!”你可以在[...nextauth] 中看到,但是在_middleware.js 中,会话值始终为空,所以我停留在登录页面。

我已经尝试在 nextAuth 中使用调试选项运行,但是没有错误消息或类似的东西。所以,我想知道:什么可能导致这个问题,我该如何解决?

可能值得注意:部署到 vercel 时问题是相同的。所以它可能与 docker 无关。

【问题讨论】:

  • 你在中间件代码中使用process.env.SECRETSECRET在Docker中设置在哪里?
  • 哇...是的,令人难以置信的是,这就是问题所在...感谢您抓住那个

标签: reactjs docker next.js next-auth


【解决方案1】:

Vercel 不支持 docker 部署:

https://vercel.com/support/articles/does-vercel-support-docker-deployments

所以当你将它部署到 Vercel 时,你的环境变量并没有设置。

【讨论】:

  • 我没有将 docker 部署到 Vercel。我只是说,当我使用 docker 部署它时,或者当我通常将它部署到 Vercel 时,我得到了同样的错误。
猜你喜欢
  • 1970-01-01
  • 2014-11-02
  • 2010-10-16
  • 2015-01-30
  • 2021-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-22
相关资源
最近更新 更多