Skip to content
IRC-CodingIRC-Coding
OAuth 2.0Authorization Code FlowAccess TokenRefresh TokenAPI Security

OAuth 2.0: Authorization Code, Flows и Access Token

OAuth 2.0 — стандартный фреймворк авторизации. Authorization Code Flow, Client Credentials, Access/Refresh Token с примерами.

S

schutzgeist

5 min read
OAuth 2.0: Authorization Code, Flows и Access Token

OAuth 2.0: код авторизации, потоки и токены доступа

Это объяснение ключевых понятий OAuth 2.0 Framework с разбором потоков, токенов и практической реализацией.

Суть

OAuth 2.0 - это стандартизированный фреймворк авторизации, позволяющий третьим сторонам получать доступ к ресурсам пользователя без знания его учетных данных.

Определение

OAuth 2.0 - широко используемый протокол авторизации для веб-, десктопных и мобильных приложений. Он дает возможность приложению (клиенту) получать доступ от имени пользователя к ресурсам другой системы (сервера ресурсов).

Основные характеристики:

  • Делегирование: пользователи передают права доступа приложениям
  • Токены вместо паролей: использование Access Token вместо пароля
  • Четкая ролевая модель: разделение ответственности между участниками
  • Стандартизация: RFC 6749 определяет фреймворк

Участники процесса:

  • Resource Owner: пользователь, которому принадлежат ресурсы
  • Client: приложение, которое хочет получить доступ к ресурсам
  • Authorization Server: выдает и валидирует токены
  • Resource Server: предоставляет защищенные ресурсы

OAuth 2.0 не передает личность пользователя, только авторизует доступ к ресурсам. Для проверки личности используется OpenID Connect.

Ключевые моменты для экзаменов

  • Фреймворк авторизации, не аутентификации
  • Роли: Resource Owner, Client, Authorization Server, Resource Server
  • Access Token и опционально Refresh Token
  • Authorization Code Flow для веб-приложений
  • Client Credentials Flow для общения сервер-сервер
  • HTTP-протокол, поддержка REST API
  • Токены повышают безопасность
  • Защита от передачи пароля через механизм делегирования

Основные компоненты

  1. Resource Owner: пользователь или система, владеющие ресурсами
  2. Client: приложение, которому нужен доступ к ресурсам
  3. Authorization Server: выдает и проверяет токены
  4. Resource Server: хостит защищенные ресурсы
  5. Access Token: краткосрочный токен для доступа к ресурсам
  6. Refresh Token: долгосрочный токен для получения новых Access Token
  7. Authorization Code: временный код для обмена на токен
  8. Redirect URI: URL перенаправления после авторизации
  9. Scope: определяет область доступа
  10. Grant Type: тип потока OAuth 2.0

Потоки OAuth 2.0

1. Authorization Code Flow (для веб-приложений)

1. Client → User: переадресация на Authorization Server
2. User → Authorization Server: вход и подтверждение
3. Authorization Server → Client: Authorization Code
4. Client → Authorization Server: обмен Code на Access Token
5. Authorization Server → Client: Access Token + Refresh Token
6. Client → Resource Server: запрос ресурсов с Access Token

2. Client Credentials Flow (для общения сервер-сервер)

1. Client → Authorization Server: отправка учетных данных
2. Authorization Server → Client: Access Token
3. Client → Resource Server: запрос ресурсов с Access Token

3. Implicit Flow (устаревший, для одностраничных приложений)

1. Client → User: переадресация на Authorization Server
2. User → Authorization Server: вход и подтверждение
3. Authorization Server → Client: Access Token прямо в фрагменте URL

Примеры реализации

Реализация Authorization Code Flow

// Frontend: запрос авторизации
const authUrl = 'https://auth.example.com/authorize';
const params = new URLSearchParams({
  response_type: 'code',
  client_id: 'your_client_id',
  redirect_uri: 'https://yourapp.com/callback',
  scope: 'read:profile write:data',
  state: generateRandomString() // защита от CSRF
});

window.location.href = `${authUrl}?${params}`;

// Обработчик Callback
async function handleCallback(code, state) {
  // Проверка state
  if (state !== getStoredState()) {
    throw new Error('Invalid state - CSRF attack detected');
  }
  
  // Обмен Code на Token
  const tokenResponse = await fetch('https://auth.example.com/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      code: code,
      client_id: 'your_client_id',
      client_secret: 'your_client_secret',
      redirect_uri: 'https://yourapp.com/callback'
    })
  });
  
  const tokens = await tokenResponse.json();
  localStorage.setItem('access_token', tokens.access_token);
  localStorage.setItem('refresh_token', tokens.refresh_token);
}

Backend: валидация токена

// Spring Boot Example
@RestController
@RequestMapping("/api")
public class ApiController {
    
    @GetMapping("/profile")
    public ResponseEntity<UserProfile> getProfile(
            @RequestHeader("Authorization") String authHeader) {
        
        // Извлечение и валидация токена
        String token = authHeader.replace("Bearer ", "");
        
        if (!tokenValidator.isValid(token)) {
            return ResponseEntity.status(401).build();
        }
        
        // Извлечение claims из токена
        Claims claims = tokenValidator.getClaims(token);
        String userId = claims.getSubject();
        List<String> scopes = claims.get("scope", List.class);
        
        // Проверка scope
        if (!scopes.contains("read:profile")) {
            return ResponseEntity.status(403).build();
        }
        
        UserProfile profile = userService.getProfile(userId);
        return ResponseEntity.ok(profile);
    }
    
    @PostMapping("/data")
    public ResponseEntity<?> createData(
            @RequestHeader("Authorization") String authHeader,
            @RequestBody DataRequest request) {
        
        String token = authHeader.replace("Bearer ", "");
        
        if (!tokenValidator.isValid(token)) {
            return ResponseEntity.status(401).build();
        }
        
        Claims claims = tokenValidator.getClaims(token);
        List<String> scopes = claims.get("scope", List.class);
        
        // Проверка прав на запись
        if (!scopes.contains("write:data")) {
            return ResponseEntity.status(403).build();
        }
        
        Data data = dataService.create(request, claims.getSubject());
        return ResponseEntity.ok(data);
    }
}

Client Credentials Flow

# Python пример для взаимодействия сервер-сервер
import requests

def get_access_token():
    token_url = "https://auth.example.com/token"
    
    data = {
        'grant_type': 'client_credentials',
        'client_id': 'your_client_id',
        'client_secret': 'your_client_secret',
        'scope': 'api:read api:write'
    }
    
    response = requests.post(token_url, data=data)
    response.raise_for_status()
    
    token_data = response.json()
    return token_data['access_token']

def api_call():
    access_token = get_access_token()
    
    headers = {
        'Authorization': f'Bearer {access_token}',
        'Content-Type': 'application/json'
    }
    
    response = requests.get(
        'https://api.example.com/data',
        headers=headers
    )
    
    return response.json()

Обновление токена через Refresh Token

// Получение нового Access Token с помощью Refresh Token
async function refreshAccessToken() {
  const refreshToken = localStorage.getItem('refresh_token');
  
  if (!refreshToken) {
    throw new Error('No refresh token available');
  }
  
  try {
    const response = await fetch('https://auth.example.com/token', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: new URLSearchParams({
        grant_type: 'refresh_token',
        refresh_token: refreshToken,
        client_id: 'your_client_id',
        client_secret: 'your_client_secret'
      })
    });
    
    if (!response.ok) {
      throw new Error('Token refresh failed');
    }
    
    const tokens = await response.json();
    localStorage.setItem('access_token', tokens.access_token);
    
    if (tokens.refresh_token) {
      localStorage.setItem('refresh_token', tokens.refresh_token);
    }
    
    return tokens.access_token;
  } catch (error) {
    // Refresh Token больше не действителен, нужна повторная авторизация
    localStorage.removeItem('access_token');
    localStorage.removeItem('refresh_token');
    redirectToLogin();
  }
}

Рекомендации по безопасности

State Parameter (защита от CSRF)

function generateState() {
  return Math.random().toString(36).substring(2, 15) +
         Math.random().toString(36).substring(2, 15);
}

function storeState(state) {
  sessionStorage.setItem('oauth_state', state);
}

function validateState(receivedState) {
  const storedState = sessionStorage.getItem('oauth_state');
  sessionStorage.removeItem('oauth_state');
  return storedState === receivedState;
}

PKCE (Proof Key for Code Exchange)

// PKCE для мобильных и одностраничных приложений
function generatePKCE() {
  const codeVerifier = generateRandomString(128);
  const codeChallenge = base64urlEncode(sha256(codeVerifier));
  
  return { codeVerifier, codeChallenge };
}

// Запрос авторизации с PKCE
const params = new URLSearchParams({
  response_type: 'code',
  client_id: 'your_client_id',
  code_challenge: pkce.codeChallenge,
  code_challenge_method: 'S256',
  redirect_uri: 'https://yourapp.com/callback'
});

Преимущества и недостатки

Преимущества

  • Безопасность: пароль не передается третьим сторонам
  • Стандартизация: широко распространен и хорошо документирован
  • Гибкость: различные потоки для разных сценариев использования
  • Масштабируемость: централизованная авторизация для множества сервисов
  • Отзыв: токены можно аннулировать в любой момент

Недостатки

  • Сложность: больше шагов, чем простая аутентификация
  • Производительность: дополнительные сетевые запросы для обновления токенов
  • Управление состоянием: клиент должен управлять статусом токенов
  • Риски безопасности: неправильная реализация может привести к уязвимостям

Часто встречающиеся вопросы на экзаменах

  1. В чем разница между OAuth 2.0 и OpenID Connect? OAuth 2.0 используется для авторизации, OpenID Connect добавляет аутентификацию (проверку личности).

  2. Объясните Authorization Code Flow! Client переадресовывает пользователя на Authorization Server, получает код, обменивает код на токен.

  3. Когда используется Client Credentials Flow? Для взаимодействия сервер-сервер без участия пользователя.

  4. Для чего служит State Parameter? Защита от CSRF путем проверки, что запрос был инициирован клиентом.

Основные ресурсы

  1. https://tools.ietf.org/html/rfc6749
  2. https://oauth.net/2/
  3. https://auth0.com/docs/authorization-framework/overview
Назад к блогу
Share:

Похожие статьи