Редиректный вход
Кроме встроенного окна есть классический путь OAuth 2.0: браузер уходит на нашу страницу входа, пользователь входит и возвращается к вам с кодом. Виджет для этого не нужен.
Когда что выбирать
Заголовок раздела «Когда что выбирать»- Встроенное окно (виджет) — пользователь не покидает вашу страницу,
окно рисуется поверх. Нужен
sdk.jsи разрешённые origin. - Authorization Code Flow (редиректный) — эта страница; полный переход на нашу страницу и обратно. Проще на серверных приложениях без SPA, работает без виджета входа на странице.
Обе формы кончаются одинаково: у вас на бэкенде код авторизации, который вы меняете на токен.
Как идёт вход
Заголовок раздела «Как идёт вход»-
Отправьте пользователя на
/oauth/authorizeс параметрами запроса. Сгенерируйте PKCE,stateиnonce, сохраните их в сессии. -
Пользователь входит на нашей странице и возвращается на ваш
redirect_uriсcodeиstateв query. -
Обменяйте код на токен и проверьте
id_token— тем же кодом, что в примере бэкенда.
import * as client from 'openid-client';
import { ISSUER, CLIENT_ID, CLIENT_SECRET, REDIRECT_URI } from './config.js';
const config = await client.discovery(new URL(ISSUER), CLIENT_ID, CLIENT_SECRET);
const pending = new Map(); // в бою — сессия/Redis, ключ по вашему session id
// Шаг 1. Собрать URL авторизации и увести туда браузер.
export async function buildLoginUrl() {
const codeVerifier = client.randomPKCECodeVerifier();
const codeChallenge = await client.calculatePKCECodeChallenge(codeVerifier);
const state = client.randomState();
const nonce = client.randomNonce();
const url = client.buildAuthorizationUrl(config, {
redirect_uri: REDIRECT_URI,
scope: 'openid profile email',
state,
nonce,
code_challenge: codeChallenge,
code_challenge_method: 'S256',
});
pending.set(state, { codeVerifier, nonce });
return url.href; // сделайте res.redirect(url.href)
}
// Шаг 2. Обработать возврат: адрес запроса содержит code и state.
export async function handleCallback(incomingUrl) {
const url = new URL(incomingUrl);
const draft = pending.get(url.searchParams.get('state'));
const tokens = await client.authorizationCodeGrant(config, url, {
pkceCodeVerifier: draft.codeVerifier,
expectedState: url.searchParams.get('state'),
expectedNonce: draft.nonce,
idTokenExpected: true,
});
return tokens.claims().sub; // парный идентификатор пользователя
}
response_type=code buildAuthorizationUrl проставит сам; code_challenge мы считаем из
code_verifier и передаём явно. Адреса эндпоинтов библиотека берёт из discovery —
хардкодить их не нужно.
Дальше — профиль пользователя и коды ошибок.