# VK ID API: Сделать вход через VK ID на сайте

Сценарий из mcp-everything. Актуально на 2026-10-24.

Кнопка «Войти через VK ID»: пользователь входит без пароля, сайт получает его имя, аватар и, если разрешено, телефон и email.

1. Создай приложение в кабинете VK ID и укажи redirect_uri. Для каждого входа сгенерируй code_verifier (43–128 символов), code_challenge — base64url от SHA-256 code_verifier — и state (не меньше 32 символов). Сохрани code_verifier и state в сессии.
2. Отправь пользователя на GET /authorize с response_type = code, client_id, redirect_uri, state, code_challenge, code_challenge_method = S256 и scope (по умолчанию vkid.personal_info; телефон и email — отдельные права). provider = ok_ru или mail_ru открывает вход через Одноклассники или Mail.ru.
3. На redirect_uri придут code (живёт 10 минут), device_id и state. Сравни state с сохранённым — иначе отклони запрос.
4. Обменяй код на токены: POST /oauth2/auth с grant_type = authorization_code, code, code_verifier, redirect_uri, client_id, device_id и state (form-urlencoded). В ответе access_token (живёт час), refresh_token, id_token и user_id.
5. Получи профиль: POST /oauth2/user_info с client_id и access_token — user_id, first_name, last_name, avatar, а также phone и email, если они есть в правах токена.

```python
import base64, hashlib, secrets, requests
from urllib.parse import urlencode

CLIENT_ID, REDIRECT = "<client_id>", "https://example.ru/auth/vk/callback"

# 1–2. Начало входа
verifier = secrets.token_urlsafe(64)[:96]
challenge = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode()).digest()).rstrip(b"=").decode()
state = secrets.token_urlsafe(32)
session.update(vk_verifier=verifier, vk_state=state)
login_url = "https://id.vk.ru/authorize?" + urlencode({
    "response_type": "code", "client_id": CLIENT_ID, "redirect_uri": REDIRECT, "state": state,
    "code_challenge": challenge, "code_challenge_method": "S256", "scope": "vkid.personal_info email"})

# 3–5. Обработчик redirect_uri
def callback(args):
    if args["state"] != session["vk_state"]:
        raise PermissionError("state mismatch")
    tokens = requests.post("https://id.vk.ru/oauth2/auth", data={
        "grant_type": "authorization_code", "code": args["code"], "code_verifier": session["vk_verifier"],
        "redirect_uri": REDIRECT, "client_id": CLIENT_ID, "device_id": args["device_id"], "state": args["state"],
    }).json()
    user = requests.post("https://id.vk.ru/oauth2/user_info",
                         data={"client_id": CLIENT_ID, "access_token": tokens["access_token"]}).json()["user"]
    return tokens, user
```

## Базовые URL

- Боевой: `https://id.vk.ru`

## Авторизация

Тип: OAuth 2.0

Пользователь проходит `GET /authorize` и возвращается на `redirect_uri` с `code` и `device_id`; код обменивается на `access_token`, `refresh_token` и `id_token` методом `POST /oauth2/auth`. PKCE обязателен: `code_verifier` — 43–128 символов, `code_challenge` — base64url от SHA-256 строки `code_verifier`. Access token действует 1 час.

## Методы сценария

### GET /authorize — Код подтверждения и форма разрешений

При успехе — редирект на `redirect_uri` с `code` (живёт 10 минут), `device_id` и `state`; при ошибке — JSON с `error` и `error_description`.

| Параметр | Где | Тип | Обяз. | Описание |
|---|---|---|---|---|
| `response_type` | query | string | да | code |
| `client_id` | query | string | да | ID приложения |
| `redirect_uri` | query | string | да | Адрес возврата |
| `state` | query | string | да | Случайная строка, не меньше 32 символов |
| `code_challenge` | query | string | да | Хэш code_verifier |
| `code_challenge_method` | query | string | да | S256 |
| `scope` | query | string | нет | Права, по умолчанию vkid.personal_info |
| `prompt` | query | string | нет | none, login, consent |
| `provider` | query | string | нет | vkid, ok_ru, mail_ru |
| `lang_id` | query | string | нет | Язык интерфейса |
| `scheme` | query | string | нет | light или dark |

### POST /oauth2/auth — Обмен кода на токены

`grant_type=authorization_code`. В ответе: `access_token`, `refresh_token`, `id_token`, `token_type`, `expires_in`, `user_id`, `state`, `scope`.

| Параметр | Где | Тип | Обяз. | Описание |
|---|---|---|---|---|
| `grant_type` | body | string | да | authorization_code |
| `code` | body | string | да | Код из редиректа |
| `code_verifier` | body | string | да | Исходная строка PKCE |
| `redirect_uri` | body | string | да | Тот же адрес возврата |
| `client_id` | body | string | да | ID приложения |
| `device_id` | body | string | да | device_id из редиректа |
| `state` | body | string | да | Строка state |
| `service_token` | body | string | нет | Для конфиденциальных приложений |
| `ip` | body | string | нет | IP пользователя |

### POST /oauth2/auth — Обновление токенов

`grant_type=refresh_token`. Возвращает новую пару `access_token` и `refresh_token` (без `id_token`); старая пара инвалидируется.

| Параметр | Где | Тип | Обяз. | Описание |
|---|---|---|---|---|
| `grant_type` | body | string | да | refresh_token |
| `refresh_token` | body | string | да | Refresh-токен |
| `client_id` | body | string | да | ID приложения |
| `device_id` | body | string | да | ID устройства |
| `state` | body | string | да | Строка state |
| `scope` | body | string | нет | Права |
| `service_token` | body | string | нет | Для конфиденциальных приложений |
| `ip` | body | string | нет | IP пользователя |

### POST /oauth2/user_info — Данные пользователя (без маскирования)

В `user`: `user_id`, `first_name`, `last_name`, `avatar`, `sex`, `verified`, `birthday`; `phone` и `email` — если в токене есть соответствующие права.

| Параметр | Где | Тип | Обяз. | Описание |
|---|---|---|---|---|
| `client_id` | body | string | да | ID приложения |
| `access_token` | body | string | да | Access-токен |

## Нюансы

- Из браузера токен передаётся только в теле запроса с `Content-Type: application/x-www-form-urlencoded`: заголовок `Authorization` и JSON вызывают ошибку preflight CORS.
- Неверный `grant_type` или `response_type` даёт ответ 404.
- После обновления токенов старая пара `access_token`/`refresh_token` перестаёт работать.
- `public_info` работает с `id_token` и отдаёт маскированные данные, `user_info` — с `access_token` и полные данные.

## Другие сценарии

- [Продлевать сессию без повторного входа](https://mcp-everything.ru/integrations/vk-id/scenarios/prodlevat-sessiyu-bez-povtornogo-vhoda.md)
- [Показать «Продолжить как …» и выйти из аккаунта](https://mcp-everything.ru/integrations/vk-id/scenarios/pokazat-prodolzhit-kak-i-vyyti.md)

---

Если этого недостаточно — официальная документация: https://id.vk.ru/about/business/go/docs/ru/vkid/latest/vk-id/connection/api-description
Вся интеграция одним документом: https://mcp-everything.ru/integrations/vk-id.md
