···2233A 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.
4455-Parts of this project were extracted from the open-source [smokesignal.events](https://tangled.sh/@smokesignal.events/smokesignal) project and are licensed under the MIT license.
55+**Note**: This project contains components extracted from the open-source [smokesignal.events](https://tangled.sh/@smokesignal.events/smokesignal) project, an AT Protocol event and RSVP management application. This library is released under the MIT license.
6677## Components
88
+1-2
crates/atproto-client/src/client.rs
···652652 })?)
653653}
654654655655-656655/// Performs an app password-authenticated HTTP POST request with JSON body and returns the response as bytes.
657656///
658657/// This is useful when the server returns binary data such as images, CAR files,
···704703 url: url.to_string(),
705704 error,
706705 })?)
707707-}706706+}
+52-1
crates/atproto-oauth-aip/src/lib.rs
···11-//! AT Protocol OAuth AIP implementation.
11+//! # AT Protocol OAuth AIP (Identity Provider) Implementation
22+//!
33+//! This crate provides a comprehensive OAuth 2.0 workflow implementation for AT Protocol
44+//! Identity Providers (AIPs). It handles the complete OAuth flow including Pushed
55+//! Authorization Requests (PAR), token exchange, and session management according to
66+//! AT Protocol specifications.
77+//!
88+//! ## Key Features
99+//!
1010+//! - **OAuth 2.0 Authorization Code Flow**: Complete implementation with PKCE support
1111+//! - **Pushed Authorization Requests (PAR)**: Enhanced security through server-side request storage
1212+//! - **Token Exchange**: Secure token issuance and refresh capabilities
1313+//! - **Session Management**: AT Protocol session establishment and validation
1414+//! - **Resource Validation**: OAuth protected resource and authorization server validation
1515+//!
1616+//! ## Usage
1717+//!
1818+//! The primary entry point is the workflow module which provides functions for each
1919+//! stage of the OAuth flow:
2020+//!
2121+//! ```rust,no_run
2222+//! use atproto_oauth_aip::workflow::{oauth_init, oauth_complete, session_exchange};
2323+//! use atproto_oauth_aip::resources::{oauth_protected_resource, oauth_authorization_server};
2424+//!
2525+//! // Initialize OAuth flow with PAR
2626+//! let auth_url = oauth_init(
2727+//! &oauth_client,
2828+//! &authorization_server,
2929+//! "user_handle",
3030+//! "https://redirect.example.com/callback"
3131+//! ).await?;
3232+//!
3333+//! // Complete OAuth flow with authorization code
3434+//! let token_response = oauth_complete(
3535+//! &oauth_client,
3636+//! &authorization_server,
3737+//! &oauth_request,
3838+//! "authorization_code"
3939+//! ).await?;
4040+//!
4141+//! // Exchange tokens for AT Protocol session
4242+//! let session = session_exchange(
4343+//! &protected_resource,
4444+//! &token_response.access_token,
4545+//! &dpop_key
4646+//! ).await?;
4747+//! ```
4848+//!
4949+//! ## Error Handling
5050+//!
5151+//! All operations use structured error types with descriptive messages following
5252+//! the project's error convention format.
253#![warn(missing_docs)]
354455/// Error types for OAuth workflow operations.
+68
crates/atproto-oauth-aip/src/workflow.rs
···11+//! # OAuth 2.0 Workflow Implementation for AT Protocol Identity Providers
22+//!
33+//! This module provides a complete OAuth 2.0 authorization code flow implementation
44+//! specifically designed for AT Protocol Identity Providers (AIPs). It handles the
55+//! three main phases of OAuth authentication: initialization, completion, and session exchange.
66+//!
77+//! ## Workflow Overview
88+//!
99+//! The OAuth workflow consists of three main functions that handle different phases:
1010+//!
1111+//! 1. **Initialization (`oauth_init`)**: Creates a Pushed Authorization Request (PAR)
1212+//! and returns the authorization URL for user consent
1313+//! 2. **Completion (`oauth_complete`)**: Exchanges the authorization code for access tokens
1414+//! 3. **Session Exchange (`session_exchange`)**: Converts OAuth tokens to AT Protocol sessions
1515+//!
1616+//! ## Security Features
1717+//!
1818+//! - **Pushed Authorization Requests (PAR)**: Enhanced security by storing authorization
1919+//! parameters server-side rather than in redirect URLs
2020+//! - **PKCE (Proof Key for Code Exchange)**: Protection against authorization code
2121+//! interception attacks
2222+//! - **DPoP (Demonstration of Proof-of-Possession)**: Cryptographic binding of tokens
2323+//! to specific keys for enhanced security
2424+//!
2525+//! ## Usage Example
2626+//!
2727+//! ```rust,no_run
2828+//! use atproto_oauth_aip::workflow::{oauth_init, oauth_complete, session_exchange, OAuthClient};
2929+//! use atproto_oauth::resources::{AuthorizationServer, OAuthProtectedResource};
3030+//!
3131+//! // 1. Initialize OAuth flow
3232+//! let oauth_client = OAuthClient {
3333+//! redirect_uri: "https://myapp.com/callback".to_string(),
3434+//! client_id: "my_client_id".to_string(),
3535+//! client_secret: "my_client_secret".to_string(),
3636+//! };
3737+//!
3838+//! let auth_url = oauth_init(
3939+//! &oauth_client,
4040+//! &authorization_server,
4141+//! "user.bsky.social",
4242+//! "https://myapp.com/callback"
4343+//! ).await?;
4444+//!
4545+//! // User visits auth_url and grants consent, returns with authorization code
4646+//!
4747+//! // 2. Complete OAuth flow
4848+//! let token_response = oauth_complete(
4949+//! &oauth_client,
5050+//! &authorization_server,
5151+//! &oauth_request,
5252+//! "received_auth_code"
5353+//! ).await?;
5454+//!
5555+//! // 3. Exchange for AT Protocol session
5656+//! let session = session_exchange(
5757+//! &protected_resource,
5858+//! &token_response.access_token,
5959+//! &dpop_private_key
6060+//! ).await?;
6161+//! ```
6262+//!
6363+//! ## Error Handling
6464+//!
6565+//! All functions return `Result<T, OAuthWorkflowError>` with detailed error information
6666+//! for each phase of the OAuth flow including network failures, parsing errors,
6767+//! and protocol violations.
6868+169use crate::errors::OAuthWorkflowError;
270use anyhow::Result;
371use atproto_oauth::{
···3939impl OAuthClientConfig {
4040 /// Returns the OAuth scope, using the default "atproto transition:generic" if not set.
4141 pub fn scope(&self) -> &str {
4242- self.scope.as_deref().unwrap_or("atproto transition:generic")
4242+ self.scope
4343+ .as_deref()
4444+ .unwrap_or("atproto transition:generic")
4345 }
4446}
4547
+13-9
crates/atproto-oauth/src/dpop.rs
···13621362 use atproto_identity::key::{KeyType, generate_key};
1363136313641364 let key_data = generate_key(KeyType::P256Private)?;
13651365-13651365+13661366 // Create a DPoP token with a nonce by manually building it
13671367 let (_, header, mut claims) = auth_dpop(&key_data, "POST", "https://example.com/token")?;
13681368-13681368+13691369 // Add nonce to claims
13701370 let test_nonce = "test_nonce_12345";
13711371- claims.private.insert("nonce".to_string(), test_nonce.into());
13721372-13711371+ claims
13721372+ .private
13731373+ .insert("nonce".to_string(), test_nonce.into());
13741374+13731375 // Create the token with nonce
13741376 let dpop_token = mint(&key_data, &header, &claims)?;
13751377···13891391 use atproto_identity::key::{KeyType, generate_key};
1390139213911393 let key_data = generate_key(KeyType::P256Private)?;
13921392-13931393- // Create a DPoP token with a specific nonce
13941394+13951395+ // Create a DPoP token with a specific nonce
13941396 let (_, header, mut claims) = auth_dpop(&key_data, "POST", "https://example.com/token")?;
13951395-13971397+13961398 // Add a nonce that won't match the expected values
13971399 let token_nonce = "token_nonce_that_wont_match";
13981398- claims.private.insert("nonce".to_string(), token_nonce.into());
13991399-14001400+ claims
14011401+ .private
14021402+ .insert("nonce".to_string(), token_nonce.into());
14031403+14001404 // Create the token with nonce
14011405 let dpop_token = mint(&key_data, &header, &claims)?;
14021406