Как устранить ошибку «invalid code_challenge» в ВК oauth2 при запросе токена?

Я осуществляю авторизацию на сайте с помощью VK ID, не используя SDK и выполняя обмен кода на серверной стороне. <br/> Первую часть авторизации, включающую получение code и device_id, я успешно завершил. <br/> Однако, когда я пытаюсь обратиться к методу <b><a href="https://id.vk.com/oauth2/auth" rel="nofollow">https://id.vk.com/oauth2/auth</a></b>, возникает ошибка: <br/> <blockquote><br/>
message: <br/>
error:  "invalid_request"<br/>
error_description: "invalid code_challenge"<br/>
</blockquote> <br/> В данном запросе я указываю URL и формирую параметры: <br/> <pre><code class="javascript">const vk = this.configService.get('vk');
    const app = this.configService.get('app');
    const { code_verifier, code_challenge } = getVerifier();

    const url = 'https://id.vk.com/oauth2/auth';
    const params = {
      grant_type: 'authorization_code',
      code,
      device_id,
      client_id: vk.appId,
      redirect_uri: app.frontend,
      code_challenge_method: 's256',
      code_verifier,
      code_challenge,
    };</code></pre> <br/> Затем я выполняю запрос: <br/> <pre><code class="javascript">import * as qs from 'qs';

    const data = await this.httpService
      .post(url, qs.stringify(params), {
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
      })
      .toPromise();</code></pre> <br/> <br/> На этом этапе я формирую code_verifier и code_challenge следующим образом: <br/> <pre><code class="javascript">import { randomBytes, createHash } from 'crypto';
import base64url from 'base64url';

export function getVerifier() {
  const code_verifier = generateRandomString(45);
  const code_challenge = getHash(code_verifier);

  return {
    code_verifier,
    code_challenge,
  };
}
function generateRandomString(length = 32) {
  const str 
  • 15 сентября 2024 г. 10:23
Ответы на вопрос 2
Ошибка «invalid code_challenge» при выполнении запроса на получение токена в ВК OAuth2 может возникать по нескольким причинам. Давайте разберемся, что может быть не так в вашем коде и какие шаги можно предпринять для устранения этой ошибки.

### Основные причины возникновения ошибки

1. **Неверное вычисление `code_challenge`**: Убедитесь, что хэш, который вы вычисляете для `code_challenge`, корректен. Использование алгоритма SHA256 и преобразование в формат Base64 URL должны выполняться правильно.

2. **Совпадение `code_verifier` и `code_challenge`**: `code_challenge` должен быть вычислён на основе вашего `code_verifier`. Если вы используете `code_verifier` при запросе токена, убедитесь, что он совпадает с тем, что вы использовали при запросе кода.

3. **Неверный `code_challenge_method`**: Проверьте, правильно ли вы указали значение `code_challenge_method`. Если вы используете SHA256, убедитесь, что он действительно установлен в `'s256'`.

4. **Синтаксические ошибки или неправильный формат данных**: Проверьте, правильно ли вы формируете параметры для запроса. Попробуйте заменить `qs.stringify(params)` на `new URLSearchParams(params).toString()` или на что-то аналогичное.

### Пример исправленного кода

Вот пример, как можно правильно генерировать `code_verifier` и `code_challenge`, а также выполнять запрос:

```javascript
import { randomBytes, createHash } from 'crypto';
import axios from 'axios';

// Функция для генерации code_verifier и code_challenge
export function getVerifier() {
  const code_verifier = generateRandomString(45);
  const code_challenge = getHash(code_verifier);
  return {
    code_verifier,
    code_challenge,
  };
}

function generateRandomString(length) {
  return randomBytes(Math.ceil(length / 2))
    .toString('hex')
    .slice(0, length);
}

function getHash(code_verifier) {
  const hash = createHash('sha256').update(code_verifier).digest('base64url');
  return hash;
}

// Ваш запрос на получение токена
async function getToken(code, device_id) {
  const { code_verifier, code_challenge } = getVerifier();
  
  const url = 'https://id.vk.com/oauth2/access_token';
  const params = {
    grant_type: 'authorization_code',
    code,
    device_id,
    client_id: vk.appId,
    redirect_uri: app.frontend,
    code_challenge_method: 's256',
    code_verifier,
    code_challenge,
  };

  const response = await axios.post(url, new URLSearchParams(params).toString(), {
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
  });

  return response.data;
}
```

### Рекомендации по отладке

1. **Логи**: Добавьте логи перед запросом, чтобы проверять значения `code_verifier` и `code_challenge`. Убедитесь, что они соответствуют друг другу.

2. **Проверка запросов**: Используйте инструменты, такие как Postman, чтобы проверить запросы и убедиться, что все параметры передаются правильно.

3. **Документация**: Изучите официальную документацию ВК по OAuth2, чтобы удостовериться, что вы не пропустили какие-либо критически важные аспекты.

Если после всех этих шагов проблема остается нерешенной, стоит обратиться к сообществу разработчиков ВК или поддержке для получения дополнительной помощи.
Не вижу параметра state (32 bytes), который генерируется на самом первом этапе. Его вроде как тоже надо передавать.
Похожие вопросы