OAuth 2.1 server implementation in Rust
|
|
vor 4 Tagen | |
|---|---|---|
| .cargo | vor 1 Woche | |
| .clusterfuzzlite | vor 2 Wochen | |
| .config | vor 3 Wochen | |
| .github | vor 4 Tagen | |
| .sqlx | vor 2 Wochen | |
| .well-known | vor 3 Wochen | |
| deploy | vor 3 Wochen | |
| docs | vor 4 Tagen | |
| fuzz | vor 1 Woche | |
| migrations | vor 2 Wochen | |
| scripts | vor 4 Tagen | |
| src | vor 4 Tagen | |
| templates | vor 1 Monat | |
| tests | vor 1 Woche | |
| .dockerignore | vor 3 Wochen | |
| .gitignore | vor 2 Wochen | |
| AGENTS.md | vor 4 Tagen | |
| Cargo.lock | vor 1 Woche | |
| Cargo.toml | vor 2 Wochen | |
| Dockerfile | vor 3 Wochen | |
| LICENSE.md | vor 1 Monat | |
| README.md | vor 4 Tagen | |
| SECURITY.md | vor 3 Wochen | |
| compose.ghcr.yaml | vor 4 Tagen | |
| compose.test.yaml | vor 3 Wochen | |
| compose.yaml | vor 4 Tagen | |
| deny.toml | vor 1 Woche | |
| docker-bake.hcl | vor 3 Wochen | |
| rust-toolchain.toml | vor 1 Monat | |
| rustfmt.toml | vor 1 Monat |
An OAuth 2.1-only authorization server written in Rust. It is deliberately not
backwards compatible with OAuth 2.0: PKCE is mandatory, the implicit and
resource-owner-password grants do not exist, bearer-in-query is rejected, and
redirect URIs match exactly (with the RFC 8252 §7.3 loopback exception, which
includes the host localhost only when allow_localhost_redirect is set).
It implements the authorization-code, refresh-token, client-credentials,
device-code, and token-exchange grants, with DPoP, mTLS, PAR, JAR, dynamic
client registration, client ID metadata documents, introspection, and
revocation. Tokens are opaque and hashed at rest by default; a global enable
switch plus a per-client jwt access-token format mints RFC 9068 at+jwt
tokens verified against the AS JWKS. All durable domain state is in PostgreSQL
and all ephemeral state is in Redis, so replicas are interchangeable.
Administration is CLI plus declarative configuration: there is no HTTP admin
API and no default credential.
compose.ghcr.yaml pulls the latest signed image,
ghcr.io/syavne/d-oauth-server:latest, and starts it against its own Postgres
and Redis. A one-shot service applies the schema, and throwaway TLS and key
files are generated on first start, so there is nothing to build or export:
docker compose -f compose.ghcr.yaml up -d --pull always
The TLS listener is on https://localhost:8080 with a self-signed certificate,
so probe it with -k. Health stays on the plaintext listener at 8081:
curl -s http://127.0.0.1:8081/health/ready
curl -sk https://localhost:8080/.well-known/oauth-authorization-server | jq .
Bootstrap the first administrator inside the running container. The password is read from stdin (nothing prompts), and the account must change it and enroll TOTP on first login:
ADMIN_PASSWORD=$(openssl rand -base64 24)
printf '%s' "$ADMIN_PASSWORD" | docker compose -f compose.ghcr.yaml exec -T app \
/d-oauth-server admin bootstrap --email admin@example.com
Stop the stack and delete its data when you are done:
docker compose -f compose.ghcr.yaml down -v
compose.yaml builds the image from the working tree and runs one replica
against its own Postgres and Redis, with a generated TLS certificate and
dev-only key files:
docker compose -f compose.yaml up --build
docker compose -f compose.yaml port app 8080 # host port of the TLS listener
The listener uses that self-signed certificate, so probe it with -k. The
discovery document advertises the configured issuer (https://localhost:8080),
independent of the random host port:
curl -sk https://127.0.0.1:<host-port>/.well-known/oauth-authorization-server | jq .
Or run the binary directly, against a Postgres and a Redis of your own, with the development environment exported as in QUICKSTART.md:
cargo build --locked
cargo run --bin d-oauth-server -- config check
cargo run --bin d-oauth-server -- migrate
cargo run --bin d-oauth-server -- admin bootstrap --email admin@example.com
cargo run --bin d-oauth-server -- serve
curl -s http://127.0.0.1:8081/health/live
curl -s http://127.0.0.1:8081/health/ready
That is the short version. The full first-run path, including creating a client and minting an access token with PKCE, is QUICKSTART.md.
Comprehensive guides live in docs/, whose index maps every
guide by audience and reading order.
| I want to... | Start with |
|---|---|
| Run it, end to end, with a token | QUICKSTART.md |
| Look up a configuration key | CONFIGURATION.md |
| Administer a live install | OPERATIONS.md, RUNBOOK.md |
| Deploy it to a cluster | DEPLOYMENT.md |
| Integrate an OAuth client | OAUTH.md |
| Protect an API (resource server) | RS-CONTRACT.md |
| Understand or change the code | ARCHITECTURE.md, CONTRIBUTING.md |
| Review the security properties | SECURITY.md |
| Inspect the schema or the Redis keys | DATABASE.md, REDIS.md |
| See what changed in a release | CHANGELOG.md |
To report a vulnerability, use the reporting policy, not a guide.
WeakKeyMaterial otherwise), and
are live secrets: losing the pepper invalidates every opaque token, and losing
the PII key makes the sealed directory unreadable. Mount them as files, not
environment values (DEPLOYMENT.md).https issuer and, unless
database.require_verify_full is set false, a database URL with
sslmode=verify-full.noeviction and persistence; the server asserts both at
startup and refuses to start otherwise
(REDIS.md).503 rather than failing open. CORS is empty by default, so browser
clients need an explicit origin allowlist.The threat model and the full invariant list are in SECURITY.md.
cargo nextest run --all-targets is the authoritative suite, backed by the
shared Postgres and Redis from compose.test.yaml; cargo test --doc covers
the doctests. The quality gates, the coverage floors, the fuzzing targets, and
the exact commands are in CONTRIBUTING.md,
with the measured baseline in
TEST-COVERAGE.md.
ISC. See LICENSE.md.