A library for ATProtocol identities.

atproto-identity-rs#

A comprehensive collection of Rust crates for building AT Protocol applications. This workspace provides identity management, record operations, OAuth 2.0 flows, HTTP client operations, XRPC services, and event streaming capabilities.

Parts of this project were extracted from the open-source smokesignal.events project and are licensed under the MIT license.

Components#

Core Libraries#

  • atproto-identity - Identity management with DID resolution and cryptographic operations. Includes 4 CLI tools for identity resolution, key management, signing, and validation.
  • atproto-record - AT Protocol record signature operations. Includes 2 CLI tools for signing and verifying records.
  • atproto-oauth - OAuth 2.0 implementation with AT Protocol security extensions including PKCE, DPoP, and JWT operations.
  • atproto-oauth-aip - AT Protocol Identity Provider (AIP) OAuth workflow implementation for client applications.
  • atproto-client - HTTP client with DPoP authentication and repository operations. Includes 3 CLI tools for client authentication testing.

Web Framework Integration#

Event Streaming#

  • atproto-jetstream - WebSocket event stream consumer with compression support. Includes 1 CLI tool for event consumption.

Quick Start#

Add the crates to your Cargo.toml:

[dependencies]
atproto-identity = "0.8.0"
atproto-record = "0.8.0"
atproto-oauth = "0.8.0"
atproto-oauth-aip = "0.8.0"
atproto-client = "0.8.0"
# Add others as needed

Basic Identity Resolution#

use atproto_identity::resolve::{resolve_subject, create_resolver};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let http_client = reqwest::Client::new();
    let dns_resolver = create_resolver(&[]);
    
    let did = resolve_subject(&http_client, &dns_resolver, "alice.bsky.social").await?;
    println!("Resolved DID: {}", did);
    
    Ok(())
}

Record Signing#

use atproto_identity::key::identify_key;
use atproto_record::signature;
use serde_json::json;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let signing_key = identify_key("did:key:zQ3shNzMp4oaaQ1gQRzCxMGXFrSW3NEM1M9T6KCY9eA7HhyEA")?;
    
    let record = json!({
        "$type": "app.bsky.feed.post",
        "text": "Hello AT Protocol!",
        "createdAt": "2024-01-01T00:00:00.000Z"
    });
    
    let signature_object = json!({
        "issuer": "did:plc:issuer123",
        "issuedAt": "2024-01-01T00:00:00.000Z"
    });
    
    let signed_record = signature::create(
        &signing_key,
        &record,
        "did:plc:user123",
        "app.bsky.feed.post",
        signature_object,
    ).await?;
    
    Ok(())
}

XRPC Service#

use atproto_xrpcs::authorization::ResolvingAuthorization;
use axum::{Json, Router, extract::Query, routing::get};
use serde::Deserialize;
use serde_json::json;

#[derive(Deserialize)]
struct HelloParams {
    subject: Option<String>,
}

async fn handle_hello(
    params: Query<HelloParams>,
    authorization: Option<ResolvingAuthorization>,
) -> Json<serde_json::Value> {
    let subject = params.subject.as_deref().unwrap_or("World");
    
    let message = if let Some(auth) = authorization {
        format!("Hello, authenticated {}! (caller: {})", subject, auth.subject())
    } else {
        format!("Hello, {}!", subject)
    };
    
    Json(json!({ "message": message }))
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    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(())
}

OAuth Client Flow#

use atproto_oauth_aip::{OAuthClient, oauth_init, oauth_complete, session_exchange};
use atproto_oauth::storage::MemoryStorage;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = OAuthClient::new(
        "https://your-app.com/client-id".to_string(),
        Some("your-client-secret".to_string()),
        "https://your-app.com/callback".to_string(),
    );
    
    let storage = MemoryStorage::new();
    
    // Start OAuth flow
    let (authorization_url, state) = oauth_init(
        &client,
        "alice.bsky.social",
        &storage,
    ).await?;
    
    println!("Visit: {}", authorization_url);
    
    // After callback with code...
    let access_token = oauth_complete(
        &client,
        "auth-code",
        "state",
        &storage,
    ).await?;
    
    // Get AT Protocol session
    let session = session_exchange(&client, &access_token, &storage).await?;
    println!("Authenticated as: {} ({})", session.handle, session.did);
    
    Ok(())
}

Command Line Tools#

The workspace includes 12 command-line tools across the crates. All CLI tools require the clap feature:

# Build with CLI support
cargo build --features clap --bins

# Identity operations (atproto-identity crate)
cargo run --features clap --bin atproto-identity-resolve -- alice.bsky.social
cargo run --features clap --bin atproto-identity-key -- generate p256
cargo run --features clap --bin atproto-identity-sign -- did:key:... data.json
cargo run --features clap --bin atproto-identity-validate -- did:key:... data.json signature

# Record operations (atproto-record crate)
cargo run --features clap --bin atproto-record-sign -- did:key:... did:plc:issuer record.json repository=did:plc:user collection=app.bsky.feed.post
cargo run --features clap --bin atproto-record-verify -- did:plc:issuer did:key:... signed_record.json repository=did:plc:user collection=app.bsky.feed.post

# Client operations (atproto-client crate)
cargo run --features clap --bin atproto-client-auth -- login alice.bsky.social password123
cargo run --features clap --bin atproto-client-app-password -- alice.bsky.social access_token /xrpc/com.atproto.repo.listRecords
cargo run --features clap --bin atproto-client-dpop -- alice.bsky.social did:key:... access_token /xrpc/com.atproto.repo.listRecords

# OAuth operations (atproto-oauth-axum crate)  
cargo run --features clap --bin atproto-oauth-tool -- login did:key:... alice.bsky.social

# XRPC service (atproto-xrpcs-helloworld crate)
cargo run --bin atproto-xrpcs-helloworld

# Event streaming (atproto-jetstream crate)
cargo run --features clap --bin atproto-jetstream-consumer -- jetstream1.us-east.bsky.network dictionary.zstd

Development#

# Build all crates
cargo build

# Run all tests  
cargo test

# Format and lint
cargo fmt && cargo clippy

# Generate documentation
cargo doc --workspace --open

License#

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments#

This library was extracted from the smokesignal.events project, an open-source AT Protocol event and RSVP management application.