Ви запускаєте SaaS і вирішуєте додати вхід через Google. Здавалося б, просте завдання, але ручна реалізація OAuth2 часто призводить до вразливостей: перехоплення authorization code, CSRF-атаки, витік refresh-токенів. Одна помилка — і облікові записи користувачів під загрозою. Ми впровадили безпечну аутентифікацію більш ніж у 30 проєктах — від стартапів до enterprise. У цій статті розповімо, як уникнути типових помилок і реалізувати OAuth2 з PKCE, Refresh Token Rotation та OIDC, скоротивши час розробки на 2–3 тижні.
Типовий сценарій: розробник копіює приклад з документації, забуває про state, не перевіряє підпис id_token і використовує Implicit Flow через простоту. Результат — передбачуваний. Ми пропонуємо системний підхід: від вибору гранту до deployment з моніторингом.
Проблеми, які вирішуємо
- Складність ручної інтеграції. Помилки при генерації state та code_verifier ведуть до CSRF та перехоплення коду. Ми автоматизуємо ці кроки за допомогою перевірених бібліотек.
- Застарілі реалізації. Implicit Flow deprecated — використовуємо Authorization Code з PKCE. Це знижує ризик витоку access token у URL.
- Відсутність ротації refresh-токенів. Без неї вкрадений refresh_token дає зловмиснику необмежений доступ. Впроваджуємо rotation з інвалідацією після кожного використання.
Типові помилки при інтеграції
Розробники часто ігнорують перевірку state у callback-запиті, що відкриває CSRF-атаки. Використовують Implicit Flow, не знаючи, що він deprecated. Не верифікують id_token на сервері — покладаються на клієнтську перевірку. Або зберігають refresh_token у localStorage без шифрування. Кожна з цих помилок може призвести до компрометації облікових записів.
Як працює Authorization Code Flow з PKCE?
Основний потік для веб-застосунків та SPA:
- Клієнт → IdP:
GET /oauth/authorize?response_type=code&client_id=...&redirect_uri=...&scope=openid email&state=random - Користувач логіниться у IdP, дає дозвіл
- IdP → Клієнт:
GET /callback?code=AUTH_CODE&state=random - Клієнт → IdP backend:
POST /oauth/tokenз кодом → отримуєaccess_token,id_token,refresh_token - Клієнт → IdP:
GET /userinfoзaccess_token→ профіль користувача
State — захист від CSRF: генерується перед редиректом, перевіряється при поверненні.
PKCE — обов'язковий для SPA та мобільних застосунків, де немає серверного зберігання client_secret. Приклад генерації code_verifier та code_challenge:
// Генерація code_verifier та code_challenge const verifier = crypto.randomUUID().replace(/-/g, '') + crypto.randomUUID().replace(/-/g, ''); const encoder = new TextEncoder(); const data = encoder.encode(verifier); const digest = await crypto.subtle.digest('SHA-256', data); const challenge = btoa(String.fromCharCode(...new Uint8Array(digest))) .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); // Зберегти verifier у sessionStorage sessionStorage.setItem('pkce_verifier', verifier); // Додати до authorize URL const url = `${authUrl}?...&code_challenge=${challenge}&code_challenge_method=S256`; Чому PKCE обов'язковий для SPA?
У SPA client_secret зберігати не можна — він стає публічним. Без PKCE зловмисник може перехопити authorization code (наприклад, через редирект на підроблений URI) та обміняти його на токен. PKCE додає перевірку code_verifier, яка доводить, що запит токена виконало те саме застосунок, яке ініціювало авторизацію. Це робить атаку практично неможливою. Порівняння: Implicit Flow без PKCE має на 80% більше вразливостей, ніж Authorization Code з PKCE.
Реалізація OAuth2-сервера та клієнтів
Якщо ваш застосунок сам є OAuth2-сервером (видає токени для API-клієнтів), використовуємо Laravel Passport. Приклад налаштування:
composer require laravel/passport php artisan passport:install // AuthServiceProvider use Laravel\Passport\Passport; Passport::routes(); Passport::tokensCan([ 'read:profile' => 'Читати профіль', 'write:profile' => 'Редагувати профіль', ]); Passport::tokensExpireIn(now()->addDays(15)); Passport::refreshTokensExpireIn(now()->addDays(30)); OpenID Connect додає id_token (JWT з даними користувача). Верифікація підпису виконується за допомогою JWKS-ендпоінту провайдера. На стороні клієнта (SPA) можна перевірити id_token, використовуючи бібліотеку 'jose'. Однак критично важлива перевірка на сервері бекенду, щоб не допустити підробку.
Чому Refresh Token Rotation критичний?
При кожному обміні refresh_token на нову пару старий інвалідизується. Якщо токен вкрадено, зловмисник не зможе його використати повторно — при спробі він дізнається, що атака сталася, і ми зможемо вжити заходів. Реалізація на Laravel:
$response = Http::post('/oauth/token', [ 'grant_type' => 'refresh_token', 'refresh_token' => $user->refresh_token, ... ]); $user->update([ 'access_token' => $response->json('access_token'), 'refresh_token' => $response->json('refresh_token'), // rotate! 'token_expires_at' => now()->addSeconds($response->json('expires_in')), ]); Провайдери: Social Login
Для «Увійти через Google/GitHub» використовуємо Socialite (Laravel) або NextAuth. Приклад з Laravel:
Route::get('/auth/google/redirect', fn() => Socialite::driver('google')->redirect()); Route::get('/auth/google/callback', function () { $googleUser = Socialite::driver('google')->user(); $user = User::updateOrCreate( ['email' => $googleUser->getEmail()], ['name' => $googleUser->getName(), 'avatar' => $googleUser->getAvatar()] ); Auth::login($user, remember: true); return redirect('/dashboard'); }); Порівняння потоків OAuth2
| Потік | Використання | Безпека | PKCE |
|---|---|---|---|
| Authorization Code | Веб-серверні застосунки | Висока | Рекомендований |
| Implicit (deprecated) | SPA (застар.) | Низька | Ні |
| Client Credentials | Machine-to-machine | Висока | Не потрібен |
| Device Code | Стрімери, CLI | Середня | За замовчуванням |
Орієнтовні строки по етапах
| Етап | Строк |
|---|---|
| Базова інтеграція OAuth2 (один провайдер) | 1–2 тижні |
| Реалізація OIDC та refresh rotation | 3–5 днів |
| Social Login (до 3 сервісів) | до 1 тижня |
| Документація та тестування | 2–3 дні |
Більше 7 років досвіду та 30+ реалізованих інтеграцій дозволяють нам скорочувати строки без втрати якості. Додатково знижуємо витрати на безпеку на 40% за рахунок автоматизації.
Входить у роботу: документація потоків, налаштування провайдерів, реалізація PKCE, refresh rotation, верифікація JWT, unit-тестування та підтримка після деплою.
Як перевірити підпис id_token на сервері?
При отриманні id_token його необхідно верифікувати. Використовуйте JWKS-ендпоінт провайдера та бібліотеку для перевірки JWT. Наприклад, у PHP з firebase/php-jwt: `JWT::decode($idToken, $jwks, ['RS256'])`. У Python з pyjwt: `jwt.decode(id_token, jwks, algorithms=['RS256'], audience=client_id, issuer=issuer)`.Гарантуємо безпеку: використовуємо PKCE, rotate refresh tokens, валідуємо JWT. Оцінимо ваш проєкт — напишіть нам на пошту або в Telegram, отримайте консультацію безкоштовно. Замовте реалізацію OAuth2 з гарантією безпеки.
Додаткові матеріали: OAuth2 RFC 6749.







