atproto-xrpcs#
A Rust library providing core building blocks for implementing XRPC services in the AT Protocol ecosystem, with JWT-based authorization extractors for Axum web handlers and DID document verification support.
Overview#
atproto-xrpcs provides foundational components for building AT Protocol XRPC (Cross-Protocol Remote Procedure Call) services. This library offers JWT-based authorization extractors that integrate with Axum web handlers, enabling secure XRPC endpoints with automatic DID document verification and structured error handling.
This project was extracted from the open-sourced Smokesignal project and is designed to be a standalone, reusable library for AT Protocol XRPC service implementations.
Features#
- JWT Authorization Extractors: Axum-compatible request extractors for JWT-based authorization
- DID Document Verification: Automatic verification of caller identities through DID document resolution
- XRPC Service Components: Core building blocks for implementing AT Protocol XRPC endpoints
- Structured Error Handling: Comprehensive error types with detailed error codes and messages
- Axum Integration: Native integration with Axum web framework for HTTP handlers
- Identity Resolution: Built-in support for resolving AT Protocol identities and handles
- Security Features: Robust authorization patterns with cryptographic verification
Installation#
Add this to your Cargo.toml:
[dependencies]
atproto-xrpcs = "0.5.0"
Usage#
Basic XRPC Service Setup#
use atproto_xrpcs::authorization::ResolvingAuthorization;
use axum::{Json, Router, extract::Query, routing::get};
use serde::Deserialize;
use serde_json::json;
#[derive(Deserialize)]
struct HelloParams {
name: Option<String>,
}
// XRPC handler with optional JWT authorization
async fn handle_hello(
params: Query<HelloParams>,
authorization: Option<ResolvingAuthorization>,
) -> Json<serde_json::Value> {
let name = params.name.as_deref().unwrap_or("World");
let message = if authorization.is_some() {
format!("Hello, authenticated {}!", name)
} else {
format!("Hello, {}!", name)
};
Json(json!({ "message": message }))
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Set up your Axum router with XRPC endpoints
let app = Router::new()
.route("/xrpc/com.example.hello", get(handle_hello))
.with_state(your_web_context);
let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await?;
axum::serve(listener, app).await?;
Ok(())
}
JWT Authorization with DID Verification#
use atproto_xrpcs::authorization::ResolvingAuthorization;
use axum::{Json, extract::Query};
use serde::Deserialize;
use serde_json::json;
#[derive(Deserialize)]
struct SecureParams {
data: String,
}
// Handler that requires authentication
async fn handle_secure_endpoint(
params: Query<SecureParams>,
authorization: ResolvingAuthorization, // Required authorization
) -> Json<serde_json::Value> {
// The ResolvingAuthorization extractor automatically:
// 1. Validates the JWT token
// 2. Resolves the caller's DID document
// 3. Verifies the signature against the DID document
// 4. Provides access to caller identity information
let caller_did = authorization.subject();
Json(json!({
"message": "Secure operation completed",
"caller": caller_did,
"data": params.data
}))
}
Error Handling in XRPC Services#
use atproto_xrpcs::errors::AuthorizationError;
use axum::{response::IntoResponse, http::StatusCode};
// XRPC services can handle authorization errors appropriately
async fn protected_handler(
authorization: Result<ResolvingAuthorization, AuthorizationError>,
) -> impl IntoResponse {
match authorization {
Ok(auth) => {
// Handle authenticated request
(StatusCode::OK, "Access granted").into_response()
}
Err(AuthorizationError::InvalidJWTToken { .. }) => {
(StatusCode::UNAUTHORIZED, "Invalid token").into_response()
}
Err(AuthorizationError::DIDDocumentResolutionFailed { .. }) => {
(StatusCode::FORBIDDEN, "Identity verification failed").into_response()
}
Err(_) => {
(StatusCode::INTERNAL_SERVER_ERROR, "Authorization error").into_response()
}
}
}
Integration with AT Protocol Identity System#
use atproto_xrpcs::authorization::ResolvingAuthorization;
use atproto_identity::{
axum::state::DidDocumentStorageExtractor,
resolve::IdentityResolver,
};
use axum::extract::State;
// XRPC handlers can access the full AT Protocol identity system
async fn handle_identity_operation(
authorization: ResolvingAuthorization,
State(identity_resolver): State<IdentityResolver>,
State(storage): State<DidDocumentStorageExtractor>,
) -> Json<serde_json::Value> {
let caller_did = authorization.subject();
// Use the identity resolver for additional operations
// Access DID document storage for caching and retrieval
Json(json!({
"caller": caller_did,
"status": "Identity verified and processed"
}))
}
Modules#
- [
authorization] - JWT-based authorization extractors with DID document verification - [
errors] - Structured error types for XRPC authorization operations
Authorization Flow#
The ResolvingAuthorization extractor implements a comprehensive authorization flow:
- JWT Extraction: Extracts JWT tokens from HTTP Authorization headers
- Token Validation: Validates JWT signature and claims structure
- DID Resolution: Resolves the token issuer's DID document
- Signature Verification: Verifies the JWT signature against the DID document's public keys
- Identity Confirmation: Confirms the caller's identity and authorization scope
Error Handling#
All errors follow a structured format with unique identifiers:
error-atproto-xrpcs-<domain>-<number> <message>: <details>
Example error categories:
error-atproto-xrpcs-authorization-1througherror-atproto-xrpcs-authorization-15- JWT authorization errors
use atproto_xrpcs::errors::AuthorizationError;
// Example error handling
match authorization_result {
Err(AuthorizationError::MissingAuthorizationHeader) => {
println!("No authorization header provided");
}
Err(AuthorizationError::InvalidJWTToken { error }) => {
println!("Invalid JWT token: {}", error);
}
Err(AuthorizationError::DIDDocumentResolutionFailed { did, error }) => {
println!("Failed to resolve DID {}: {}", did, error);
}
Ok(auth) => println!("Authorization successful for: {}", auth.subject()),
}
Security Features#
- JWT Validation: Comprehensive JWT token structure and signature validation
- DID Verification: Automatic resolution and verification of caller DID documents
- Cryptographic Security: Integration with
atproto-identityfor secure key operations - Authorization Scope: Support for AT Protocol authorization scopes and permissions
- Identity Binding: Strong binding between JWT tokens and DID document public keys
Dependencies#
This crate builds on:
atproto-identity- Identity resolution and cryptographic operationsatproto-oauth- OAuth 2.0 operations and JWT handlingatproto-record- AT Protocol record operationsaxum- Web framework for HTTP handlers and extractorsreqwest- HTTP client for DID document resolutionserde_json- JSON handling for XRPC request/response datatokio- Async runtime for web service operationsthiserror- Structured error type derivation
AT Protocol XRPC#
This library implements components for AT Protocol XRPC, which provides:
- Cross-Protocol Communication: Standardized RPC over HTTP for AT Protocol services
- Schema-Driven APIs: Type-safe API definitions using Lexicon schemas
- Authentication Integration: Seamless integration with AT Protocol identity and authorization
- Service Discovery: Support for AT Protocol service discovery and routing
Library Only#
This crate is designed as a library and does not provide command line tools. All functionality is accessed programmatically through the Rust API. For a complete example implementation, see the atproto-xrpcs-helloworld crate which demonstrates a full XRPC service using these components.
Contributing#
Contributions are welcome! Please ensure that:
- All tests pass:
cargo test - Code is properly formatted:
cargo fmt - No linting issues:
cargo clippy - New functionality includes appropriate tests and documentation
- Error handling follows the project's structured error format
License#
This project is licensed under the MIT License. See the LICENSE file for details.
Acknowledgments#
This library was extracted from the Smokesignal project, an open-source event and RSVP management and discovery application.