Реалізація SSO (Single Sign-On) для веб-застосунку

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.
Розробка та обслуговування будь-яких видів сайтів:
Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація SSO (Single Sign-On) для веб-застосунку
Складна
~1-2 тижні
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація SSO (Single Sign-On) для веб-додатку

SSO — це не просто «вхід через Google». Це архітектурне рішення, яке впливає на модель сесії, безпеку токенів, життєвий цикл аутентифікації та інтеграцію з корпоративною інфраструктурою. Неправильно спроектований SSO стає однією точкою відмови — буквально: якщо Identity Provider недоступний, користувачі не можуть увійти до жодного додатку.

Протоколи та їх застосування

Два актуальні стандарти: SAML 2.0 та OpenID Connect (OIDC). SAML використовується в корпоративному середовищі (Azure AD, Okta, ADFS) — на основі XML, громіздкий, але широко підтримуваний. OIDC — поверх OAuth 2.0, на основі JSON, нативний для вебу та мобайла.

Для нових проектів вибір майже завжди OIDC. Виняток — інтеграція з legacy enterprise IdP, які підтримують тільки SAML. У цьому випадку можна поставити брокер (Keycloak, Dex), який приймає SAML та надає OIDC далі.

Базовий потік Authorization Code з PKCE:

Browser → /authorize?response_type=code&code_challenge=... → IdP
IdP → callback?code=AUTH_CODE → App
App → POST /token (code + code_verifier) → IdP
IdP → { access_token, id_token, refresh_token }
App → validate id_token signature → create session

PKCE обов'язковий для публічних клієнтів (SPA, мобайл) — захист від перехоплення коду авторизації.

Архітектура з кількома додатками

У класичному SSO центральна сесія зберігається у IdP, додатки мають свої короткоживучі сесії. Механізм Single Logout (SLO) вимагає особливої уваги: при виході з одного додатку IdP повинен повідомити інші через backchannel (server-to-server) або front-channel (редиректи через браузер).

Схема з кількома Service Provider:

┌──────────┐    ┌──────────┐    ┌──────────┐
│  App A   │    │  App B   │    │  App C   │
└────┬─────┘    └────┬─────┘    └────┬─────┘
     │               │               │
     └───────────────┴───────────────┘
                     │
              ┌──────▼──────┐
              │  IdP        │
              │  (Keycloak) │
              └─────────────┘

Кожний додаток — це client у терміології IdP з власними налаштуваннями: дозволені redirect URIs, scopes, час життя токену.

Валідація токенів

id_token — це JWT. Валідація обов'язкова на кожному запиті, якщо токен передається як credentials. Мінімальний набір перевірок:

from jwt import PyJWT, algorithms
import requests

def validate_id_token(token: str, client_id: str, issuer: str) -> dict:
    # Отримати публічні ключі IdP
    jwks_uri = f"{issuer}/.well-known/openid-configuration"
    config = requests.get(jwks_uri).json()
    jwks = requests.get(config["jwks_uri"]).json()

    header = PyJWT.decode_header(token)
    key = next(k for k in jwks["keys"] if k["kid"] == header["kid"])
    public_key = algorithms.RSAAlgorithm.from_jwk(key)

    claims = PyJWT.decode(
        token,
        public_key,
        algorithms=["RS256"],
        audience=client_id,
        issuer=issuer,
        options={"verify_exp": True}
    )
    return claims

Ключі IdP потрібно кешувати з TTL, а не запрошувати при кожному вилику. Одночасно потрібна можливість інвалідувати кеш при ротації ключів (подія keys_changed або просто короткий TTL 1–6 годин).

Інтеграція в Laravel-додаток

Для Laravel є пакет socialite для простих провайдерів (Google, GitHub) та league/oauth2-client для кастомних OIDC. Для корпоративного SSO часто використовують aacotroneo/laravel-saml2 або пряму інтеграцію через firebase/php-jwt.

Приклад OIDC callback:

// routes/web.php
Route::get('/auth/callback', [SsoController::class, 'callback']);

// SsoController.php
public function callback(Request $request): RedirectResponse
{
    $code = $request->input('code');
    $state = $request->input('state');

    // Перевірка state проти CSRF
    if ($state !== session('oauth_state')) {
        abort(400, 'Invalid state');
    }

    $tokens = $this->oidcClient->exchangeCode($code);
    $claims = $this->oidcClient->validateIdToken($tokens['id_token']);

    $user = User::updateOrCreate(
        ['sub' => $claims['sub']],
        [
            'email'      => $claims['email'],
            'name'       => $claims['name'],
            'provider'   => 'corporate_sso',
            'last_login' => now(),
        ]
    );

    Auth::login($user, remember: true);

    return redirect()->intended('/dashboard');
}

Поле sub (subject) — стабільний ідентифікатор користувача від IdP. Email може змінюватися, sub — ні. Лінкувати аккаунти потрібно по sub, а не по email.

Single Logout

SLO — складніше, ніж здається. Backchannel logout: IdP відправляє POST на logout endpoint кожного додатку з logout_token (JWT з sid). Додаток знаходить сесію по sid і знищує її.

Route::post('/auth/backchannel-logout', function (Request $request) {
    $logoutToken = $request->input('logout_token');
    $claims = validateLogoutToken($logoutToken); // аналогічно id_token

    $sessionId = $claims['sid'];

    // Видалити всі сесії з даним SSO session ID
    DB::table('sessions')
        ->where('sso_session_id', $sessionId)
        ->delete();

    return response()->noContent();
})->middleware('throttle:60,1');

Front-channel logout працює через iframe: IdP відкриває logout URL кожного додатку в прихованих фреймах. Надійність нижча — залежить від політики браузера (ITP у Safari блокує сторонні cookies у iframe).

Обробка помилок та edge cases

  • IdP недоступний: потрібна резервна опція — локальна форма входу з паролем або graceful degradation з повідомленням «SSO тимчасово недоступний»
  • Закінчення сесії IdP під час активної роботи: оновлення токену через refresh_token повинно відбуватися прозоро, без переривання користувача
  • Зміна email користувача: якщо IdP оновив email, додаток повинен реагувати коректно — не створювати дубль аккаунту
  • Мультитенантність: у різних клієнтів можуть бути різні IdP. Визначаємо IdP по домену email або по tenant_id у URL (app.example.com/{tenant}/login)

Тимчасовість реалізації

  • Інтеграція з одним OIDC провайдером (Google Workspace, Azure AD) — 3–5 днів
  • Власний IdP на Keycloak з кількома додатками — 2–3 тижні
  • SAML + OIDC брокер з мультитенантністю — від 4 тижнів

Основний час йде не на код, а на конфігурацію IdP, тестування edge cases аутентифікації та налаштування SLO. Токени, які «вроді працюють», можуть містити неверні claims або не проходити валідацію в продакшні через розбіжність часу на серверах (claim iat/exp).