Architecture
Overview
This chapter describes the architecture of the oauth2-passkey library.
Current Components
- demo-both: Example Axum application that uses both OAuth2 and passkey authentication
- demo-oauth2: Example Axum application using OAuth2-only authentication
- demo-passkey: Example Axum application using passkey-only authentication
- demo-custom-login: Example Axum application with custom login and account pages
- demo-profile: Example Axum application demonstrating user profile extension
- demo-todo: Example Axum application demonstrating app data linked to users
- demo-cross-origin: Example Axum application demonstrating cross-origin authentication (Auth + API servers)
- oauth2_passkey_axum: Provides OAuth2 and passkey authentication handlers for Axum applications
- Includes routers for OAuth2, passkey, and user account endpoints
- Handles HTTP-specific concerns like request/response handling
- oauth2_passkey: Core authentication coordination library
- config: Environment variable configuration management
- coordination: Central coordination layer that orchestrates authentication flows
- oauth2: OAuth2 authentication operations, stores OAuth2 accounts
- passkey: Passkey/WebAuthn operations, stores passkey credentials
- session: Session management using cache store, provides session cookies
- storage: Cache and SQL store providers (PostgreSQL and SQLite support)
- userdb: User database operations, provides user_id management
- utils: Common utility functions
- test_utils: Testing utilities for unit and integration tests
Component Responsibilities
oauth2_passkey (Core Library)
-
coordination: Provides a unified API for authentication operations
- Orchestrates the authentication flows between different modules
- Handles error mapping and coordination between components
- Exposes high-level functions for authentication operations
-
oauth2: Handles OAuth2 authentication
- Manages OAuth2 provider integration
- Stores and retrieves OAuth2 accounts
- Handles OAuth2 authentication flow (authorization, token exchange)
-
passkey: Handles WebAuthn/Passkey authentication
- Manages passkey registration and authentication
- Stores and retrieves passkey credentials
- Implements WebAuthn protocol for credential verification
-
session: Manages user sessions
- Creates and validates session tokens
- Handles session cookies and page session tokens
- Provides user information from sessions
-
storage: Provides data persistence
- Implements cache storage for temporary data
- Provides SQL database access for persistent data
- Supports both PostgreSQL and SQLite
-
userdb: Manages user accounts
- Creates and updates user records
- Provides user lookup functionality
- Links authentication methods to user accounts
oauth2_passkey_axum (Axum Integration)
- Provides Axum-specific HTTP handlers and routers
- Translates between HTTP requests/responses and core library functions
- Manages authentication middleware for Axum applications
Security Considerations
- Session tokens are securely managed with proper expiration
- Page session tokens provide protection against session desynchronization
- Passkey credentials follow WebAuthn security standards
- OAuth2 implementation follows best practices for authorization flow
Data Flow
┌─────────────────┐
│ Browser │
└────────┬────────┘
│ HTTP Request
▼
┌─────────────────────────────────────────────────────────────────────┐
│ oauth2_passkey_axum │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Router │───▶│ Handlers │───▶│ Static Assets (UI) │ │
│ └─────────────┘ └──────┬──────┘ └─────────────────────────┘ │
└────────────────────────────┼────────────────────────────────────────┘
│ calls
▼
┌─────────────────────────────────────────────────────────────────────┐
│ oauth2_passkey │
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ coordination │ │
│ │ (orchestrates authentication flows) │ │
│ └───────┬─────────────┬─────────────┬─────────────┬─────────────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ oauth2 │ │ passkey │ │ session │ │ userdb │ │
│ │ │ │ │ │ │ │ │ │
│ │ Google │ │ WebAuthn │ │ cookies │ │ accounts │ │
│ │ OIDC │ │ FIDO2 │ │ tokens │ │ linking │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │ │
│ └─────────────┴──────┬──────┴─────────────┘ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ storage │ │
│ │ ┌─────────────────────┐ ┌──────────────────────────────┐ │ │
│ │ │ cache_store │ │ data_store │ │ │
│ │ │ (session, CSRF, │ │ (users, credentials, │ │ │
│ │ │ WebAuthn state) │ │ OAuth2 accounts) │ │ │
│ │ └──────────┬──────────┘ └───────────────┬──────────────┘ │ │
│ └─────────────┼───────────────────────────────┼─────────────────┘ │
└────────────────┼───────────────────────────────┼────────────────────┘
│ │
▼ ▼
┌────────────────┐ ┌────────────────┐
│ Memory / Redis │ │ SQLite / PgSQL │
└────────────────┘ └────────────────┘
Request Flow Example (Passkey Authentication)
- Browser sends authentication request to
/o2p/passkey/auth/start - Router dispatches to passkey handler in
oauth2_passkey_axum - Handler calls
coordinationlayer inoauth2_passkey - Coordination orchestrates:
sessionvalidates existing session statepasskeygenerates WebAuthn challenge (stored incache_store)
- Response returns to browser with challenge
- Browser completes WebAuthn ceremony, sends assertion
passkeyverifies assertion against stored credential (data_store)sessioncreates authenticated session (stored incache_store)userdbretrieves user information- Response returns with session cookie
Key Design Points
- coordination is the central orchestration layer - all auth flows go through it
- oauth2 and passkey modules are independent (no cross-dependencies)
- cache_store handles temporary data (sessions, CSRF tokens, WebAuthn challenges)
- data_store handles persistent data (users, credentials, OAuth2 accounts)
Why Singleton Pattern Instead of Axum State
In typical Axum applications, shared resources (database pools, caches) are passed to handlers via the State<T> extractor. This library takes a different approach: it uses global static storage initialized by init().
This design means:
- For library users: You don’t need to manage state - just call
init().await?and merge the router - Internally: Any function can access configuration and database connections without threading state through every function argument
oauth2_passkey_axum::init().await?;
let app = Router::new()
.merge(oauth2_passkey_full_router());
This provides a simpler API at the cost of some flexibility. For a detailed comparison of both approaches and the trade-offs involved, see Storage Pattern.
Future Directions
- Further consolidation of related functionality
- Enhanced error handling and logging with standardization on thiserror
- Additional authentication methods
- Improved documentation and examples