Библиотека 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 может быть отозван или иметь ограниченный срок жизни. Всегда обрабатывайте ошибки обновления.
Интеграция с популярными провайдерами
Библиотека поддерживает настройку для разных сервисов