简体   繁体   English

API 路由中的 NextAuth:unstable_getServerSession 和 getToken 总是返回 null

[英]NextAuth in API routes: unstable_getServerSession and getToken always return null

I try to use the unstable_getServerSession in an nextJS API route (before I was using a custom authentication method) Maybe I have a wrong setup with JWT?我尝试在 nextJS API 路由中使用unstable_getServerSession的getServerSession(在我使用自定义身份验证方法之前)也许我的 JWT 设置错误? Maybe I can't see the forest for the trees anymore也许我再也见不到森林了

I followed the tutorial "Securing API routes" : pages/api/get-session-example.js https://next-auth.js.org/tutorials/securing-pages-and-api-routes#securing-api-routes我按照教程“保护 API 路由”pages/api/get-session-example.js https://next-auth.js.org/tutorials/securing-pages-and-api-routes#securing-api-routes

  • in the getServerSideProps function I call my next/API endopoint via a AxiosgetServerSideProps function 我通过 Axios 调用我的下一个/API 端点
  • I pass Bearer token to the next/API call我将Bearer token传递给下一个/API 调用
  • the API endpoint should authorize that user is allowed to call it by JWT API 端点应该授权用户被 JWT 调用它
  • the API endpoint then should fetch Stripe subscriptions based on the stripe_customer_id embedded in the JWT然后,API 端点应该根据嵌入在stripe_customer_id中的 stripe_customer_id 获取 Stripe 订阅

But whatever I try the session and token in the next/API endpoint is always null但无论我尝试session和下一个/API 端点中的token始终是null

This is my getServerSideProps function:这是我的getServerSideProps function:


export const getServerSideProps: GetServerSideProps = async (context: GetServerSidePropsContext) => {

    // ### contains the correct SESSION (it works!)
    const session = await unstable_getServerSession(context.req, context.res, authOptions)

    // ### contains the correct TOKEN (it works!)
    const token = await getToken(context).then(token => {
            return token.token as string; 
        })
    if (!session) {
        return {
            redirect: {
                permanent: false,
                destination: url.accountSignIn().href,
            },
        };
    }
    else {
        // My SESSION IS AVAILABLE (I use this to show user data.
        // #### NOW I CALL THE NEXT/API endpoint with the getToken() response
        const stripeSubscriptions = await axios.get(`${process.env.NEXT_PUBLIC_URL}/api/account/subscriptions`, { headers: { 'Authorization': 'Bearer ' + token } }).then(res => {
            console.log('res.data :>> ', res.data);
            const subscriptions = res.data.data as Stripe.Subscription[]
            return subscriptions
        }).catch((err: any) => {
            console.log("Dashboard ERR", err.response.data.message || err); // #### I ALWAYS END with {
  error: "Unauthorized",  message: "not authorized",  data: []}
  
            signOut({ callbackUrl: 'http://localhost:8000/account/login' })
            return err.response.data

        });

        return {
            props: { stripeSubscriptions, session }, // will be passed to the page component as props
        };
    }

This is my next/API/endpoint /api/account/subscriptions这是我的下一个/API/endpoint /api/account/subscriptions

import { NextApiRequest, NextApiResponse } from 'next';
import Stripe from 'stripe';

const secret = process.env.NEXTAUTH_SECRET // #### is correct
import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "../../api/auth/[...nextauth]"
import { getToken } from "next-auth/jwt"

export default async (req: NextApiRequest, res: NextApiResponse) => {

    // #### the header definitely has the Bearer token in it's request: req.headers.authorization: `"Bearer eyJhbGciOiJIUzI1NiJ9......"`
    const session = await unstable_getServerSession(req, res, authOptions)  // ### ALWAYS RETURNS "NULL"
    const token = await getToken({ req, secret }) // ### ALWAYS RETURNS "NULL"

    if (session) { // ### ALWAYS "NULL"

        return new Promise<void>((resolve) => {
            const customer_id = session.stripe_id as string; // #### should get extracted from JWT
            const stripe = new Stripe(process.env.STRIPE_API_SECRET!, { apiVersion: '2020-08-27' });

            stripe.subscriptions.list({ customer: customer_id }).then(subscriptions => {
                res.status(200).json({ data: subscriptions });
                return resolve();
            }).catch(err => {
                res.status(500).json({ error: `Stripe Subscriptions error: ${err.message}` });
                return resolve();
            }
            );
        })
    } else {
        // Not Signed in
        // #### I ALWAYS END HERE
        res.status(401).json({ error: 'Unauthorized', message: "not authorized", data: [] });
    }
    res.end()

and here is my COMPLETE [...nextauth].ts file:这是我的 COMPLETE [...nextauth].ts文件:

I have to mention, that a Rails backend creates the JWT.我不得不提一下,Rails 后端创建了 JWT。

import jwtDecode from 'jwt-decode';
import NextAuth from 'next-auth';
import type { NextAuthOptions } from 'next-auth';

import ProviderCredentials from 'next-auth/providers/credentials';
import { JWT } from 'next-auth/jwt';

export const authOptions: NextAuthOptions = {
    // Configure one or more authentication providers
    providers: [
        ProviderCredentials({
            id: 'credentials',
            name: 'credentials',
            credentials: {
                username: {
                    label: 'Email',
                    type: 'email',
                    placeholder: 'email@ihrUnternehmen.com',
                },
                password: { label: 'Passwort', type: 'password' },
            },
            async authorize(credentials, req) {
                // The 'url' is pointing to a Rails API endpoint which returns a JWT Token, and account data
                const url = `${process.env.NEXT_PUBLIC_API_URL}/auth/login`;

                const res = await fetch(url, {
                    method: 'POST',
                    body: JSON.stringify(credentials),
                    headers: {
                        'Content-Type': 'application/json',
                    },
                });
                const user = await res.json();
                // If no error and we have user data, return it
                console.log('user authorize :>> ', user);
                if (user && (user.error || user.message)) {
                    throw new Error(user.message || user.error);
                }

                if (res.ok && user) {
                    return user; // MY CONTENT
                }
                // Return null if user data could not be retrieved
                return null;
            },
        }),
    ],
    session: {
        strategy: 'jwt',
    },
    // callback is where we decide whether the token is ready to be refreshed.
    jwt: {
        secret: process.env.NEXTAUTH_SECRET,
    },
    secret: process.env.NEXTAUTH_SECRET,
    callbacks: {
        // This callback is called whenever a JSON Web Token is created (i.e. at sign in)
        // or updated (i.e whenever a session is accessed in the client).
        // The returned value will be signed and optionally encrypted,
        // and it is stored in a cookie

        // The arguments user, account, profile and isNewUser are only passed the first time
        // this callback is called on a new session, after the user signs in.
        // In subsequent calls, only token will be available.
        async jwt(props) {
            var { token, user, account, profile, isNewUser } = props
            console.log('JWT: props :>> ', props);
            // This user return by provider {} as you mentioned above MY CONTENT {token:}
            if (user) {
                if (user.token) {
                    var token = {
                        token: user.token,
                        name: user.name,
                        email: user.email,
                        shop: user.shop,
                        stripeId: user.stripe_id,
                        billingAddress: user.billingAddress,
                    };
                }
            }
            return token;
        },

        // controls if a user is allowed to sign in.
        // user object is the response returned from the authorize callback
        async signIn(props) {
            const { user, account } = props

            console.log('signIn: props :>> ', props);
            return true;
        },

        // signs out the user
        // user object is the response returned from the authorize callback
        async signOut({ token, session }) {
            console.log('signOUT token :>> ', token);
            console.log('signOUT session :>> ', session);
            return true;
        },
        // session callback is where we specify what will be available on the client with useSession() or getSession().
        // The session callback is called WHENEVER a SESSION IS CHECKED.
        // By default, only a subset of the TOKEN is returned for increased security.
        // If you want to make something available you ADDED TO THE TOKEN through the jwt() callback,
        // you have to explicitly forward it here to make it available to the client.
        async session(props) {
            const { session, token, user } = props;
            console.log('session: props :>> ', props);
            // first time jwt callback is run, user object is available
            if (token) {
                session.user = token;
            }
            //if you want to add user details info
            return session;
        },

        redirect({ url, baseUrl }) {
            if (url.startsWith(baseUrl)) return url;
            // Allows relative callback URLs
            else if (url.startsWith('/')) return new URL(url, baseUrl).toString();
            return baseUrl;
        },
    },

    pages: {
        signIn: 'account/login',
        // signOut: 'account/logout',
        // error: '/auth/error', // Error code `passed` in query string as ?error=
        // verifyRequest: '/auth/verify-request', // (used for check email message)
        // newUser: '/auth/new-user', // New users will be directed here on first sign in (leave the property out if not of interest)
    },
    // events: {
    //     async signIn(message) {
    //         console.log('user sign in EVENT called ;:>> ', message);
    //     },
    //     async signOut(message) {
    //         console.log('user sign out EVENT called ;:>> ', message);
    //     },
    // },
};
export default NextAuth(authOptions);

Please help me, What I am doing wrong?请帮助我,我做错了什么?

I try now since six days to solve that issue.我从六天开始就尝试解决这个问题。 (I copy the question from here ( https://github.com/nextauthjs/next-auth/discussions/5402 ) into Stackoverflow, because on Github, I didn't got any response till today). (我将这里的问题( https://github.com/nextauthjs/next-auth/discussions/5402 )复制到 Stackoverflow 中,因为在 Github 上,直到今天我还没有得到任何回复)。 I hope into the intelligence of the "swarm")我希望进入“蜂群”的智慧)

in jwt callback在 jwt 回调

 return Promise.resolve(token)

in session callback在 session 回调

return Promise.resolve(session)

in authorize在授权

if (res.ok && user) {
      return Promise.resolve(user)
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 HttpClient的get return对于任何其他API始终为null - HttpClient get return always null for any rest API TypeError:无法将 undefined 或 null 转换为对象 | 下一步认证 - TypeError: Cannot convert undefined or null to object | NextAuth 反应 NextAuth 凭据 null 和 Auth 不工作 - React NextAuth credentials null and Auth Not Working Angular - 发布到 .NET 框架 API 的值始终为 null - Angular - Value posted to .NET Framework API is always null 在 Angular 中使用 Rxjs 进行顺序 API 调用,返回 null - Sequential API call using Rxjs in Angular with null return 参数化路径角度5中的空值 - Null Value in Parameterised Routes Angular 5 为什么在将字符串与 null 进行比较时,TS 不会说“此条件将始终返回 'false'”? - Why won't TS say “This condition will always return 'false'” when comparing a string to a null? 由于类型为“MutableRefObject”,此条件将始终返回“false”<string | null> ' 和 'string' 没有重叠。 TS2367</string> - This condition will always return 'false' since the types 'MutableRefObject<string | null>' and 'string' have no overlap. TS2367 使用TypeScript的Express路由的返回类型 - Return type for Express routes with TypeScript Posting a nested object from Angular to Spring REST API is always null - Posting a nested object from Angular to Spring REST API is always null
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM