Что нужно
Запросите у X-GRK регистрацию приложения. Вам выдадут:
app_name— публичный идентификатор клиента (client_id)app_secret— секрет только для вашего бэкенда (client_secret)- белый список
redirect_uri— адреса, на которые можно вернуть пользователя
Секрет не кладите в JavaScript, мобильное приложение и публичный репозиторий.
Храните в переменных окружения сервера. Чужой redirect_uri сервер отклонит.
Базовый URL (issuer): https://id.x-grk.com
Discovery: /.well-known/openid-configuration
Как это работает
- Пользователь нажимает «Войти через X-GRK ID» на вашем сайте.
- Браузер уходит на X-GRK ID, входит или регистрируется, подтверждает доступ.
- Вас возвращают на
redirect_uri?code=...&state=.... - Ваш сервер проверяет
stateи обмениваетcodeна токены, предъявляяapp_secret. - По access-токену запрашиваете профиль.
Пароль, капча и двухфакторка остаются на стороне X-GRK ID. Вам этого реализовывать не нужно.
Кнопка входа
Сгенерируйте случайный state, сохраните его в своей сессии, затем редирект:
https://id.x-grk.com/oauth/authorize ?response_type=code &client_id=ВАШ_APP_NAME &redirect_uri=https://ваш.сайт/callback &scope=openid%20profile%20email &state=случайная-строка
Или SDK (секрет сюда не передаётся):
<script src="https://id.x-grk.com/static/sdk/xgrk-id.js"></script>
<div id="xgrk-login"></div>
<script>
XGrkID.init({
issuer: "https://id.x-grk.com",
appName: "ВАШ_APP_NAME",
redirectUri: "https://ваш.сайт/callback",
display: "redirect" // или "popup"
}).renderButton("#xgrk-login");
</script>
Для popup страница callback должна закрыть окно и обновить window.opener.
Параметры authorize:
| Параметр | Нужен | |
|---|---|---|
response_type | да | только code |
client_id | да | ваш app_name |
redirect_uri | да | точно как в заявке, байт в байт |
scope | нет | по умолчанию openid profile email |
state | да | защита от CSRF, вернётся как есть |
nonce | нет | попадёт в id_token |
display | нет | page или popup |
Scope: openid — идентификатор, profile — имя и аватар, email — почта.
Обмен кода
Только с вашего сервера на сервер X-GRK ID.
POST https://id.x-grk.com/oauth/token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code &code=... &redirect_uri=https://ваш.сайт/callback &client_id=ВАШ_APP_NAME &client_secret=ВАШ_APP_SECRET
Можно те же поля как app_name / app_secret. redirect_uri должен совпасть с тем, что уходил в authorize.
Ответ:
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "eyJ...",
"id_token": "eyJ...",
"scope": "openid profile email"
}
Код одноразовый и живёт несколько минут. Повторный обмен того же кода не пройдёт.
Профиль
GET https://id.x-grk.com/oauth/userinfo Authorization: Bearer ACCESS_TOKEN
{
"sub": "XGRK-7K3N2P9Q1M",
"name": "Анна",
"preferred_username": "XGRK-7K3N2P9Q1M",
"picture": "https://id.x-grk.com/avatar/XGRK-7K3N2P9Q1M",
"email": "user@mail.test",
"email_verified": false
}
Стабильный ключ пользователя — sub (X-GRK ID). Почта не подтверждается.
picture и name есть при scope profile.
Минимальный Flask-колбэк:
@app.get("/callback")
def callback():
if request.args.get("state") != session.get("state"):
abort(400)
token = exchange_code(request.args["code"]) # POST /oauth/token с app_secret
profile = userinfo(token["access_token"]) # GET /oauth/userinfo
session["user"] = profile["sub"]
return redirect("/")
Обновление и отзыв
POST /oauth/token grant_type=refresh_token &refresh_token=... &client_id=... &client_secret=...
Старый refresh при этом отзывается. Access живёт 1 час, refresh — 30 дней либо до отзыва пользователем в кабинете.
POST /oauth/revoke token=... client_id=... client_secret=...
Ошибки
На redirect: access_denied (пользователь отказал), invalid_scope, unsupported_response_type.
На токене: 401 invalid_client (неверный app_name/secret), 400 invalid_grant (код истёк, уже использован, не совпал redirect_uri).
Неизвестный клиент или чужой redirect не уводят браузер на ваш домен — это защита от open redirect.
PKCE
Для SPA или мобильного клиента добавьте code_challenge (S256) в authorize
и code_verifier в token. Если challenge был задан, без верного verifier обмен не пройдёт.
Публичный клиент всё равно не должен держать app_secret в браузере:
обмен кода делайте через свой бэкенд.