Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает OAuth
OAuth — это открытый стандарт авторизации, который позволяет пользователям предоставлять доступ к своим ресурсам третьим приложениям без передачи пароля. Это основной механизм "Войти через Google", "Войти через Facebook" и т.д.
Основные участники
- Resource Owner — пользователь, владелец ресурсов
- Client — приложение, которое хочет получить доступ
- Authorization Server — сервер, выдающий токены (например, Google)
- 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 на сервере) |
| Implicit | SPA (Single Page App) | Низкая (deprecated) |
| Client Credentials | Server-to-Server | Высокая |
| Resource Owner Password | Legacy приложения | Низкая (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
| Аспект | OAuth | Session Cookie |
|---|---|---|
| Интеграция | С провайдерами (Google, GitHub) | Собственная система |
| Сложность | Средняя | Простая |
| Безопасность пароля | Не хранишь пароль | Полная ответственность |
| UX | "Login with..." | Регистрация + пароль |
| SSO | Встроенный | Нужно реализовывать |
Итого
OAuth основной flow:
- Пользователь кликает "Войти через Google"
- Редирект на сервер Google (где user авторизуется)
- Google возвращает auth code
- Back-end обменивает код на access_token (используя secret)
- Back-end получает данные пользователя через access_token
- Создаёшь сессию в приложении
Ключевые моменты:
- Никогда не передавай client_secret в front-end
- Всегда проверяй state для защиты от CSRF
- Используй HTTPS
- Обрабатывай refresh_token для долгоживущих сессий
- Валидируй redirect_uri