Потоки OAuth на фронтенде
Реализуйте поток кода авторизации с PKCE в SPA, обменивайте код на токены, безопасно храните токены доступа и избегайте неявного потока
«Потоки OAuth на фронтенде» — бесплатный урок Frontend Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Frontend Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Frontend Academy содержит 4 уроков всего.
Что такое OAuth
OAuth 2.0 — это протокол делегирования: ваше приложение получает разрешение действовать от имени пользователя в стороннем сервисе (Google, GitHub и т. д.), никогда не видя пароль пользователя. Он используется для вариантов «Войти через Google», приложений OAuth для GitHub и большинства современных систем аутентификации.
Основные роли
Владелец ресурса: пользователь. Клиент: ваше приложение. Сервер авторизации: поставщик OAuth (Google, Auth0). Сервер ресурсов: API, защищённый token доступа.
Поток кода авторизации
Стандартный безопасный поток для приложений на сервере: 1) Перенаправьте пользователя на страницу входа поставщика. 2) Пользователь входит в систему и даёт согласие. 3) Поставщик перенаправляет его обратно с кодом. 4) Сервер обменивает код на token доступа (секрет, доступный только серверу).
Зачем нужен PKCE в SPA
SPA не могут хранить секрет клиента, поскольку он оказался бы в браузерной сборке. PKCE (подтверждение ключа для обмена кодом) заменяет секрет одноразовыми verifier и challenge для каждого запроса. Теперь это стандарт для всех общедоступных клиентов OAuth.
Поток PKCE по шагам
1) Создайте случайный code_verifier. 2) Вычислите его хеш SHA-256 = code_challenge. 3) Перенаправьте пользователя на /authorize с challenge. 4) После согласия получите код обратно. 5) Обменяйте код + verifier на token доступа. Поставщик проверяет, что challenge = hash(verifier).
Создание verifier и challenge
Verifier — это случайная строка, а challenge — её значение SHA-256 в кодировке base64 для URL.
function base64url(arr) {
return btoa(String.fromCharCode(...arr))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
async function generatePKCE() {
const arr = new Uint8Array(32);
crypto.getRandomValues(arr);
const verifier = base64url(arr);
const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));
const challenge = base64url(new Uint8Array(hash));
return { verifier, challenge };
}Перенаправление к поставщику
Сформируйте URL авторизации с challenge, параметром state (защита от CSRF) и областями действия.
const { verifier, challenge } = await generatePKCE();
const state = crypto.randomUUID();
sessionStorage.setItem('pkce_verifier', verifier);
sessionStorage.setItem('oauth_state', state);
const url = new URL('https://accounts.google.com/o/oauth2/v2/auth');
url.searchParams.set('client_id', CLIENT_ID);
url.searchParams.set('redirect_uri', `${origin}/auth/callback`);
url.searchParams.set('response_type', 'code');
url.searchParams.set('scope', 'openid email profile');
url.searchParams.set('code_challenge', challenge);
url.searchParams.set('code_challenge_method', 'S256');
url.searchParams.set('state', state);
window.location.href = url.toString();Обработка обратного вызова
Поставщик перенаправляет пользователя на ваш URL обратного вызова с параметрами ?code=...&state=.... Проверьте state для предотвращения CSRF, затем обменяйте код.
// /auth/callback page:
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const returnedState = params.get('state');
const expected = sessionStorage.getItem('oauth_state');
if (returnedState !== expected) throw new Error('Bad state');
const verifier = sessionStorage.getItem('pkce_verifier');
const tokens = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
client_id: CLIENT_ID,
redirect_uri: `${origin}/auth/callback`,
code_verifier: verifier
})
}).then(r => r.json());
// tokens: { access_token, id_token, refresh_token, expires_in }Безопасное хранение токенов
Лучшая практика: не храните tokens доступа в localStorage, откуда их можно украсть через XSS. Варианты: 1) Только в памяти (очищается при обновлении страницы — требуется повторная аутентификация). 2) Файл cookie с HttpOnly (устанавливается вашим сервером после потока PKCE и недоступен для кражи через JS).
Избегайте неявного потока
Старый неявный поток возвращает token доступа непосредственно во фрагменте URL. Он объявлен устаревшим в OAuth 2.0 Security BCP, поскольку tokens попадают в историю браузера и рефереры. Всегда используйте Authorization Code + PKCE.
Tokens обновления
Tokens доступа истекают, обычно через 1 час. Долгоживущий token обновления незаметно получает новые tokens доступа. В SPA tokens обновления всё чаще передаются через файлы cookie с HttpOnly — никогда не используйте для них localStorage.
ID token и tokens доступа
ID token (OpenID Connect): JWT, подтверждающий личность пользователя. Access token: непрозрачный token или JWT, используемый для вызова API. Перед доверием проверьте подпись и утверждения ID token (iss, aud, exp, nonce).
Библиотеки аутентификации
Не создавайте собственную реализацию. Используйте: oidc-client-ts для низкоуровневого OIDC, auth0/spa-js для Auth0, @clerk/clerk-react для Clerk, NextAuth.js/Auth.js для Next, Nuxt-auth для Nuxt. Они обрабатывают PKCE, обновление и хранение.
Быстрая проверка
Почему одностраничные приложения должны использовать PKCE (подтверждение ключа для обмена кодом) вместе с потоком кода авторизации, а не только базовый поток кода авторизации?
Итоги: OAuth во фронтенде
Authorization Code + PKCE — современный стандарт для SPA. Создавайте verifier + challenge S256 для каждого запроса. Перенаправляйте пользователя на /authorize с challenge + state. Проверяйте state при обратном вызове. Обменивайте код + verifier на tokens. Никогда не используйте неявный поток. Для хранения tokens предпочтительнее файлы cookie с HttpOnly. Используйте библиотеки (auth0-spa-js, oidc-client-ts, NextAuth), а не собственную реализацию.
Часто задаваемые вопросы
Урок «Потоки OAuth на фронтенде» бесплатный?
Да — полный текст урока «Потоки OAuth на фронтенде» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Frontend Academy, подпишись на CoddyKit PRO. Курс Frontend Academy содержит 4 уроков всего.
Чему я научусь в уроке «Потоки OAuth на фронтенде»?
Реализуйте поток кода авторизации с PKCE в SPA, обменивайте код на токены, безопасно храните токены доступа и избегайте неявного потока Ты практикуешь Frontend Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Frontend Academy?
Предыдущий опыт не требуется. Frontend Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Потоки OAuth на фронтенде»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Frontend Academy?
Да. Каждый урок Frontend Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Предотвращение XSS: кодирование вывода и CSP
- CSRF: файлы cookie SameSite и токены
- Политика безопасности содержимого: nonce и hash
- Потоки OAuth на фронтенде