Дай мне код авторизации
Ответ нейросети
10 сентября 2025 12:23
Задача понятна: ты хочешь увидеть рабочий пример кода, реализующий процесс авторизации по схеме Authorization Code (OAuth 2.0), чтобы понять, как это работает на практике. Ниже даю минимальный учебный пример на Python (Flask) и пояснения по шагам. Это учебный макет, не для продакшена (нет TLS, полноценных проверок и т.д.).
Цель по твоему запросу: Понять. Я сначала объясню концепцию, затем дам рабочий код и пояснения к каждому фрагменту.
Что мы сделаем
- Реализуем простой «сервер авторизации» (Authorization Server) с двумя точками:
- /authorize — клиент перенаправляется сюда, чтобы получить authorization code.
- /token — клиент обменивает code на access token.
- Реализуем небольшой «клиент» (CLI), который:
- открывает браузер для авторизации,
- после логина пользователя получает authorization code,
- обменивает код на токен и выводит его.
- Добавлю дополнительную точку /resource, чтобы проверить защиту токеном.
Установка
- Убедись, что установлен Python 3.8+.
- Установи Flask: pip install flask
- (Опционально) установи requests: pip install requests
- Код сервера авторизации (authorization_server.py) Код ниже реализует простой OAuth2-совместимый сервер с двумя конечными точками и базовым хранилищем пользователей и клиентов.
- Что делает:
- /authorize: принимает параметры client_id, redirect_uri, response_type=code, state. Если пользователь не авторизован — отправляет форму входа. Если авторизован — создает authorization code и перенаправляет по redirect_uri с code и state.
- /login: простая форма входа. После входа сохраняет в сессии пользователя и возвращает на исходную страницу.
- /token: принимает code, client_id, client_secret, redirect_uri и возвращает access_token.
- /resource: защищенный ресурс, доступный только по Bearer токену.
Код (authorization_server.py):
from flask import Flask, request, redirect, session, render_template_string, jsonify
import secrets, time
app = Flask(__name__)
app.secret_key = 'super_secret_key_for_demo' # в продакшене хранить в env
# Простые данные пользователей и клиентов (для учебы)
USERS = {
'alice': 'wonderland',
'bob': 'builder'
}
CLIENTS = {
'demo_client': {
'client_secret': 'demo_secret',
'redirect_uris': ['http://localhost:8000/callback']
}
}
# Хранилища кодов и токенов
AUTH_CODES = {} # code -> {client_id, redirect_uri, username, expires_at}
ACCESS_TOKENS = {} # access_token -> {username, client_id, expires_at}
def generate_code():
return secrets.token_urlsafe(32)
def generate_token():
return secrets.token_urlsafe(48)
@app.route('/authorize')
def authorize():
client_id = request.args.get('client_id')
redirect_uri = request.args.get('redirect_uri')
response_type = request.args.get('response_type')
state = request.args.get('state')
if client_id not in CLIENTS:
return "Unknown client", 400
if redirect_uri not in CLIENTS[client_id]['redirect_uris']:
return "Invalid redirect_uri", 400
if response_type != 'code':
return "Unsupported response_type", 400
if 'username' not in session:
# перенаправим на страницу логина, сохранив исходник в next
login_next = request.url
return redirect(f'/login?next={login_next}')
# пользователь авторизован — создаём authorization code
code = generate_code()
AUTH_CODES[code] = {
'client_id': client_id,
'redirect_uri': redirect_uri,
'username': session['username'],
'expires_at': time.time() + 300 # код действует 5 минут
}
# перенаправляем обратно к client с кодом
redirect_with_code = f'{redirect_uri}?code={code}'
if state:
redirect_with_code += f'&state={state}'
return redirect(redirect_with_code)
@app.route('/login', methods=['GET','POST'])
def login():
if request.method == 'GET':
next_url = request.args.get('next', '/')
return render_template_string('''
<h2>Login</h2>
<form method="post">
<input name="username" placeholder="Username"/><br/>
<input type="password" name="password" placeholder="Password"/><br/>
<input type="hidden" name="next" value="{{ next_url }}">
<button type="submit">Login</button>
</form>
''', next_url=next_url)
username = request.form.get('username')
password = request.form.get('password')
next_url = request.form.get('next') or '/'
if not username or USERS.get(username) != password:
return "Invalid credentials", 401
session['username'] = username
return redirect(next_url)
@app.route('/token', methods=['POST'])
def token():
grant_type = request.form.get('grant_type')
code = request.form.get('code')
redirect_uri = request.form.get('redirect_uri')
client_id = request.form.get('client_id')
client_secret = request.form.get('client_secret')
if grant_type != 'authorization_code':
return jsonify({'error':'unsupported_grant_type'}), 400
if client_id not in CLIENTS:
return jsonify({'error':'invalid_client'}), 401
if CLIENTS[client_id]['client_secret'] != client_secret:
return jsonify({'error':'invalid_client'}), 401
if code not in AUTH_CODES:
return jsonify({'error':'invalid_grant'}), 400
code_entry = AUTH_CODES.pop(code)
if code_entry['client_id'] != client_id:
return jsonify({'error':'invalid_grant'}), 400
if code_entry['redirect_uri'] != redirect_uri:
return jsonify({'error':'invalid_grant'}), 400
if time.time() > code_entry['expires_at']:
return jsonify({'error':'invalid_grant'}), 400
access_token = generate_token()
ACCESS_TOKENS[access_token] = {
'username': code_entry['username'],
'client_id': client_id,
'expires_at': time.time() + 3600
}
return jsonify({
'access_token': access_token,
'token_type': 'Bearer',
'expires_in': 3600
})
@app.route('/resource')
def resource():
auth_header = request.headers.get('Authorization')
if not auth_header or not auth_header.startswith('Bearer '):
return jsonify({'error':'unauthorized'}), 401
token = auth_header.split()[1]
token_entry = ACCESS_TOKENS.get(token)
if not token_entry or token_entry['expires_at'] < time.time():
return jsonify({'error':'invalid_token'}), 401
username = token_entry['username']
return jsonify({'data': f'Hello, {username}! This is protected data.'})
if __name__ == '__main__':
app.run(debug=True, port=5000)
- Клиентская часть (CLI-демонстрация) (oauth_client.py) Этот клиент демонстрирует как получить authorization code через браузер, а затем обменять его на access token. Для упрощения не пишем локальный обработчик редиректа — пользователь вручную копирует URL с кодом и вставляет в консоль.
- Что делает:
- формирует URL авторизации и открывает в браузере
- после логина пользователь копирует полный redirect URL из адресной строки и вставляет в консоль
- извлекает code и отправляет запрос на /token
- выводит access_token и пример запроса к защищенному ресурсу
Код (oauth_client.py):
import requests
import webbrowser
import urllib.parse
from urllib.parse import urlparse, parse_qs
SERVER = 'http://127.0.0.1:5000'
CLIENT_ID = 'demo_client'
CLIENT_SECRET = 'demo_secret'
REDIRECT_URI = 'http://localhost:8000/callback' # произвольный заглушечный URL для примера
def main():
# Шаг 1: построить URL авторизации
authorize_url = (
f"{SERVER}/authorize"
f"?response_type=code&client_id={CLIENT_ID}"
f"&redirect_uri={urllib.parse.quote(REDIRECT_URI)}"
f"&state=xyz"
)
print("Откройте этот URL в браузере для авторизации (браузер откроется автоматически):")
print(authorize_url)
webbrowser.open(authorize_url)
# Шаг 2: пользователь логинится на сервере авторизации,
# затем копирует полный redirect URL из адресной строки и вставляет сюда
redirected = input("Вставьте полный Redirect URL после авторизации: ").strip()
parsed = urlparse(redirected)
code = parse_qs(parsed.query).get('code')
if not code:
print("Не найден параметр code в redirect URL.")
return
code = code[0]
# Шаг 3: обменяем code на access_token
token_resp = requests.post(
f"{SERVER}/token",
data={
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': REDIRECT_URI,
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET
}
)
if token_resp.status_code != 200:
print("Ошибка обмена кода на токен:", token_resp.text)
return
token_data = token_resp.json()
access_token = token_data['access_token']
print("Получен access_token:", access_token)
# Шаг 4: пример доступа к защищенному ресурсу
resp = requests.get(
f"{SERVER}/resource",
headers={'Authorization': f"Bearer {access_token}"}
)
print("Ответ ресурса:", resp.status_code, resp.text)
if __name__ == '__main__':
main()
Как запустить и поэтапно проверить
- Запусти сервер авторизации:
- python authorization_server.py
- Он будет слушать на http://127.0.0.1:5000
- Открой клиентскую часть:
- Убедись, что в редирект URI клиента добавлен http://localhost:8000/callback в настройках клиента (в нашем примере это сделано в коде).
- python oauth_client.py
- Скрипт откроет браузер для авторизации. Введи учетку из USERS (alice/wonderland или bob/builder).
- После входа в браузере адресная строка покажет редирект с параметрами code и state. Скопируй полный URL и вставь в консоль там, где просит программа.
- Клиент обменяет code на токен и попытается обратиться к /resource, получив защищенные данные.
Пояснения по шагам
- Что такое authorization_code?
- Это одноразовый код, выданный пользователю после успешной аутентификации клиента и одобрения запрашиваемого доступа. Он служит временным мостом между сервером авторизации и клиентом.
- Зачем нужен redirect_uri и state?
- redirect_uri гарантирует, что код возвращается именно к нашему клиенту. state — дополнительный параметр защиты от CSRF; он можно использовать для валидации возвращенного запроса.
- Как работают token и access_token?
- После обмена кода на токен клиент получает access_token, который можно использовать для доступа к защищенным ресурсам. В реальной системе токен обычно JWT или opaque token и имеет срок жизни (expires_in).
Замечания и безопасность
- Этот пример рассчитан на учебные цели. Он не включает HTTPS, полноценные проверки, защиту от CSRF, хранение секретов в безопасном месте и пр.
- В продакшене обязательно используйте HTTPS, валидируйте все параметры, храните клиентские секреты в безопасном месте (например, секреты окружения), реализуйте дополнительные проверки и мониторинг.
Если хочешь, могу адаптировать пример под:
- другой язык/фреймворк (Node.js, Django, FastAPI и т.д.)
- полноценную oauth2-систему с поддержкой рефреш-токенов
- упрощенную версию без Authorization Server (любой другой сценарий)
Хочешь, чтобы я адаптировал под конкретный язык или добавил UI для авторизации?
Понравился ответ?
Задай свой вопрос
Напиши любую задачу или вопрос, а нейросеть её решит



