typescript - Konstigt problem med NextJS och NextAuth
Jag har följande autentiseringsinställning för NextJS med en egen tokenleverantör/tjänst. Jag följer denna guide och min kod ser ut så här
async function refreshAccessToken(authToken: AuthToken) {
try {
const tokenResponse = await AuthApi.refreshToken(authToken.refresh_token);
return new AuthToken(
tokenResponse.access_token,
tokenResponse.token_type,
tokenResponse.expires_in,
tokenResponse.refresh_token
);
} catch (error) {
return new AuthToken(
authToken?.access_token,
authToken?.token_type,
authToken?.expires_in,
authToken?.refresh_token,
"An error occurred whilst refreshing token"
);
}
}
export const authOptions: NextAuthOptions = {
providers: [
CredentialsProvider({
name: 'credentials',
credentials: {
username: { label: 'Username', type: 'text' },
password: { label: 'Password', type: 'password' }
},
authorize: async (credentials) => {
try {
if (!credentials) {
return null;
}
const { username, password } = credentials;
const authToken = await AuthApi.login(username, password);
const session: Session = new Session(authToken, null);
if (authToken != null && authToken.error == '') {
const userDetails = await AuthApi.getUserDetails(authToken.access_token);
if (userDetails != null) {
session.user = userDetails;
}
}
console.log(session);
return session as any;
} catch (error) {
console.error(error);
throw error;
}
}
})
],
session: {
strategy: 'jwt',
maxAge: Constants.AccessTokenLifetimeSeconds
},
secret: process.env.APP_SECRET,
jwt: {
secret: process.env.APP_SECRET,
maxAge: Constants.AccessTokenLifetimeSeconds
},
pages: { signIn: '/login' },
callbacks: {
jwt: async ({ user }: any) => {
let token: AuthToken | null = null;
if (user != null && user.token != null) {
token = user.token;
const clock = new Clock();
if (user.token.expiry_date_time < clock.nowUtc()) {
token = await refreshAccessToken(user.token);
}
}
console.log("OO", user, token);
return token;
},
session: async ({ session, user, token }) => {
//session.user = user;
//session.token = token;
return session;
}
}
};
export default NextAuth(authOptions);
De relevanta klasserna är
export default class AuthToken implements IAuthToken {
public expiry_date_time: Date;
constructor(
public access_token: string,
public token_type: string,
public expires_in: number,
public refresh_token: string,
public error: string = ""
) {
const clock = new Clock();
this.expiry_date_time = new Date(
clock.nowUtc().getTime() + (this.expires_in - 60) * 1000
);
}
}
och
export default class Session implements ISession {
constructor(
public token: AuthToken | null,
public user: UserDetails | null
) { }
}
Men i återuppringningen får jag följande fel
fel - TypeError: JWT Claims Set MÅSTE vara ett objekt vid new ProduceJWT (C:\VRPM\Repos\portal\node_modules\jose\dist\node\cjs\jwt\produce.js:10:19) vid new EncryptJWT (C:\VRPM\Repos\portal\node_modules\jose\dist\node\cjs\jwt\encrypt.js:7:1) vid Object.encode (C:\VRPM\Repos\portal\node_modules\next-auth\jwt\index.js:49:16) vid async Object.callback (C:\VRPM\Repos\portal\node_modules\next-auth\core\routes\callback.js:429:22) vid async NextAuthHandler (C:\VRPM\Repos\portal\node_modules\next-auth\core\index.js:295:28) vid async NextAuthNextHandler (C:\VRPM\Repos\portal\node_modules\next-auth\next\index. js:23:19) vid async C:\VRPM\Repos\portal\node_modules\next-auth\next\index.js:59:32 vid async Object.apiResolver (C:\VRPM\Repos\portal\node_modules\next\dist\server\api-utils\node.js:184:9) vid async DevServer.runApi (C:\VRPM\Repos\portal\node_modules\next\dist\server\next-server.js:403:9) vid async Object.fn (C:\VRPM\Repos\portal\node_modules\next\dist\server\base-server.js:493:37) { sida: '/api/auth/[...nextauth]'
och jag har ingen aning om hur man löser det eftersom datan jag returnerar är ett objekt:
token: AuthToken {
access_token: '...Nn_ooqUtFvdfh53k',
token_type: 'Bearer',
expires_in: 86400,
refresh_token: '...ZddCpNTc',
error: '',
expiry_date_time: 2023-02-09T19:29:15.307Z
}
Kan någon säga var jag gör fel här?
Jag har upptäckt att om jag återvänder
return {
token: token
};
felet försvinner, men vad är det rätta sättet att göra detta?
Svar #1
En sak jag märkte i alla exempel på next-auth-implementationer är att authorize()-funktionen är tänkt att returnera användarens detaljer (id, e-post osv.), inte ett objekt som innehåller token och user-detaljer.
Det verkar som att du felaktigt förlitar dig på token från detta användarobjekt under jwt() callback när det borde hämtas från callbacken själv.
Jag har satt upp en Next.js + next-auth sandlåda och jag går igenom din kod nu.
Istället för detta:
jwt: async ({ user }: any) => {
let token = user.token;
if (user != null && user.token != null) {
const clock = new Clock();
if (user.token.expiry_date_time < clock.nowUtc()) {
token = await refreshAccessToken(user.token);
}
}
return token;
},Gör detta:
jwt: async ({ token, user }: any) => {
// at login, save user token to jwt token
if (user) {
token.accessToken = user.token.accessToken;
token.accessTokenExpiry = user.token.accessTokenExpiry;
token.refreshToken = user.token.refreshToken;
}
// check if we should refresh the token
const shouldRefreshTime = user.token.expiry_date_time < clock.nowUtc();
if (shouldRefreshTime) {
token = refreshAccessToken(token);
}
// return the token
return Promise.resolve(token);
},Även inne i authorize():
Ändra detta från:
return session;Till:
return session.user;
Viktig förändring i jwt()-callbacken:
jwt: async ({ user }: any) => {Till:
jwt: async ({ token, user }: any) => {Dessutom bör din session()-callback inte returnera refreshToken. De nämner denna säkerhetsrisk i sin dokumentation.
Callbacken session() bör uppdateras.
Från detta:
session: async ({ session, user, token }) => {
//session.user = user;
//session.token = token;
return session; <--- entire token (refreshToken, etc) being returned...
}
Till detta:
session: async ({ session, user, token }) => {
// Send properties to the client, like an access_token and user from a provider.
session.accessToken = token.accessToken;
session.user = user;
return session
}
