# Passki
A simple, secure, and easy-to-use WebAuthn/Passkey implementation for Rust.
[](https://crates.io/crates/passki)
[](https://docs.rs/passki)
[](https://github.com/gblach/passki#license)
## Features
- ✨ **Simple API** - Easy-to-use interface for passkey registration and authentication
- 🔐 **Multiple Algorithms** - Support for EdDSA (Ed25519), ES256 (P-256), and RS256 (RSA)
- 🛡️ **Security First** - Built-in replay attack protection via signature counters
- 📦 **Zero Runtime Dependencies** - Only cryptography libraries, no web framework lock-in
- ✅ **WebAuthn Level 2 Compliant** - Follows the latest W3C specification
- 🦀 **Pure Rust** - Memory-safe implementation with no unsafe code
## Installation
Add this to your `Cargo.toml`:
```toml
[dependencies]
passki = "0.1"
```
## Quick Start
```rust
use passki::{
Passki, AttestationConveyancePreference, ResidentKeyRequirement,
UserVerificationRequirement, StoredPasskey,
};
// Initialize Passki with your relying party information
let passki = Passki::new(
"example.com", // Relying Party ID (domain)
"https://example.com", // Relying Party Origin
"Example Corp" // Relying Party Name
);
// Registration flow
// Step 1: Start registration and send challenge to client
let user_id = b"unique_user_identifier_12345"; // At least 16 bytes
let (registration_challenge, registration_state) = passki.start_passkey_registration(
user_id, // User ID (bytes)
"alice@example.com", // Username
"Alice Smith", // Display name
60000, // Timeout (ms)
AttestationConveyancePreference::None, // Attestation
ResidentKeyRequirement::Preferred, // Resident key
UserVerificationRequirement::Preferred, // User verification
None, // Exclude existing credentials
).expect("user_id must be at least 16 bytes");
// Send registration_challenge to client (as JSON)
// Client uses WebAuthn API to create credential
// Step 2: Receive credential from client and complete registration
let stored_passkey = passki.finish_passkey_registration(
®istration_credential, // Credential from client
®istration_state, // State from step 1
)?;
// Save stored_passkey to your database associated with the user
// Authentication flow
// Step 1: Start authentication and send challenge to client
let (authentication_challenge, authentication_state) = passki.start_passkey_authentication(
&user_passkeys, // User's stored passkeys
60000, // Timeout (ms)
UserVerificationRequirement::Preferred, // User verification
);
// Send authentication_challenge to client (as JSON)
// Client uses WebAuthn API to sign the challenge
// Step 2: Receive credential from client and verify authentication
let result = passki.finish_passkey_authentication(
&authentication_credential, // Credential from client
&authentication_state, // State from step 1
&stored_passkey, // User's passkey from database
)?;
// Update the counter in your database to prevent replay attacks
stored_passkey.counter = result.counter;
```
## Supported Algorithms
Passki supports the following COSE algorithms:
- **EdDSA** (Ed25519) - Algorithm ID: -8
- **ES256** (ECDSA with P-256 and SHA-256) - Algorithm ID: -7
- **RS256** (RSASSA-PKCS1-v1_5 with SHA-256) - Algorithm ID: -257
## Security Considerations
- 🔒 **Always use HTTPS in production** to prevent man-in-the-middle attacks
- 🔄 **Update signature counters** after successful authentication to detect cloned authenticators
- 🎯 **Verify origin matches** your expected domain (Passki does this automatically)
- 💾 **Store passkeys securely** in your database with proper access controls
- ⏱️ **Set appropriate timeouts** for registration and authentication ceremonies
- 🔐 **Use user verification** when handling sensitive operations
## Architecture
Passki follows a simple two-step pattern for both registration and authentication:
1. **Start**: Generate a challenge and return it to the client
2. **Finish**: Verify the response from the client
This design keeps state management simple and allows you to store session data however you prefer (in-memory, Redis, database, etc.).
## Requirements
- Rust 1.85 or later (Edition 2024)
- A web server to handle HTTP requests
- HTTPS in production (required by WebAuthn specification)
## Examples
Check out the `examples/` directory for a complete working example:
- `poem.rs` - Full integration with Poem web framework showing registration and authentication flows
Run the example:
```bash
cargo run --example poem
```
Then visit `http://localhost:3000` in your browser.
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
This project is licensed under the Apache License, Version 2.0 ([LICENSE](LICENSE)
or http://www.apache.org/licenses/LICENSE-2.0).
## Acknowledgments
Passki is built on top of excellent Rust cryptography libraries:
- [ed25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek) - EdDSA signatures
- [p256](https://github.com/RustCrypto/elliptic-curves) - ECDSA with P-256
- [rsa](https://github.com/RustCrypto/RSA) - RSA signatures
- [ciborium](https://github.com/enarx/ciborium) - CBOR encoding/decoding
## Resources
- [WebAuthn Specification](https://www.w3.org/TR/webauthn-2/)
- [FIDO Alliance](https://fidoalliance.org/)
- [WebAuthn Guide](https://webauthn.guide/)