[英]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
getServerSideProps
function I call my next/API endopoint via a Axios在getServerSideProps
function 我通过 Axios 调用我的下一个/API 端点Bearer token
to the next/API call我将Bearer token
传递给下一个/API 调用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.