У меня есть следующая настройка аутентификации для NextJS с использованием настраиваемого поставщика/службы токенов. Я следую этому руководству, и мой код выглядит так:
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.info(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.info("OO", user, token);
return token;
},
session: async ({ session, user, token }) => {
//session.user = user;
//session.token = token;
return session;
}
}
};
export default NextAuth(authOptions);
Соответствующие классы
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
);
}
}
и
export default class Session implements ISession {
constructor(
public token: AuthToken | null,
public user: UserDetails | null
) { }
}
Но в обратном вызове я получаю следующую ошибку
ошибка - TypeError: набор утверждений JWT ДОЛЖЕН быть объектом в новом ProduceJWT (C:\VRPM\Repos\portal\node_modules\jose\dist\node\cjs\jwt\produce.js:10:19) в новом EncryptJWT (C:\VRPM\Repos\portal\node_modules\jose\dist\node\cjs\jwt\encrypt.js:7:1) в Object.encode (C:\VRPM\Repos\portal\node_modules\next-auth\jwt\index.js:49:16) в асинхронном Object.callback (C:\VRPM\Repos\portal\node_modules\next-auth\core\routes\callback.js:429:22) в асинхронном NextAuthHandler (C:\VRPM\Repos\portal\node_modules\next-auth\core\index.js:295:28) в асинхронном NextAuthNextHandler (C:\VRPM\Repos\portal\node_modules\next-auth\next\index.js:23:19) в асинхронном режиме C:\VRPM\Repos\portal\node_modules\next-auth\next\index.js:59:32 в асинхронном Object.apiResolver (C:\VRPM\Repos\portal\node_modules\next\dist\server\api-utils\node.js:184:9) в асинхронном DevServer.runApi (C:\VRPM\Repos\portal\node_modules\next\dist\server\next-server.js:403:9) в асинхронном Object.fn (C:\VRPM\Repos\portal\node_modules\next\dist\server\base-server.js:493:37) { страница: '/api/auth/[...nextauth]'
и я понятия не имею, как разрешить, поскольку данные, которые я возвращаю, являются объектом:
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
}
Может ли кто-нибудь сказать мне, где я ошибаюсь?
Я обнаружил, что если я вернусь
return {
token: token
};
ошибка уходит, но как правильно это сделать?
Одна вещь, которую я заметил во всех примерах реализации next-auth, заключается в том, что функция authorize()
предназначена для возврата сведений о пользователе (идентификатор, адрес электронной почты и т. д.), а не объект, содержащий сведения token
и user
.
Похоже, вы неправильно полагаетесь на токен из этого пользовательского объекта при обратном вызове jwt()
, когда он должен быть получен из самого обратного вызова.
Я настроил песочницу Next.js
+ next-auth
и сейчас просматриваю ваш код.
Вместо этого:
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;
},
Сделай это:
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);
},
Также внутри authorize()
:
Измените это с:
return session;
К:
return session.user
;
Важное изменение в обратном вызове jwt()
:
jwt: async ({ user }: any) => {
К:
jwt: async ({ token, user }: any) => {
Кроме того, ваш обратный вызов session()
не должен возвращать refreshToken
. Они упоминают об этой проблеме безопасности в своих документах.
Обратный вызов session()
должен быть обновлен.
Из этого:
session: async ({ session, user, token }) => {
//session.user = user;
//session.token = token;
return session; <--- entire token (refreshToken, etc) being returned...
}
К этому:
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
}
К сожалению, это не решает того, что происходит.
Хороший момент - я злоупотребляю !==
из-за того, что пропустил эту маленькую деталь при обзоре. Обновление моего ответа - я думаю, что нашел проблему.
Привет, Уэс, спасибо за это и за ваше время, меня не было, извините за задержку и т. д. Рад принять это. Я внес некоторые изменения, но я все еще обеспокоен, есть ли шанс, что вы сможете взглянуть на это - вопрос проверки кода, основанный на моей окончательной реализации? codereview.stackexchange.com/questions/283281/…
Спасибо за ваше время... Просто для вашего сведения - чек
!= null
проверяет какundefined
, так иnull
. Сейчас прочитаю ваш ответ...