Почему пароль в каждом запросе — это как отдать ключи от квартиры курьеру «на всякий случай»
Если вы когда-нибудь проектировали REST API и упирались в вопрос «а как мы тут поймём, что это вообще клиент Вася, а не хакер Петя?» — поздравляю, вы дошли до темы авторизации. И первое, что всплывает в гугле, — это буквенный салат: OAuth, OAuth 2.0, JWT, Bearer, OIDC, SAML. Половина слов звучит одинаково, вторая половина — как заклинания из Hogwarts Legacy.
Давайте разберёмся, что из этого что, и почему OAuth и JWT почти всегда идут в паре (хотя в теории не обязаны).
Аутентификация vs авторизация: два слова, которые все путают
Прежде чем лезть в OAuth, разложим два термина, которые путают даже в тендерной документации крупных банков.
Аутентификация (authentication) — это «кто вы?». Проверка, что пользователь тот, за кого себя выдаёт (логин/пароль, биометрия, SMS-код).
Авторизация (authorization) — это «что вам можно?». Проверка, имеет ли уже опознанный пользователь право совершить действие (читать заказы, списывать деньги, удалять юзеров).
Условно: охранник на входе в офис смотрит пропуск — это аутентификация. Турникет пускает вас только на 3-й этаж — это авторизация. OAuth, несмотря на название, — это в первую очередь про авторизацию (передачу прав между сервисами), хотя часто используется как часть системы входа.
Если хотите освежить, чем REST API отличается от всего остального и почему он stateless — у меня есть отдельный пост про основные принципы REST API и про stateless vs stateful на пальцах. Вся логика токенов ниже построена именно на stateless-модели.
Что такое OAuth 2.0 и зачем он появился
OAuth 2.0 — это протокол делегированной авторизации. Ключевое слово тут — «делегированной». Когда сервис А хочет получить данные пользователя из сервиса Б, пользователь не даёт сервису А свой логин и пароль от Б. Вместо этого сервис Б выдаёт сервису А временный токен с ограниченными правами.
Классический пример: приложение «Умный планировщик» хочет читать ваш Google Calendar. Без OAuth вам пришлось бы отдать планировщику свой пароль от Google (и молиться). С OAuth — Google выдаёт планировщику токен вида «можно читать календарь Паши до 15 мая, и больше ничего».
Четыре роли OAuth
В протоколе участвуют четыре действующих лица. Запомнить их проще всего через аналогию с рестораном:
| Роль OAuth | Кто это | Аналогия в ресторане |
|---|---|---|
| Resource Owner | Пользователь, владелец данных | Гость, заказавший бутылку вина |
| Client | Приложение, которое хочет получить данные | Официант, который несёт вино |
| Authorization Server | Сервер, выдающий токены | Администратор, проверяющий возраст гостя |
| Resource Server | API с защищёнными данными | Винный погреб, откуда достают бутылку |
Клиент (приложение) идёт к администратору (authorization server), показывает согласие гостя (resource owner), получает временный пропуск (access token) и с ним идёт в погреб (resource server) за вином. Гостя при этом никто не заставляет бегать с паролем.
Access token, refresh token и почему их два
Самая распространённая путаница — это зачем вообще два токена, если один уже работает.
Access token — короткоживущий токен (обычно 5–60 минут), которым клиент подписывает каждый запрос к API. Это «пропуск на сегодня».
Refresh token — долгоживущий токен (дни, недели, месяцы), который хранится у клиента и нужен только для одного — попросить у authorization server новый access token. Это «договор с администрацией, что вам можно выписывать пропуска».
Смысл разделения простой: access token летает по сети в каждом запросе, значит шанс его утечь выше. Поэтому он короткий — даже если украдут, жить будет недолго. Refresh token лежит в безопасном хранилище (server-side, keychain, httpOnly cookie) и светится наружу редко.
На диаграмме показан полный flow Authorization Code — самый безопасный и самый распространённый сценарий OAuth 2.0. Пользователь логинится на стороне Authorization Server (клиент никогда не видит пароль), получает code, обменивает его на пару токенов и дальше общается с API по access-токену. Когда тот протухает — тихо обновляет через refresh.
Что такое JWT и при чём тут OAuth
OAuth — это протокол, он не диктует формат токена. Токен может быть хоть строкой abc123, хоть UUID, хоть чем угодно. Но на практике в 90% случаев в качестве access token используется JWT — JSON Web Token.
JWT (JSON Web Token) — это стандарт самодостаточного токена. Он состоит из трёх частей, разделённых точками:
header.payload.signature. Внутри уже лежит вся нужная информация о пользователе и правах, подписанная криптографически.
Пример реального JWT (разбит на части для читаемости):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NSIsIm5hbWUiOiJQYXZlbCIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcxNDU2NzIwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5cПервая часть — header с алгоритмом подписи. Вторая — payload с данными (id, имя, роль, срок действия). Третья — подпись, которую сервер проверяет своим секретом.
Почему JWT так полюбили
Ключевое преимущество JWT — самодостаточность. Resource server может проверить токен локально, по одной лишь подписи, без похода в базу или в Authorization Server. Для stateless-архитектуры это подарок судьбы.
Диаграмма показывает, как из трёх кирпичиков (header, payload, signature) собирается итоговый Bearer-токен. Payload виден всем — это просто base64, не шифрование. Но подделать его нельзя: изменишь хоть один байт — подпись перестанет сходиться.
Bearer: как токен попадает в запрос
Токен летает в HTTP-заголовке Authorization со схемой Bearer:
GET /api/v1/orders HTTP/1.1Host: api.example.comAuthorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOi...Accept: application/jsonСлово Bearer дословно означает «предъявитель». Логика буквальная: кто предъявил токен — тот и пользователь. Поэтому утечка Bearer-токена равна утечке сессии. Поэтому access токены короткие. Поэтому HTTPS обязателен. Поэтому в браузерах токен лучше хранить в httpOnly cookie, а не в localStorage (где его утащит любой XSS).
OAuth Grant Types: какой flow выбрать
OAuth 2.0 описывает несколько сценариев получения токена — они называются grant types. На практике актуальны три.
| Grant Type | Когда использовать | Безопасность |
|---|---|---|
| Authorization Code + PKCE | SPA, мобильные приложения, любые публичные клиенты | Высокая |
| Client Credentials | Сервис-к-сервису (M2M), без пользователя | Высокая |
| Resource Owner Password | Только легаси и свои приложения | Низкая, не рекомендуется |
Два других — Implicit и Device Code — либо устарели (первый), либо применяются в узких случаях (второй — для Smart TV и консолей, где ввод пароля неудобен).
PKCE — то, без чего не стоит жить в 2026
PKCE (Proof Key for Code Exchange) — это расширение Authorization Code flow, защищающее от перехвата authorization code. Клиент генерирует случайную строку (code_verifier), хэширует её и отправляет хэш при инициализации flow. Потом при обмене code на токен присылает оригинал — сервер сверяет. Злоумышленник, даже перехватив code, без verifier ничего не обменяет.
Раньше PKCE считался обязательным только для мобильных приложений. Сейчас рекомендуется для всех публичных клиентов, включая SPA.
Типичные ошибки при проектировании авторизации
За годы ревью чужих API я собрал коллекцию граблей, на которые наступает каждый третий.
- Хранить JWT в
localStorage. Любая XSS-уязвимость = утечка всех токенов. Используйте httpOnly cookie с флагами Secure и SameSite. - Забыть поле
expв payload. Токен становится вечным. Когда такой утечёт — будет весело (и долго). - Проверять токен только на Gateway. Resource server должен проверять подпись самостоятельно. Иначе любой, кто попал внутрь сети, ходит без билета.
- Один и тот же секрет на все окружения. Dev-секрет утёк в гит → прод скомпрометирован.
- Класть в payload всё подряд. Email, телефон, паспорт — лишнее. Payload не шифруется, он только подписан. Храните там только id и роли.
- Не ротировать refresh token. После каждого использования refresh должен меняться. Иначе украденный refresh = бессмертная сессия.
- Путать JWT с сессией. JWT нельзя отозвать мгновенно (он stateless). Если нужна возможность «выкинуть пользователя здесь и сейчас» — либо короткий TTL, либо блэклист, либо возвращение к серверным сессиям.
OAuth, OIDC и прочие аббревиатуры
Чтобы закрыть тему однозначно:
- OAuth 2.0 — авторизация (делегирование доступа).
- OpenID Connect (OIDC) — надстройка над OAuth 2.0, добавляющая аутентификацию. Именно OIDC возвращает
id_tokenс данными пользователя (в отличие от access token, которому плевать, кто пользователь, главное — что ему можно). - SAML — старший брат OAuth, XML-based, живёт в корпоративном SSO (Active Directory, Okta). Если вы делаете B2C — вам не сюда.
- API Key — не OAuth. Простой статический ключ для сервис-к-сервису без пользователя. Работает, но без гибкости и срока жизни (если только не прикрутить ротацию руками).
Хотите вход через Google в своё приложение — это OIDC поверх OAuth. Хотите защитить внутреннее API между микросервисами — Client Credentials с JWT. Хотите SSO между десятком корпоративных систем — SAML или Keycloak.
Заключение
OAuth 2.0 и JWT — это не «модная штука для стартапов», а индустриальный стандарт, без которого REST API в 2026-м проектировать уже неприлично. При этом ни OAuth, ни JWT не решают всех проблем безопасности сами по себе — это лишь кирпичи. Положите их криво — получите красивую, подписанную криптографически уязвимость.
Главное правило: токен — это секрет. Короткий срок жизни, HTTPS, ротация, httpOnly, минимальные claims в payload. Делаете это — спите спокойно. Не делаете — увидимся в новостях про очередную утечку (желательно, конечно, не в качестве главного героя).
PS: «Мы потом перепишем авторизацию» — фраза, после которой авторизация не переписывается никогда. Лучше сразу.