OAuth клиент для Rust: библиотека oauth2-rs

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Библиотека oauth2-rs: реализация OAuth 2.0 на Rust

В современной веб-разработке OAuth 2.0 — стандартный протокол для делегирования доступа. Библиотека oauth2-rs предоставляет полную реализацию клиента OAuth 2.0 на языке Rust. Она позволяет интегрировать аутентификацию через популярные сервисы: Google, GitHub, Yandex, VK и другие.

Данная библиотека поддерживает все ключевые гранты (flows): Authorization Code, Client Credentials, Implicit, а также Refresh Token. Особое внимание уделено безопасности: используется PKCE (Proof Key for Code Exchange) для публичных клиентов и обязательная валидация токенов.

Установка oauth2-rs

Добавьте зависимость в Cargo.toml:

[dependencies]
oauth2 = "4.4"
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.11", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }

Также потребуется HTTP-клиент (рекомендуется reqwest с async-поддержкой) и сериализатор serde для работы с JSON.

Базовая настройка OAuth клиента

Рассмотрим пример для Google OAuth 2.0. Сначала создадим конфигурацию:

use oauth2::{
    AuthorizationCode, AuthUrl, ClientId, ClientSecret, 
    RedirectUrl, TokenUrl, CsrfToken, Scope,
    basic::BasicClient, TokenResponse
};

let client_id = ClientId::new("YOUR_GOOGLE_CLIENT_ID".to_string());
let client_secret = ClientSecret::new("YOUR_GOOGLE_CLIENT_SECRET".to_string());
let auth_url = AuthUrl::new("https://accounts.google.com/o/oauth2/v2/auth".to_string())
    .expect("Invalid auth URL");
let token_url = TokenUrl::new("https://oauth2.googleapis.com/token".to_string())
    .expect("Invalid token URL");

let client = BasicClient::new(
    client_id,
    Some(client_secret),
    auth_url,
    Some(token_url)
).set_redirect_uri(
    RedirectUrl::new("http://localhost:8080/callback".to_string())
        .expect("Invalid redirect URL")
);

Генерация URL для авторизации

Создаём ссылку, на которую нужно перенаправить пользователя:

let (auth_url, csrf_token) = client
    .authorize_url(CsrfToken::new_random)
    .add_scope(Scope::new("openid".to_string()))
    .add_scope(Scope::new("email".to_string()))
    .url();

println!("Перейдите по ссылке: {}", auth_url);
// Сохраните csrf_token для проверки при callback

CsrfToken защищает от атак подделки межсайтовых запросов (CSRF). Его нужно временно сохранить (например, в сессии или куках) и проверить при обработке callback.

Обработка callback и получение токена

После того как пользователь подтвердит доступ, сервис перенаправит его на redirect_uri с параметром code. Пример обработчика на Rust:

use actix_web::{web, HttpRequest, HttpResponse};
use oauth2::AuthorizationCode;

async fn callback(query: web::Query<std::collections::HashMap<String, String>>) -> HttpResponse {
    let code = query.get("code")
        .expect("Missing authorization code");
    let state = query.get("state")
        .expect("Missing state");

    // Проверяем state против сохранённого csrf_token
    // ...

    let token_result = client
        .exchange_code(AuthorizationCode::new(code.clone()))
        .request_async(&reqwest::Client::new())
        .await;

    match token_result {
        Ok(token) => {
            let access_token = token.access_token().secret();
            let refresh_token = token.refresh_token()
                .map(|rt| rt.secret().clone());
            // Сохраняем токены в БД или сессии
            HttpResponse::Ok().body("Авторизация успешна!")
        },
        Err(e) => {
            eprintln!("Ошибка обмена кода: {:?}", e);
            HttpResponse::InternalServerError().body("Ошибка авторизации")
        }
    }
}

Работа с Refresh Token

Access token обычно живёт недолго (1 час). Для продления сессии используйте refresh token:

let refresh_token = RefreshToken::new("stored_refresh_token".to_string());
let new_token = client
    .exchange_refresh_token(&refresh_token)
    .request_async(&reqwest::Client::new())
    .await
    .expect("Failed to refresh token");

let new_access_token = new_token.access_token().secret();
println!("Новый access token: {}", new_access_token);

Важно: refresh token может быть отозван или иметь ограниченный срок жизни. Всегда обрабатывайте ошибки обновления.

Интеграция с популярными провайдерами

Библиотека поддерживает настройку для разных сервисов

Рекомендаций