Обработка событий сессии и сценарии авторизации

Статья описывает, как приложение реагирует на события сессии и какие сценарии авторизации нужно обработать при интеграции с OpenID Connect Session Management.

Настройка модуля описана здесь.

RP iframe проверяет полученный при авторизации session_state и на основании этого отправляет события в ваше приложение через postMessage.

Приложение должно слушать эти события и реагировать.

window.addEventListener('message', (event) => {
  // Проверяем, что сообщение пришло с доверенного origin
  if (event.origin !== 'https://your-project.softline.ru') {
    return;
  }

  if (event.data.type === 'oidc_session_changed') {
    console.log('Сессия изменилась');
    window.location.href = '/login?prompt=none';
  }
});

Что здесь происходит:

  1. Слушаем все postMessage.
  2. Отбрасываем сообщения с чужих origin — это защита от подделки.
  3. Если пришло событие oidc_session_changed — перенаправляем пользователя на повторную авторизацию.
    Событие oidc_session_changed отправляется, если пользователь:
    • вышел из системы;
    • вошёл под другим аккаунтом;
    • переавторизовался;
    • изменил состояние сессии у провайдера.

Что делать при изменении сессии (рекомендуемый алгоритм):

  1. Выполнить повторную авторизацию.
  2. Получить новый session_state.
  3. Перезапустить RP iframe.

При первом входе session_state отсутствует.

Последовательность действий для получения:

  1. Перенаправьте пользователя на авторизацию (на OAuth2 Provider) либо на тихую проверку:
    return redirect()->away(
      'https://login.softline.ru/oauth2/authorize?' . http_build_query([
        'client_id'     => 'abc123xyz',
        'redirect_uri'  => 'https://your-project.softline.ru/oauth2/callback',
        'response_type' => 'code',
        'scope'         => 'auth.data.personal.get',
        'state'         => 'sdfwgdgd6SF',
        'prompt'        => 'none'
      ])
    );
  2. Провайдер возвращает пользователя на адрес редиректа (redirect_uri), ответ в том числе содержит актуальный session_state.
    В случае, если в ответ получена ошибка, см. описание кейса далее.
  3. Приложение проверяет подлинность ответа (state из ответа совпадает с state из запроса), и в случае корректности сохраняет session_state.
  4. Приложение создаёт RP iframe.
  5. Начинается мониторинг сессии.
Важно! Параметр session_state нужно сохранять при любом ответе провайдера, даже если это ошибка (access_denied). Провайдер всегда передает самую актуальную версию session_state.

Если session_state уже сохранён:

  • повторная авторизация не нужна;
  • RP iframe запускается сразу;
  • мониторинг начинается автоматически.
  1. Провайдер перенаправляет пользователя на redirect_uri и передает в ответе параметры, в том числе session_state.
    /callback?code=def50200b07...&state=s761MQGea2Yt...&session_state=7fd534dab... 
  2. Обмен кода на токен:
    curl -X POST "https://login.softline.ru/oauth2/token" \
      -d "grant_type=authorization_code" \
      -d "code=def50200b07..." \
      -d "redirect_uri=https://your-project.softline.ru/oauth2/callback" \
      -d "client_id=abc123xyz" \
      -d "client_secret=your_client_secret"
    Ответ:
    {
      "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0N",
      "token_type": "Bearer",
      "expires_in": 7200,
      "refresh_token": "tGzv3my0kx0BYrHjWmF2nkdZv9",
      "scope": "auth.data.personal.get"
    }
  3. Получение данных пользователя:
    curl -X GET "https://login.softline.ru/api/v1/auth/user" \
      -H "Authorization: Bearer ACCESS_TOKEN"
  4. Дальнейшие действия:
    • Создайте или обновите пользователя.
    • Авторизуйте его в приложении.
    • Создайте RP iframe.

session_state может прийти не только при успешной авторизации, но и при ошибке access_denied. Это нормальное поведение провайдера.

Пример ответа:

/callback?
  state=Eqk5886Ch...
  &session_state=8d0717a9...
  &error=access_denied
&error_description=The+resource+owner+or+authorization+server+denied+the+request. &hint=Unauthenticated&message=The+resource+owner+or+authorization+server+denied+the+request

В контексте управления сессией access_denied обычно означает, что пользователь больше не авторизован у провайдера.
Например:

  • нажал Выйти;
  • сессия истекла;
  • был разлогинен на стороне провайдера.

При получении error = access_denied необходимо:

  1. Разлогинить пользователя в вашем приложении.
  2. Очистить локальную сессию.
  3. Показать страницу логина.
session_state нужно сохранить даже при access_denied — он понадобится для дальнейшей работы RP iframe.