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

В чём разница между scope в OpenID и OAuth2?

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

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

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

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

Scope в OpenID Connect vs OAuth2: фундаментальные различия

Это очень важный вопрос, потому что многие разработчики путают OAuth2 и OpenID Connect, думая что это одно и то же. На самом деле это разные протоколы, и scope в них используется по-разному.

Первое: что это вообще

OAuth2 — это протокол АВТОРИЗАЦИИ

OAuth2 отвечает на вопрос:
"Какие ресурсы приложение может получить от пользователя?"

OpenID Connect — это слой АУТЕНТИФИКАЦИИ поверх OAuth2

OpenID Connect отвечает на вопрос:
"Кто это пользователь и какая у него информация?"
┌──────────────────────────────┐
│    OpenID Connect (Auth)     │  ← Узнаём КОГО мы авторизуем
├──────────────────────────────┤
│      OAuth2 (Authz)          │  ← Что разрешено делать
└──────────────────────────────┘

Scope в OAuth2

Что это

Scope в OAuth2 — это разрешения (permissions) для доступа к ресурсам. Это уровень АВТОРИЗАЦИИ.

Пользователь:
"Приложение просит доступ к моим:
- профилю
- календарю
- контактам

ОК или отклонить?"

Примеры scope'ов OAuth2

# Google OAuth2 scopes
scopes = [
    'https://www.googleapis.com/auth/userinfo.profile',  # Профиль
    'https://www.googleapis.com/auth/userinfo.email',    # Email
    'https://www.googleapis.com/auth/calendar',          # Календарь
    'https://www.googleapis.com/auth/drive',             # Google Drive
]

# GitHub OAuth2 scopes
scopes = [
    'repo',              # Полный доступ к репозиториям
    'read:user',         # Читать профиль
    'user:email',        # Читать email'ы
    'workflow',          # Действия с workflow
]

Как это работает (OAuth2 поток)

1. Приложение запрашивает authorization
https://provider.com/oauth/authorize?scope=profile,email

2. Пользователь видит:
"Приложение 'MyApp' запрашивает:
- Доступ к профилю
- Доступ к email

[Разрешить] [Отклонить]"

3. Если разрешить, приложение получает access_token

4. С этим token'ом приложение может получить ресурсы
GET /api/user/profile
Authorization: Bearer <access_token>

Ответ: {
  "name": "Alice",
  "email": "alice@example.com"
}

Пример: OAuth2 с Python

from authlib.integrations.flask_client import OAuth

oauth = OAuth()

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

@app.route('/login')
def login():
    redirect_uri = url_for('authorize', _external=True)
    return google.authorize_redirect(redirect_uri)

@app.route('/authorize')
def authorize():
    token = google.authorize_access_token()
    user_info = token.get('userinfo')  # Информация о пользователе
    return jsonify(user_info)

Scope в OpenID Connect

Что это

Scope в OpenID Connect — это какую информацию о пользователе мы хотим получить в ID token'е.

Это НЕ о доступе к ресурсам.
Это о том, какую ИНФОРМАЦИЮ О ПОЛЬЗОВАТЕЛЕ
получить в ID token'е.

Стандартные OpenID Connect scope'ы

# OpenID Connect scope'ы (стандартные)
scopes = [
    'openid',       # ОБЯЗАТЕЛЕН! Включает ID token
    'profile',      # name, family_name, given_name, picture, birthdate, etc.
    'email',        # email, email_verified
    'address',      # street_address, locality, postal_code, etc.
    'phone',        # phone_number, phone_number_verified
]

# Это NOT разрешения на действия!
# Это просто "включить эту информацию в ID token"

Содержимое ID Token (с разными scope'ами)

# Без scope (openid только)
{
    "iss": "https://provider.com",
    "sub": "user123",      # Subject ID (всегда)
    "aud": "client_id",
    "exp": 1234567890,
    "iat": 1234567800
}

# С scope: openid profile
{
    "iss": "https://provider.com",
    "sub": "user123",
    "aud": "client_id",
    "name": "Alice",
    "family_name": "Smith",
    "given_name": "Alice",
    "picture": "https://example.com/alice.jpg",
    "locale": "en-US"
}

# С scope: openid email
{
    "iss": "https://provider.com",
    "sub": "user123",
    "aud": "client_id",
    "email": "alice@example.com",
    "email_verified": true
}

# С scope: openid profile email
{
    "iss": "https://provider.com",
    "sub": "user123",
    "aud": "client_id",
    "name": "Alice",
    "email": "alice@example.com",
    "email_verified": true,
    "picture": "https://example.com/alice.jpg"
}

Пример: OpenID Connect с Python

from authlib.integrations.flask_client import OAuth
from authlib.jose import jwt

oauth = OAuth()

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

@app.route('/login')
def login():
    redirect_uri = url_for('authorize', _external=True)
    return Google.authorize_redirect(redirect_uri)

@app.route('/authorize')
def authorize():
    token = Google.authorize_access_token()
    
    # ID token содержит информацию о пользователе
    id_token = token.get('id_token')
    user_info = jwt.decode(id_token, options={'verify_signature': False})
    
    print(user_info)  # {
    #     "iss": "https://accounts.google.com",
    #     "sub": "110169547...",
    #     "name": "Alice",
    #     "email": "alice@example.com",
    #     "picture": "https://...",
    #     "email_verified": true
    # }
    
    # Создаём сессию пользователя
    session['user'] = user_info
    return redirect('/')

Сравнение OAuth2 vs OpenID Connect

АспектOAuth2OpenID Connect
НазначениеАВТОРИЗАЦИЯАУТЕНТИФИКАЦИЯ
Отвечает на"Что может делать?""Кто это?"
Возвращаетaccess_tokenID token + access_token
scopeРазрешения на ресурсыИнформация в ID token'е
Типичный scoperepo, calendar, driveopenid, profile, email
Нужна ли программа?Да (чтобы что-то делать)Нет (просто узнать кто)
ID tokenНет (не обязателен)Да (обязателен)

Реальная жизнь: пример с Google

OAuth2: доступ к Google Drive

Получаем доступ к файлам пользователя в Google Drive

scope: 'https://www.googleapis.com/auth/drive'
↓
access_token
↓
GET https://www.googleapis.com/drive/v3/files
Authorization: Bearer <access_token>
↓
[
  { id: "file1", name: "Document.docx" },
  { id: "file2", name: "Spreadsheet.xlsx" }
]

OpenID Connect: авторизация на сайте

Пользователь нажимает "Sign in with Google"

scope: 'openid profile email'
↓
ID token (содержит информацию)
↓
ID token декодирован:
{
  "sub": "110169547...",
  "name": "Alice",
  "email": "alice@example.com",
  "email_verified": true
}
↓
Заходим в приложение как alice@example.com

Пример: полный OpenID Connect поток

from flask import Flask, url_for, session, redirect
from authlib.integrations.flask_client import OAuth
from authlib.jose import jwt

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret'
oauth = OAuth(app)

# Регистрируем Google как OpenID Provider
google = oauth.register(
    name='google',
    client_id='YOUR_CLIENT_ID',
    client_secret='YOUR_CLIENT_SECRET',
    server_metadata_url='https://accounts.google.com/.well-known/openid-configuration',
    client_kwargs={
        'scope': 'openid profile email'  # OpenID Connect scope'ы
    }
)

@app.route('/')
def index():
    return '''<a href="/login">Sign in with Google</a>'''

@app.route('/login')
def login():
    redirect_uri = url_for('authorize', _external=True)
    return google.authorize_redirect(redirect_uri)

@app.route('/authorize')
def authorize():
    token = google.authorize_access_token()
    
    # Декодируем ID token (без проверки подписи для примера)
    id_token_str = token['id_token']
    user_info = jwt.decode(id_token_str, options={'verify_signature': False})
    
    # Сохраняем в сессию
    session['user'] = {
        'id': user_info['sub'],
        'name': user_info.get('name'),
        'email': user_info.get('email'),
        'picture': user_info.get('picture')
    }
    
    return redirect('/dashboard')

@app.route('/dashboard')
def dashboard():
    user = session.get('user')
    if not user:
        return redirect('/login')
    
    return f'''<html>
        <body>
            <h1>Welcome, {user['name']}!</h1>
            <p>Email: {user['email']}</p>
            <img src="{user['picture']}">
            <a href="/logout">Logout</a>
        </body>
    </html>'''

@app.route('/logout')
def logout():
    session.pop('user', None)
    return redirect('/')

if __name__ == '__main__':
    app.run()

Ошибки: что часто путают

❌ Ошибка 1: OAuth2 scope вместо OpenID

# НЕПРАВИЛЬНО (OAuth2, не OpenID)
scope='repo,user:email'  # GitHub scope'ы

# ПРАВИЛЬНО (OpenID Connect)
scope='openid profile email'

❌ Ошибка 2: смешивание OAuth2 и OpenID

# НЕПРАВИЛЬНО (микс)
scope='openid profile email drive'  # Смешиваем OpenID и OAuth2

# ПРАВИЛЬНО (оба есть, но разные)
scope='openid profile email'                   # OpenID
scope='https://www.googleapis.com/auth/drive'  # OAuth2

❌ Ошибка 3: использование userinfo endpoint вместо ID token

# НЕПРАВИЛЬНО (лишний запрос)
user_info = requests.get(
    'https://accounts.google.com/oauth/userinfo',
    headers={'Authorization': f'Bearer {access_token}'}
).json()

# ПРАВИЛЬНО (уже есть в ID token)
user_info = jwt.decode(id_token, options={'verify_signature': False})

Когда какой использовать

OAuth2 нужен когда:

  • Приложение должно "делать что-то" от имени пользователя
  • Получить доступ к файлам, календарю, почте
  • Опубликовать пост в соцсети
  • Изменить чужие ресурсы

OpenID Connect нужен когда:

  • Просто узнать кто пользователь
  • Авторизация на сайте
  • Заполнить профиль после регистрации
  • SSO (Single Sign-On)

Заключение

Scope в OAuth2 — это разрешения для доступа к API. Scope в OpenID Connect — это информация в ID token'е.

Слово "scope" одно, но использование принципиально разное.

Психологический лайфхак:

  • OAuth2 scope → "Могу ли я...?" (делать что-то)
  • OpenID scope → "Знаю ли я...?" (информацию о пользователе)
В чём разница между scope в OpenID и OAuth2? | PrepBro