← Назад к вопросам

Как работает OAuth?

1.7 Middle🔥 151 комментариев
#REST API и HTTP#Безопасность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как работает OAuth

OAuth — это открытый стандарт авторизации, который позволяет пользователям предоставлять доступ к своим ресурсам третьим приложениям без передачи пароля. Это основной механизм "Войти через Google", "Войти через Facebook" и т.д.

Основные участники

  1. Resource Owner — пользователь, владелец ресурсов
  2. Client — приложение, которое хочет получить доступ
  3. Authorization Server — сервер, выдающий токены (например, Google)
  4. Resource Server — сервер с защищёнными ресурсами пользователя

OAuth 2.0 Flow: Authorization Code Grant

Самый распространённый поток (используется в веб-приложениях):

User          Client App          Authorization Server
 |                |                         |
 |--- 1. Click "Login with Google" ------->|
 |                |                         |
 |                |--- 2. Redirect to auth page
 |<------- 3. Authorization Page ----------|
 |                                          |
 |--- 4. Grant Permission -------->|        |
 |                                 |        |
 |     <------- 5. Redirect with auth code -|
 |                |<----- code + state -----|
 |                |
 |                |--- 6. POST /token (code + secret)
 |                |--------> Authorization Server
 |                |
 |                |<------ access_token ---|
 |                |
 |     <--- 7. User logged in, redirect to dashboard
 |

Пошаговый процесс

1. Инициация (Front-End)

Пользователь кликает "Войти через Google":

# Front-End код (JavaScript)
# Редирект на URL авторизации
import urllib.parse

client_id = "your_client_id"
redirect_uri = "https://yourapp.com/callback"
scope = "openid email profile"
state = "random_state_value"  # Защита от CSRF

auth_url = (
    "https://accounts.google.com/o/oauth2/v2/auth?"
    f"client_id={client_id}&"
    f"redirect_uri={urllib.parse.quote(redirect_uri)}&"
    f"scope={urllib.parse.quote(scope)}&"
    f"state={state}&"
    "response_type=code"
)

# Пользователь переходит на auth_url

2. Пользователь авторизуется

На странице Google пользователь:

  • Вводит email и пароль
  • Видит, какие разрешения просит приложение
  • Нажимает "Разрешить"

3. Редирект обратно с кодом

Google перенаправляет пользователя на redirect_uri:

https://yourapp.com/callback?code=auth_code_123&state=random_state_value

4. Back-End обмениваются кодом на токен (критично!)

import requests

# Back-End код
def handle_oauth_callback(auth_code, state):
    # Проверяем state для защиты от CSRF
    stored_state = session.get("oauth_state")
    if state != stored_state:
        raise SecurityError("State mismatch - possible CSRF attack")
    
    # Обмениваем код на токены
    token_endpoint = "https://oauth2.googleapis.com/token"
    
    data = {
        "code": auth_code,
        "client_id": "your_client_id",
        "client_secret": "your_client_secret",  # НИКОГДА не передавай в front-end!
        "grant_type": "authorization_code",
        "redirect_uri": "https://yourapp.com/callback"
    }
    
    response = requests.post(token_endpoint, data=data)
    token_data = response.json()
    
    return {
        "access_token": token_data["access_token"],
        "refresh_token": token_data.get("refresh_token"),
        "expires_in": token_data["expires_in"],
        "token_type": token_data["token_type"]  # Обычно "Bearer"
    }

5. Использование Access Token

Приложение использует access_token для получения данных пользователя:

def get_user_info(access_token):
    headers = {
        "Authorization": f"Bearer {access_token}"
    }
    
    response = requests.get(
        "https://www.googleapis.com/oauth2/v2/userinfo",
        headers=headers
    )
    
    user_data = response.json()
    return {
        "id": user_data["id"],
        "email": user_data["email"],
        "name": user_data["name"],
        "picture": user_data.get("picture")
    }

6. Создание сессии

Приложение создаёт собственную сессию/JWT:

from flask import session, Flask, redirect, request
from datetime import datetime, timedelta
import jwt

app = Flask(__name__)

@app.route("/callback")
def oauth_callback():
    code = request.args.get("code")
    state = request.args.get("state")
    
    # Получить токены
    tokens = handle_oauth_callback(code, state)
    
    # Получить информацию о пользователе
    user_info = get_user_info(tokens["access_token"])
    
    # Создать или обновить пользователя в БД
    user = User.get_or_create(
        oauth_id=user_info["id"],
        email=user_info["email"],
        name=user_info["name"]
    )
    
    # Сохранить токены
    user.oauth_tokens = {
        "access_token": tokens["access_token"],
        "refresh_token": tokens["refresh_token"],
        "expires_at": datetime.now() + timedelta(seconds=tokens["expires_in"])
    }
    user.save()
    
    # Создать сессию приложения
    session["user_id"] = user.id
    
    return redirect("/dashboard")

Типы OAuth Flows

FlowИспользованиеБезопасность
Authorization CodeВеб-приложенияВысокая (secret на сервере)
ImplicitSPA (Single Page App)Низкая (deprecated)
Client CredentialsServer-to-ServerВысокая
Resource Owner PasswordLegacy приложенияНизкая (deprecated)
PKCEМобильные приложенияВысокая (для без-серверных клиентов)

Refresh Token

Access token имеет ограниченное время жизни. Для обновления используется refresh token:

def refresh_access_token(refresh_token):
    data = {
        "refresh_token": refresh_token,
        "client_id": "your_client_id",
        "client_secret": "your_client_secret",
        "grant_type": "refresh_token"
    }
    
    response = requests.post(
        "https://oauth2.googleapis.com/token",
        data=data
    )
    
    new_tokens = response.json()
    return {
        "access_token": new_tokens["access_token"],
        "expires_in": new_tokens["expires_in"]
    }

Безопасность OAuth

import secrets
import hashlib
import base64

# 1. Используй HTTPS — всегда!
# ❌ http://example.com/callback
# ✅ https://example.com/callback

# 2. Проверяй state параметр против CSRF
state = secrets.token_urlsafe(32)
session["oauth_state"] = state
# ... позже проверь, что state совпадает

# 3. НИКОГДА не передавай client_secret в front-end
# ❌ Вставлять secret в JavaScript
# ✅ Хранить secret на сервере

# 4. Валидируй redirect_uri
allowed_redirects = [
    "https://yourapp.com/callback",
    "https://yourapp.com/callback/google"
]
if redirect_uri not in allowed_redirects:
    raise ValueError("Invalid redirect_uri")

# 5. Используй PKCE для мобильных приложений
code_verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).decode()
code_challenge = base64.urlsafe_b64encode(
    hashlib.sha256(code_verifier.encode()).digest()
).decode()

# Отправить code_challenge в auth запросе
# Отправить code_verifier при обмене кодом на token

Популярные OAuth провайдеры

from authlib.integrations.flask_client import OAuth

oauth = OAuth()

# Google
google = oauth.register(
    name="google",
    client_id="...",
    client_secret="...",
    server_metadata_url="https://accounts.google.com/.well-known/openid-configuration",
    client_kwargs={"scope": "openid email profile"}
)

# GitHub
github = oauth.register(
    name="github",
    client_id="...",
    client_secret="...",
    access_token_url="https://github.com/login/oauth/access_token",
    access_token_params=None,
    authorize_url="https://github.com/login/oauth/authorize",
    authorize_params=None,
    api_base_url="https://api.github.com/"
)

# Yandex
yandex = oauth.register(
    name="yandex",
    client_id="...",
    client_secret="...",
    access_token_url="https://oauth.yandex.ru/token",
    authorize_url="https://oauth.yandex.ru/authorize"
)

Сравнение OAuth vs Session

АспектOAuthSession Cookie
ИнтеграцияС провайдерами (Google, GitHub)Собственная система
СложностьСредняяПростая
Безопасность пароляНе хранишь парольПолная ответственность
UX"Login with..."Регистрация + пароль
SSOВстроенныйНужно реализовывать

Итого

OAuth основной flow:

  1. Пользователь кликает "Войти через Google"
  2. Редирект на сервер Google (где user авторизуется)
  3. Google возвращает auth code
  4. Back-end обменивает код на access_token (используя secret)
  5. Back-end получает данные пользователя через access_token
  6. Создаёшь сессию в приложении

Ключевые моменты:

  • Никогда не передавай client_secret в front-end
  • Всегда проверяй state для защиты от CSRF
  • Используй HTTPS
  • Обрабатывай refresh_token для долгоживущих сессий
  • Валидируй redirect_uri