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

Что такое OAuth 2.0 с использованием Google или Яндекс?

1.7 Middle🔥 161 комментариев
#JavaScript Core

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

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

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

OAuth 2.0 - Безопасная аутентификация через внешних провайдеров

OAuth 2.0 - это стандартный протокол, который позволяет пользователям авторизоваться на твоем сайте, используя аккаунты Google, Яндекс или других сервисов. Это безопаснее, чем просить пароль, и удобнее для пользователя. Давайте разберёмся как это работает.

1. Основные концепции OAuth 2.0

OAuth 2.0 вводит несколько ключевых ролей:

const oauth2Roles = {
  'Resource Owner': 'Пользователь (у которого есть аккаунт в Google)',
  'Client (приложение)': 'Твой сайт / приложение',
  'Authorization Server': 'Сервер Google (выдаёт токены)',
  'Resource Server': 'API Google (хранит данные пользователя)'
};

// Процесс:
// 1. Пользователь нажимает "Войти через Google"
// 2. Твоё приложение перенаправляет на Google
// 3. Пользователь вводит пароль в Google (НЕ на твоём сайте)
// 4. Google выдаёт токен
// 5. Твоё приложение получает токен и создаёт сессию

2. Полный поток OAuth 2.0 с Google

Вот что происходит step-by-step:

// Шаг 1: Пользователь нажимает кнопку "Login with Google"
const step1 = {
  action: 'Пользователь видит на странице кнопку',
  code: '<button onClick="loginWithGoogle()">Login with Google</button>'
};

// Шаг 2: Фронтенд перенаправляет на Google
const step2 = {
  action: 'Редирект на Google Auth URL',
  url: 'https://accounts.google.com/o/oauth2/v2/auth',
  params: {
    client_id: 'YOUR_GOOGLE_CLIENT_ID',
    redirect_uri: 'https://yourapp.com/callback',  // Куда вернуться
    scope: 'openid email profile',  // Какие данные просим
    response_type: 'code',
    state: 'random_token_to_prevent_csrf'  // Защита от атак
  }
};

// Шаг 3: Пользователь авторизуется в Google
const step3 = {
  action: 'Пользователь вводит пароль Google',
  note: 'Это происходит на сервере Google, не на твоём сайте'
};

// Шаг 4: Google редирект с кодом авторизации
const step4 = {
  action: 'Google редирект обратно на твой сайт',
  url: 'https://yourapp.com/callback?code=AUTH_CODE&state=random_token',
  code: 'Код авторизации (временный, действителен 10 минут)'
};

// Шаг 5: Backend обменивает код на токен
const step5 = {
  action: 'Backend отправляет запрос на Google',
  method: 'POST https://oauth2.googleapis.com/token',
  body: {
    code: 'AUTH_CODE_FROM_STEP4',
    client_id: 'YOUR_GOOGLE_CLIENT_ID',
    client_secret: 'YOUR_GOOGLE_CLIENT_SECRET',  // НИКОГДА не в браузере!
    redirect_uri: 'https://yourapp.com/callback',
    grant_type: 'authorization_code'
  },
  response: {
    access_token: 'TOKEN_FOR_API_REQUESTS',
    id_token: 'JWT_TOKEN_WITH_USER_INFO',
    refresh_token: 'TOKEN_TO_GET_NEW_ACCESS_TOKEN_LATER',
    expires_in: 3600
  }
};

// Шаг 6: Backend использует токен для получения данных пользователя
const step6 = {
  action: 'Backend запрашивает данные пользователя',
  url: 'https://www.googleapis.com/oauth2/v2/userinfo',
  headers: {
    'Authorization': 'Bearer ACCESS_TOKEN'
  },
  response: {
    id: '123456789',
    email: 'user@gmail.com',
    name: 'John Doe',
    picture: 'https://lh3.googleusercontent.com/...'
  }
};

// Шаг 7: Backend создаёт пользователя и сессию
const step7 = {
  action: 'Backend создаёт своего пользователя',
  process: [
    'Проверить, существует ли пользователь с email user@gmail.com',
    'Если нет - создать нового пользователя',
    'Создать сессию / JWT токен',
    'Отправить сессию на фронтенд'
  ]
};

// Шаг 8: Пользователь авторизован
const step8 = {
  action: 'Пользователь вошёл в приложение',
  nextRequests: 'Все запросы отправляются с токеном сессии'
};

3. Практический пример - код на фронтенде (React)

import { GoogleOAuthProvider, GoogleLogin } from '@react-oauth/google';

function LoginPage() {
  const handleSuccess = async (credentialResponse) => {
    // credentialResponse.credential - JWT токен от Google
    const token = credentialResponse.credential;
    
    // Отправляем на backend
    const response = await fetch('/api/v1/auth/google', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ token })
    });
    
    const data = await response.json();
    
    // Сохраняем сессию
    localStorage.setItem('session_token', data.session_token);
    
    // Редирект на главную
    window.location.href = '/';
  };
  
  const handleError = () => {
    console.error('Login Failed');
  };
  
  return (
    <GoogleOAuthProvider clientId="YOUR_GOOGLE_CLIENT_ID">
      <GoogleLogin
        onSuccess={handleSuccess}
        onError={handleError}
      />
    </GoogleOAuthProvider>
  );
}

4. Практический пример - код на бэкенде (Python/FastAPI)

from google.auth.transport import requests
from google.oauth2 import id_token
from fastapi import APIRouter, HTTPException
from sqlalchemy.orm import Session

router = APIRouter(prefix="/api/v1/auth")

@router.post("/google")
async def google_login(payload: dict, db: Session):
    # Шаг 1: Верифицировать JWT токен от Google
    try:
        idinfo = id_token.verify_oauth2_token(
            payload['token'],
            requests.Request(),
            "YOUR_GOOGLE_CLIENT_ID"
        )
    except ValueError:
        raise HTTPException(status_code=401, detail="Invalid token")
    
    # Шаг 2: Извлечь информацию пользователя
    email = idinfo.get('email')
    name = idinfo.get('name')
    google_id = idinfo.get('sub')
    picture = idinfo.get('picture')
    
    # Шаг 3: Найти или создать пользователя
    user = db.query(User).filter(User.email == email).first()
    
    if not user:
        user = User(
            email=email,
            name=name,
            google_id=google_id,
            avatar_url=picture
        )
        db.add(user)
        db.commit()
    
    # Шаг 4: Создать сессию
    session_token = generate_jwt_token(user.id)
    
    return {
        "session_token": session_token,
        "user": {
            "id": user.id,
            "email": user.email,
            "name": user.name
        }
    }

5. Отличие ID Token от Access Token

const tokens = {
  idToken: {
    purpose: 'Содержит информацию о пользователе',
    format: 'JWT (можно декодировать и прочитать)',
    content: {
      sub: 'user_id',
      email: 'user@gmail.com',
      name: 'John',
      iat: 1234567890,
      exp: 1234571490
    },
    usage: 'Использовать для создания сессии на бэкенде',
    security: 'Нужно верифицировать подпись!'
  },
  
  accessToken: {
    purpose: 'Для доступа к Google API',
    format: 'Непрозрачный токен',
    usage: 'Отправлять в header Authorization при запросах к Google API',
    example: 'GET /oauth2/v2/userinfo -H "Authorization: Bearer ACCESS_TOKEN"',
    lifetime: '1 час (потом нужен refresh)'
  },
  
  refreshToken: {
    purpose: 'Для получения новых access tokens',
    lifetime: 'Долгоживущий (может быть до года)',
    usage: 'Сохранять на бэкенде, не отправлять на фронтенд',
    example: 'Если access_token истёк, используй refresh_token для получения нового'
  }
};

6. OAuth 2.0 с Яндексом

Процесс примерно такой же, но некоторые детали отличаются:

const yandexOAuth = {
  authUrl: 'https://oauth.yandex.ru/authorize',
  tokenUrl: 'https://oauth.yandex.ru/token',
  userinfoUrl: 'https://login.yandex.ru/info',
  
  params: {
    client_id: 'YOUR_YANDEX_CLIENT_ID',
    redirect_uri: 'https://yourapp.com/callback',
    response_type: 'code',
    state: 'random_string'
  },
  
  differences: [
    'Yandex не выдаёт ID token по умолчанию',
    'Нужно использовать access_token для получения информации',
    'Процесс немного отличается'
  ]
};

7. Безопасность OAuth 2.0

const security = {
  clientSecret: {
    rule: 'НИКОГДА не передавай client_secret на фронтенд',
    reason: 'Его может видеть любой в DevTools',
    correct: 'Используй только на защищённом бэкенде'
  },
  
  codeVerifier: {
    name: 'PKCE (Proof Key for Public Clients)',
    use: 'Для фронтенд-приложений без бэкенда',
    process: 'Генерируешь code_verifier на клиенте, отправляешь хеш на сервер'
  },
  
  state: {
    purpose: 'Защита от CSRF атак',
    rule: 'Всегда генерируй уникальный state и проверяй при возврате'
  },
  
  redirectUri: {
    rule: 'Регистри ровань в Google только те URI, на которые будешь редиректить',
    security: 'Иначе злоумышленник может перехватить код'
  }
};

8. Типичные ошибки

const mistakes = {
  'Не проверил подпись ID token': 'Может быть поддельный токен',
  'Передал client_secret на фронтенд': 'Уязвимость безопасности',
  'Не проверил state при возврате': 'CSRF атака возможна',
  'Сохранил refresh_token на фронтенде': 'Может быть украден из localStorage',
  'Не обработал истечение access_token': 'Запросы начнут падать'
};

9. Когда использовать OAuth

const useOAuth = [
  'Быстрая регистрация - пользователь не вводит пароль',
  'Безопасность - пароль не передаётся твоему серверу',
  'Удобство - один клик вместо заполнения формы',
  'Многофакторная аутентификация - если Google/Yandex требуют',
  'Универсальность - один аккаунт для всех сервисов'
];

const dontUseOAuth = [
  'Если тебе нужна максимальная контроль над аутентификацией',
  'Если требуется специальная логика (например, пригласительные коды)',
  'Если нужна полная независимость от внешних сервисов'
];

10. Что происходит если пользователь удалит приложение

const removal = {
  scenario: 'Пользователь отозвал доступ приложению в Google',
  process: [
    '1. Пользователь идёт в Google Account -> Security',
    '2. Удаляет твоё приложение из подключённых приложений',
    '3. Google аннулирует всё tokens (access и refresh)',
    '4. Твоё приложение получит 401 Unauthorized при следующем запросе'
  ],
  recovery: 'Отправь пользователя снова на авторизацию через Google'
};

Итог

OAuth 2.0 - это стандартный и безопасный способ аутентификации через Google, Яндекс и других провайдеров. Основной процесс: пользователь редиректится на Google -> авторизуется -> получает код -> бэкенд обменивает код на токен -> создаёт сессию. Это намного безопаснее, чем просить пароль, и удобнее для пользователя. Главное - не забывай о безопасности: никогда не передавай client_secret на фронтенд, проверяй state, верифицируй токены.