Rust SDK
Official OAuth42 SDK for Rust. Works with Actix Web, Axum, Rocket, and any Rust web framework.
Installation
Add to your Cargo.toml:
[dependencies]
o42sdk = "0.1"With framework support:
[dependencies]
o42sdk = { version = "0.1", features = ["actix", "axum"] }Quick Start
use o42sdk::{OAuth42Client, Config};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create client with builder pattern
let mut client = OAuth42Client::builder()
.issuer("https://api.oauth42.com")?
.client_id("your_client_id")
.client_secret("your_client_secret")
.redirect_uri("https://yourapp.com/callback")?
.scopes(vec!["openid", "profile", "email"])
.build()?;
// Discover endpoints automatically
client.discover().await?;
// Generate authorization URL with PKCE
let (auth_url, code_verifier) = client
.authorize_url().await?
.with_pkce()
.build()?;
println!("Visit: {}", auth_url);
// After user authorizes, exchange code for tokens
let tokens = client
.exchange_code(&code, Some(&code_verifier))
.await?;
println!("Access token: {}", tokens.access_token);
Ok(())
}Actix Web Integration
use actix_web::{web, App, HttpResponse, HttpServer};
use actix_session::{Session, SessionMiddleware, storage::CookieSessionStore};
use actix_web::cookie::Key;
use o42sdk::{OAuth42Client, Config};
async fn login(
client: web::Data<OAuth42Client>,
session: Session,
) -> Result<HttpResponse, actix_web::Error> {
let mut client = client.as_ref().clone();
let (auth_url, code_verifier) = client
.authorize_url().await
.map_err(actix_web::error::ErrorInternalServerError)?
.with_pkce()
.build()
.map_err(actix_web::error::ErrorInternalServerError)?;
session.insert("code_verifier", code_verifier)?;
Ok(HttpResponse::Found()
.append_header(("Location", auth_url.to_string()))
.finish())
}
async fn callback(
client: web::Data<OAuth42Client>,
session: Session,
query: web::Query<CallbackQuery>,
) -> Result<HttpResponse, actix_web::Error> {
let code_verifier = session
.get::<String>("code_verifier")?
.ok_or_else(|| actix_web::error::ErrorUnauthorized("No code verifier"))?;
let mut client = client.as_ref().clone();
let tokens = client
.exchange_code(&query.code, Some(&code_verifier))
.await
.map_err(actix_web::error::ErrorInternalServerError)?;
session.insert("access_token", tokens.access_token)?;
Ok(HttpResponse::Found()
.append_header(("Location", "/dashboard"))
.finish())
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let client = OAuth42Client::builder()
.issuer("https://api.oauth42.com").unwrap()
.client_id(std::env::var("OAUTH42_CLIENT_ID").unwrap())
.client_secret(std::env::var("OAUTH42_CLIENT_SECRET").unwrap())
.redirect_uri("https://yourapp.com/callback").unwrap()
.scopes(vec!["openid", "profile", "email"])
.build()
.unwrap();
let client_data = web::Data::new(client);
HttpServer::new(move || {
App::new()
.app_data(client_data.clone())
.wrap(SessionMiddleware::new(
CookieSessionStore::default(),
Key::generate(),
))
.route("/login", web::get().to(login))
.route("/callback", web::get().to(callback))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
#[derive(serde::Deserialize)]
struct CallbackQuery {
code: String,
}Axum Integration
use axum::{
extract::{Query, State},
http::StatusCode,
response::{IntoResponse, Redirect},
routing::get,
Router,
};
use axum_extra::extract::cookie::{Cookie, CookieJar};
use o42sdk::OAuth42Client;
use std::sync::Arc;
use tokio::sync::Mutex;
type AppState = Arc<Mutex<OAuth42Client>>;
async fn login(
State(client): State<AppState>,
jar: CookieJar,
) -> Result<(CookieJar, Redirect), StatusCode> {
let mut client = client.lock().await;
let (auth_url, code_verifier) = client
.authorize_url().await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
.with_pkce()
.build()
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let jar = jar.add(Cookie::new("code_verifier", code_verifier));
Ok((jar, Redirect::to(auth_url.as_str())))
}
#[derive(serde::Deserialize)]
struct CallbackParams {
code: String,
}
async fn callback(
State(client): State<AppState>,
jar: CookieJar,
Query(params): Query<CallbackParams>,
) -> Result<(CookieJar, Redirect), StatusCode> {
let code_verifier = jar
.get("code_verifier")
.ok_or(StatusCode::UNAUTHORIZED)?
.value()
.to_string();
let mut client = client.lock().await;
let tokens = client
.exchange_code(¶ms.code, Some(&code_verifier))
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let jar = jar.add(Cookie::new("access_token", tokens.access_token));
Ok((jar, Redirect::to("/dashboard")))
}
#[tokio::main]
async fn main() {
let client = OAuth42Client::builder()
.issuer("https://api.oauth42.com").unwrap()
.client_id(std::env::var("OAUTH42_CLIENT_ID").unwrap())
.client_secret(std::env::var("OAUTH42_CLIENT_SECRET").unwrap())
.redirect_uri("https://yourapp.com/callback").unwrap()
.scopes(vec!["openid", "profile", "email"])
.build()
.unwrap();
let state = Arc::new(Mutex::new(client));
let app = Router::new()
.route("/login", get(login))
.route("/callback", get(callback))
.with_state(state);
let listener = tokio::net::TcpListener::bind("127.0.0.1:8080")
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}Features
Type Safety
Full type safety with Result types and compile-time guarantees.
Async/Await
Built on Tokio for high-performance async operations.
Framework Support
Optional integrations for Actix Web, Axum, and Rocket.
Zero-Cost Abstractions
Minimal runtime overhead with compile-time optimizations.
Best Practices
Use Environment Variables
Store credentials securely using std::env or dotenvy crate.
Error Handling
Use Result types and the ? operator for idiomatic error handling.
Enable PKCE
Always use .with_pkce() for enhanced security.
TLS/SSL
Uses rustls for secure TLS connections by default.