stevebest / passport-vkontakte

VK.com authentication strategy for Passport and Node.js

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

commented

Без passReqToCallback невозможно получить state из query. Речь идёт про авторизацию без сессии.

Тоже столкнулся с этой ошибкой. @ekifox вы забыли про параметр passReqToCallback при реализации функции verifyWrapper.

commented

Без ломания обратной совместимости я сделал #57.
Здесь passReqToCallback можно не передавать, так как он всегда включен.

@negezor Спасибо за PR! Выглядит вроде разумно, но проверить, к сожалению, не могу. С тех пор, как я удалил свой аккаунт в ВК, приходится при приеме изменений полагаться на показания свидетелей.

commented

Я смерджил свои изменения в master ветке. И установил зависимость с git:

"dependencies": {
	"passport": "^0.4.1",
	"passport-vkontakte": "https://github.com/negezor/passport-vkontakte"
}

Уже сейчас используется в продакшене, замеченных ошибок не было.

Как мы знаем, если ошибок не было замечено, это не значит, что их нет. :)

Я погрузился в археологию, и выяснил, что все эти непонятные манипуляции были вызваны необходимостью получить емейл пользователя (#13 #15 #16). Другая часто встречающаяся причина претензий к модулю — сбои при отказе авторизации. У меня сложилось впечатление, что изменения последних лет — это перетягивание каната между теми, кто хочет получить емейлы, и теми, кто хочет нормально обрабатывать отказы. Кажется, что починка одного влечет поломку другого, или что-то типа того.

К сожалению, для меня этот модуль уже лет эдак шесть не приносит никакой пользы, а только возлагает ненужную ответственность. Я сам им не пользуюсь, поэтому и не могу поддерживать его в надлежащем состоянии. Он остро нуждается в приведении в соответствие с текущим состоянием экосистемы, окружающей Node, Passport и VK API, особенно в том, что касается тестирования и интеграции.

Я клоню к тому, что был бы безмерно благодарен тому, кто взял бы на себя смелость не только починить модуль под себя, но и протестировать его поведение в других сценариях взаимодействия.

commented

Каким образом передача req в callback влияет на то что не будет получен email в params?

commented

Протестировал с req со scope: ['email'] и без, а так же без req со scope: ['email'] и без. В обоих случаях это никак не влияло на результат. Со scope: ['email'] был email, и без не было.

Здорово, спасибо!

А что, если отменить авторизацию при входе?

commented

Соотвественено получим ошибку, в моём случае кастомный обработчий. Так что это будет в 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 действия.

commented

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