VKontakteStrategy: verify callback must take 4 or 5 parameters
herenickname opened this issue · comments
Привет!
Я использую твой модуль с @nestjs/passport (это обвязка passport для фреймворка nest).
Вот так выглядит мой обработчик:
import { Strategy, VerifyCallback } from 'passport-vkontakte'
import { PassportStrategy } from '@nestjs/passport'
import { Injectable } from '@nestjs/common'
import { AuthService } from '../auth.service'
@Injectable()
export class VkStrategy extends PassportStrategy(Strategy, 'vk') {
constructor(private authService: AuthService) {
super({
clientID: 1,
clientSecret: '123',
callbackURL: 'https://localhost/auth/vk/callback',
scope: ['status', 'email', 'friends', 'notify']
})
}
async validate (accessToken: string, refreshToken: string, params: string, profile: any, done: VerifyCallback): Promise<any> {
console.log({ accessToken, refreshToken, params, profile })
done(null, profile)
}
}
И после прохождения авторизации в ВК получаю:
[ExceptionsHandler] VKontakteStrategy: verify callback must take 4 or 5 parameters +9700ms
Error: VKontakteStrategy: verify callback must take 4 or 5 parameters
at VkStrategy.passportVerify [as _verify] (/root/backend/node_modules/passport-vkontakte/lib/strategy.js:95:18)
at /root/backend/node_modules/passport-oauth2/lib/strategy.js:200:24
at /root/backend/node_modules/passport-vkontakte/lib/strategy.js:177:7
at passBackControl (/root/backend/node_modules/oauth/lib/oauth2.js:134:9)
at IncomingMessage.<anonymous> (/root/backend/node_modules/oauth/lib/oauth2.js:157:7)
at IncomingMessage.emit (events.js:327:22)
at IncomingMessage.EventEmitter.emit (domain.js:485:12)
at endReadableNT (_stream_readable.js:1224:12)
at processTicksAndRejections (internal/process/task_queues.js:84:21)
Я перешел в node_modules/passport-vkontakte/lib/strategy.js
,
в функции verifyWrapper
поменял var arity = verify.length;
на var arity = 5;
и все заработало, в своем обработчике я получил нужный ответ.
К сожалению, у меня нет возможности продебажить все самостоятельно, но очень надеюсь, что ты выпустишь фикс :)
P.S.: console.log(verify.length)
отдает 0
Без passReqToCallback
невозможно получить state
из query
. Речь идёт про авторизацию без сессии.
Тоже столкнулся с этой ошибкой. @ekifox вы забыли про параметр passReqToCallback
при реализации функции verifyWrapper
.
Без ломания обратной совместимости я сделал #57.
Здесь passReqToCallback
можно не передавать, так как он всегда включен.
@negezor Спасибо за PR! Выглядит вроде разумно, но проверить, к сожалению, не могу. С тех пор, как я удалил свой аккаунт в ВК, приходится при приеме изменений полагаться на показания свидетелей.
Я смерджил свои изменения в master ветке. И установил зависимость с git:
"dependencies": {
"passport": "^0.4.1",
"passport-vkontakte": "https://github.com/negezor/passport-vkontakte"
}
Уже сейчас используется в продакшене, замеченных ошибок не было.
Как мы знаем, если ошибок не было замечено, это не значит, что их нет. :)
Я погрузился в археологию, и выяснил, что все эти непонятные манипуляции были вызваны необходимостью получить емейл пользователя (#13 #15 #16). Другая часто встречающаяся причина претензий к модулю — сбои при отказе авторизации. У меня сложилось впечатление, что изменения последних лет — это перетягивание каната между теми, кто хочет получить емейлы, и теми, кто хочет нормально обрабатывать отказы. Кажется, что починка одного влечет поломку другого, или что-то типа того.
К сожалению, для меня этот модуль уже лет эдак шесть не приносит никакой пользы, а только возлагает ненужную ответственность. Я сам им не пользуюсь, поэтому и не могу поддерживать его в надлежащем состоянии. Он остро нуждается в приведении в соответствие с текущим состоянием экосистемы, окружающей Node, Passport и VK API, особенно в том, что касается тестирования и интеграции.
Я клоню к тому, что был бы безмерно благодарен тому, кто взял бы на себя смелость не только починить модуль под себя, но и протестировать его поведение в других сценариях взаимодействия.
Каким образом передача req
в callback влияет на то что не будет получен email
в params
?
Протестировал с req
со scope: ['email']
и без, а так же без req
со scope: ['email']
и без. В обоих случаях это никак не влияло на результат. Со scope: ['email']
был email
, и без не было.
Здорово, спасибо!
А что, если отменить авторизацию при входе?
Соотвественено получим ошибку, в моём случае кастомный обработчий. Так что это будет в req.query
:
{
error: 'access_denied',
error_reason: 'user_denied',
error_description: 'User denied your request',
state: 'e589d590ec7fb6bf8e4f2bc875fc50c1383f6cf6b0baf17bfbf699185348f8d4'
}
В error: null
, но это стандартное поведение, вот в info
:
{ message: 'User denied your request' }
На дефолтном обработчике кидает на указанные failure действия.
It is good that it can be installed from GitHub directly. But it is better to merge it and publish.
I finally found some time to merge #57 and publish it as 0.5.0
. Sorry for the delay, and thanks for your patience!
Поймал ту же самую ошибку. verifyWrapper конфликтует с тем, как NestJS сам оборачивает функцию верификации. Там callback вызывается через лямбду со spread оператором ((...params: any[]) => callback(...params)
), из-за чего console.log(verify.length)
и отдает 0.
Вот такой код выдаёт ошибку, хотя должен работать. Аналогичный код для других реализаций passport-oauth2 работает.
import { Strategy } from 'passport-vkontakte';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
@Injectable()
export class VkontakteStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
clientID: 123,
clientSecret: '456',
callbackURL: 'http://localhost:3000/auth/vkontakte/callback',
});
}
async validate(req: any, accessToken: string, refreshToken: string, params: string, profile: any): Promise<any> {
return profile;
}
}
Если вместо метода класса использовать аргумент конструктора, то всё работает, хоть и выглядит не так красиво, как хотелось бы:
import { Strategy, VerifyCallback } from 'passport-vkontakte';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
@Injectable()
export class VkontakteStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
clientID: 123,
clientSecret: '456',
callbackURL: 'http://localhost:3000/auth/vkontakte/callback',
},
async function validate(
accessToken: string,
refreshToken: string,
params: string,
profile: any,
done: VerifyCallback,
) {
done(null, profile);
}
);
}
}
В коде passport-oauth2 обработка арности функции выглядит так:
if (self._passReqToCallback) {
var arity = self._verify.length;
if (arity == 6) {
self._verify(req, accessToken, refreshToken, params, profile, verified);
} else { // arity == 5
self._verify(req, accessToken, refreshToken, profile, verified);
}
} else {
var arity = self._verify.length;
if (arity == 5) {
self._verify(accessToken, refreshToken, params, profile, verified);
} else { // arity == 4
self._verify(accessToken, refreshToken, profile, verified);
}
}
Видимо, тут тоже нужен какой-то fallback для случаев, когда arity == 0