Как происходит процесс аутентификации и выдачи токена (OAuth 2.0 / JWT)?

«Как происходит процесс аутентификации и выдачи токена (OAuth 2.0 / JWT)?» — вопрос из категории Безопасность, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Процесс аутентификации по схеме OAuth 2.0 с выдачей JWT-токена (поток Resource Owner Password Credentials или Authorization Code):

1. Запрос на получение токена: Клиентское приложение отправляет HTTP POST-запрос на эндпоинт аутентификации сервера авторизации (Authorization Server).

Пример тела запроса для потока Password Credentials:

POST /oauth/token HTTP/1.1
Host: auth-server.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret)

grant_type=password&username=user&password=pass&scope=read write

2. Валидация учетных данных: Сервер авторизации:

  • Проверяет client_id и client_secret (для confidential clients).
  • Аутентифицирует пользователя по username и password (сверяя с БД, LDAP, etc.).
  • Проверяет запрошенные scope на соответствие правам клиента.

3. Генерация JWT-токена: Если валидация успешна, сервер создаёт JWT (JSON Web Token), состоящий из трёх частей:

  • Header: Алгоритм подписи (например, {"alg": "HS256", "typ": "JWT"}).
  • Payload (Claims): Данные (например, {"sub": "123", "username": "john", "roles": ["USER"], "exp": 1678886400}).
  • Signature: Цифровая подпись, созданная с использованием секретного ключа (HMAC) или приватного ключа (RSA).

4. Отправка ответа клиенту: Сервер возвращает JSON-ответ с токеном и метаданными.

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "def50200e2b4c...",
  "scope": "read write"
}

5. Использование токена клиентом: Клиент сохраняет токен (в памяти, localStorage или безопасных куках) и использует его для доступа к защищённым ресурсам (Resource Server), добавляя в заголовок:

GET /api/protected-resource HTTP/1.1
Host: api-server.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Пример кода на Spring Security (упрощённо):

@RestController
public class AuthController {
    @PostMapping("/login")
    public ResponseEntity<AuthResponse> login(@RequestBody LoginRequest request) {
        // 1. Аутентификация
        Authentication authentication = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())
        );
        // 2. Генерация JWT
        String jwt = jwtTokenUtil.generateToken(authentication);
        // 3. Ответ
        return ResponseEntity.ok(new AuthResponse(jwt));
    }
}

Важно: Поток password рекомендуется только для доверенных клиентов (например, собственные мобильные приложения). Для веб-приложений предпочтительнее поток Authorization Code с PKCE.