yubico-rs

A Rust client for Yubico's OTP validation API, letting apps verify YubiKey one-time passwords over HTTP.

SDK
Cargo
v0.11.0
56stars
MIT OR Apache-2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
34/100Needs Attention
Development Activity0
Maintenance20
Community44
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
53/100Fair
Architecture65
Code Quality35
Innovation55
Learning Curve55

yubico-rs is a Rust client library for Yubico’s OTP validation web service, the API YubiKey hardware tokens rely on to confirm a one-time password is genuine. It builds the signed validation request (client ID, nonce, HMAC-SHA1 signature over the query string) required by the Validation Protocol V2.0, sends it to Yubico’s redundant api[1-5].yubico.com endpoints, and verifies the signed response — checking OTP, nonce, and signature — before returning a pass/fail result to the caller.

Both blocking and async entry points share the same core request-building and verification logic: sync_verifier::verify fires the request to all configured API hosts in parallel via a thread pool and returns on the first success, while async_verifier::verify_async (enabled by the default online-tokio feature) does the same over FuturesUnordered on a Tokio runtime. Both paths support routing the OTP through an authenticated HTTP proxy, non-default validation servers, and TLS backend selection (native-tls or rustls-tls) via Cargo features.

What You Get

  • Config builder - a Config struct with chainable setters for client ID, API key, sync level, timeout, proxy, and custom API hosts.
  • Blocking verifier - verify() fires the OTP validation request across all configured API hosts on a thread pool and returns on the first success.
  • Async verifier - verify_async() (default online-tokio feature) runs the same validation over Tokio and FuturesUnordered.
  • Request signing and response verification - HMAC-SHA1 request signing and response signature/OTP/nonce checks are built in, so callers never handle raw validation-protocol bytes.

Common Use Cases

  • Second-factor login - a Rust web service verifies a YubiKey OTP submitted alongside a password before completing sign-in.
  • CLI authentication gating - an internal command-line tool requires a YubiKey tap before running a privileged command.
  • Self-hosted validation servers - an organization runs its own Yubico-compatible validation servers and points the client at a custom api_hosts list instead of Yubico’s public ones.
  • Async web backends - a Tokio-based API server validates OTPs without blocking its request-handling threads.

Under The Hood

Architecture The crate is small and cleanly separated by responsibility: config.rs holds the Config builder and default API host list, sec.rs isolates the HMAC-SHA1 signing/verification primitives, yubicoerror.rs defines a single YubicoError enum implementing std::error::Error, and lib.rs builds the signed Request/ResponseVerifier pair shared by both sync_verifier and async_verifier. The two verifier modules are thin façades over that shared core — one dispatches across a threadpool::ThreadPool and blocks on an mpsc channel, the other drives the same hosts concurrently through futures::stream::FuturesUnordered — so the protocol logic is written once and only the concurrency model differs; the main soft spot is an unguarded decode(...).unwrap() in the response-signature check in lib.rs, which could panic on a malformed server response instead of surfacing a YubicoError.

Tech Stack The crate targets stable Rust (edition 2018) and depends on reqwest (both the blocking client and the default async client, gated by the online-tokio feature) for HTTP, tokio and futures for async dispatch, threadpool for the blocking path’s fan-out, hmac/sha1 for request signing, base64 for key/signature encoding, rand for nonce generation, and form_urlencoded for query construction. TLS backend is selectable via Cargo features (native-tls default, rustls-tls optional). There is no database or web framework involved — it is a pure outbound HTTP client crate published to crates.io, with a Dockerfile provided only to run the bundled examples reproducibly.

Code Quality No test files or #[test] functions exist anywhere in the repository (confirmed by directory search), so correctness rests entirely on manual verification and the four runnable examples under examples/. Error handling is otherwise explicit and typed — nearly every fallible operation returns Result<T, YubicoError> with a purpose-built error variant (BadOTP, ReplayedOTP, BadSignature, NonceMismatch, etc.) and From conversions from reqwest::Error, io::Error, and base64::DecodeError — with one notable exception being the .unwrap() on signature decoding noted above. Naming is consistent and CI (Travis) runs cargo build --all --all-features and cargo test, but the test step has nothing to execute.

API Design The public surface is deliberately small: a fluent Config::default().set_client_id(..).set_key(..) builder plus a single verify() (or verify_async()) call that returns Ok or a specific YubicoError variant. This keeps integration to a handful of lines, mirrors the shape of Yubico’s own validation protocol closely enough to be easy to reason about for anyone who has read the spec, and offers sync and async variants of the identical call so it drops into either a blocking or Tokio-based codebase without behavioral surprises. Documentation is limited to the README and inline example files rather than doc comments on public items, which raises the bar slightly for a newcomer who wants doc-hosted API reference instead of source reading.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search