Basic OAuth Authorization Server
A lightweight Python package that implements an OAuth 2.0 client_credentials authorization server. It issues JWT tokens that can be used to authenticate requests to your protected resources.
Why?
Sometimes you just need a quick and easy way to spin up an OAuth client credentials server for testing, development, or prototyping. I built this to get more familiar with the OAuth 2.0 spec and to have a simple tool for local development that I understand, because I wrote it.
Features
- OAuth 2.0 Client Credentials Flow - Standard-compliant token endpoint
- JWT Access Tokens - Tokens are signed JWTs with configurable algorithms
- Multiple Signing Algorithms - Support for HMAC-SHA (HS256, HS384, HS512), RSA (RS256, RS384, RS512), RSA-PSS (PS256, PS384, PS512), ECDSA (ES256, ES384, ES512), and EdDSA (Ed25519)
- SQLite Persistence - Simple file-based database, no external DB required
- CLI Client Management - Create and manage clients from the command line
- Optional Admin Dashboard - Web UI for client management (localhost-only by default)
- Single Unified CLI - One command (
basic-oauth2-server) for all operations
Installation
pip install basic-oauth2-server
With admin dashboard:
pip install basic-oauth2-server[admin]
Quick Start
1. Create a client
# For HMAC algorithms (HS256/384/512), you need a signing secret
basic-oauth2-server clients create \
--client-id my-app \
--client-secret my-secret \
--signing-secret my-signing-key \
--algorithm HS256
2. Start the server
basic-oauth2-server serve --port 8080 --host localhost
3. Request a token
Using urlencoded form data:
curl -X POST http://localhost:8080/oauth/token \
-d "grant_type=client_credentials" \
-d "client_id=my-app" \
-d "client_secret=$(echo -n 'my-secret' | base64)"
Using Basic Auth header:
curl http://localhost:8080/oauth/token \
-u "my-app:$(echo -n 'my-secret' | base64)" \
-d "grant_type=client_credentials"
CLI Commands
All functionality is accessed through the basic-oauth2-server command:
basic-oauth2-server <command> [options]
Commands:
serve Start the OAuth authorization server
clients Manage OAuth clients (create, list, delete)
admin Start the admin dashboard server
Shared configuration options
The app shares these configuration options across all commands, which can be set via CLI arguments or environment variables:
| Option | Environment Variable | Default | Description |
|---|---|---|---|
--db |
OAUTH_DB_PATH |
./oauth.db |
Path to SQLite database file |
--app-url |
APP_URL |
http://localhost:8080 |
Issuer URL for JWT iss claim (should match your server URL) |
serve
Start the main OAuth authorization server.
basic-oauth2-server serve [options]
| Option | Environment Variable | Default | Description |
|---|---|---|---|
--port |
OAUTH_PORT |
8080 |
Port for the server |
--host |
OAUTH_HOST |
localhost |
Host address to bind |
--rsa-private-key |
OAUTH_RSA_PRIVATE_KEY |
- | RSA private key for RS*/PS* |
--ec-p256-private-key |
OAUTH_EC_P256_PRIVATE_KEY |
- | ECDSA P-256 private key for ES256 |
--ec-p384-private-key |
OAUTH_EC_P384_PRIVATE_KEY |
- | ECDSA P-384 private key for ES384 |
--ec-p521-private-key |
OAUTH_EC_P521_PRIVATE_KEY |
- | ECDSA P-521 private key for ES512 |
--eddsa-private-key |
OAUTH_EDDSA_PRIVATE_KEY |
- | Ed25519 private key for EdDSA |
--rsa-key-id |
OAUTH_RSA_KEY_ID |
- | Key ID for RSA (JWT kid header) |
--ec-p256-key-id |
OAUTH_EC_P256_KEY_ID |
- | Key ID for EC P-256 (kid) |
--ec-p384-key-id |
OAUTH_EC_P384_KEY_ID |
- | Key ID for EC P-384 (kid) |
--ec-p521-key-id |
OAUTH_EC_P521_KEY_ID |
- | Key ID for EC P-521 (kid) |
--eddsa-key-id |
OAUTH_EDDSA_KEY_ID |
- | Key ID for EdDSA (kid) |
Note: Private keys are only needed if you have clients using that algorithm. Key IDs are optional and will be included in the JWT header as kid when specified. Keys can be provided as file paths with @ prefix (e.g., @/path/to/key.pem) or as PEM-encoded strings.
clients
Manage OAuth clients via the CLI.
# Create a new client with HMAC-SHA256 (signing secret auto-generated)
basic-oauth2-server clients create \
--client-id my-service \
--client-secret supersecret \
--algorithm HS256
# Output:
# JWT_ALGORITHM=HS256
# JWT_SECRET=xxxx
# Or provide your own signing secret
basic-oauth2-server clients create \
--client-id my-service \
--client-secret supersecret \
--signing-secret my-hmac-key \
--algorithm HS256
# Create a client with RSA signing (server must have --rsa-private-key)
basic-oauth2-server clients create \
--client-id my-service \
--client-secret supersecret \
--algorithm RS256
# Create a client with ECDSA P-256 (server must have --ec-p256-private-key)
basic-oauth2-server clients create \
--client-id my-service \
--client-secret supersecret \
--algorithm ES256
# Create a client with EdDSA (server must have --eddsa-private-key)
basic-oauth2-server clients create \
--client-id my-service \
--client-secret supersecret \
--algorithm EdDSA
# List all clients
basic-oauth2-server clients list
# Delete a client
basic-oauth2-server clients delete --client-id my-service
| Option | Description |
|---|---|
--client-id |
Client identifier |
--client-secret |
Client secret (password for obtaining tokens). Stored as SHA256 hash |
--algorithm |
Signing algorithm: HS*, RS*, PS*, ES*, or EdDSA |
--signing-secret |
Signing secret for HMAC algorithms (auto-generated if not provided for HS256/384/512) |
--scope |
Add allowed scope. Can be used multiple times. |
--audience |
Add allowed audience. Can be used multiple times. |
Secret Formats
The --client-secret, --signing-secret, and private key options all accept values in these formats:
| Prefix | Format | Example |
|---|---|---|
@ |
File path | @/path/to/secret.txt |
base64: |
Base64 | base64:c2VjcmV0... |
hex:, 0x |
Hexadecimal | 0xdeadbeef1234... |
| (none) | Plain text | my-secret |
Examples:
# Read secrets from files
basic-oauth2-server clients create \
--client-id app \
--client-secret @./client-secret.txt \
--signing-secret @./signing-key.txt \
--algorithm HS256
# Base64-encoded secrets
basic-oauth2-server clients create \
--client-id app \
--client-secret base64:Y2xpZW50LXNlY3JldA== \
--signing-secret base64:c2lnbmluZy1rZXk= \
--algorithm HS256
# Use asymmetric algorithm with private key from file
basic-oauth2-server clients create \
--client-id app \
--client-secret mysecret \
--algorithm RS256
# Then start server with the key
basic-oauth2-server serve --rsa-private-key @./private.pem
admin
Start the optional admin dashboard for managing clients via a web UI.
Note: Requires the admin extra: pip install basic-oauth2-server[admin]
basic-oauth2-server admin [options]
| Option | Environment Variable | Default | Description |
|---|---|---|---|
--port |
OAUTH_ADMIN_PORT |
8081 |
Port for admin dashboard |
--host |
OAUTH_ADMIN_HOST |
localhost |
Host address (localhost only by default for security) |
Token Endpoint
POST /oauth/token
Request a new access token.
Request Parameters:
| Parameter | Required | Description |
|---|---|---|
grant_type |
Yes | Must be client_credentials |
client_id |
Yes | The client identifier |
client_secret |
Yes | The client secret in base64 |
scope |
No | Space-separated list of requested scopes |
audience |
No | Intended audience for the token |
The parameters client_id and client_secret can also be provided via HTTP Basic Authentication header instead of the request body.
Success Response (200 OK):
{
"access_token": "ey...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read write"
}
Error Response (400/401):
{
"error": "invalid_client",
"error_description": "Client authentication failed"
}
Configuration
Configuration can be provided via CLI arguments or environment variables. CLI arguments take precedence.
| Environment Variable | Description |
|---|---|
APP_KEY |
Encryption key for sensitive data stored in database |
APP_URL |
Issuer URL for JWT iss claim |
OAUTH_PORT |
Main server port |
OAUTH_HOST |
Main server host |
OAUTH_DB_PATH |
SQLite database path |
OAUTH_ADMIN_PORT |
Admin dashboard port |
OAUTH_ADMIN_HOST |
Admin dashboard host |
OAUTH_RSA_PRIVATE_KEY |
RSA private key for RS*/PS* algorithms |
OAUTH_EC_P256_PRIVATE_KEY |
ECDSA P-256 private key for ES256 |
OAUTH_EC_P384_PRIVATE_KEY |
ECDSA P-384 private key for ES384 |
OAUTH_EC_P521_PRIVATE_KEY |
ECDSA P-521 private key for ES512 |
OAUTH_EDDSA_PRIVATE_KEY |
Ed25519 private key for EdDSA |
OAUTH_RSA_KEY_ID |
Key ID for RSA keys (included in JWT kid header) |
OAUTH_EC_P256_KEY_ID |
Key ID for EC P-256 key (JWT kid header) |
OAUTH_EC_P384_KEY_ID |
Key ID for EC P-384 key (JWT kid header) |
OAUTH_EC_P521_KEY_ID |
Key ID for EC P-521 key (JWT kid header) |
OAUTH_EDDSA_KEY_ID |
Key ID for EdDSA key (JWT kid header) |
APP_KEY
The APP_KEY environment variable is required for encrypting sensitive data (such as HMAC signing secrets) before storing them in the SQLite database. Client secrets are stored as SHA256 hashes. This key should be a secure, random string.
Generate a key using openssl:
openssl rand -base64 32
Set it before running any command:
export APP_KEY="your-generated-key-here"
basic-oauth2-server serve
Important: Keep this key safe. If you lose it, you will not be able to decrypt existing client secrets in the database.
Examples
Setup with HMAC
# Create a client with HMAC signing
basic-oauth2-server clients create \
--client-id dev-client \
--client-secret dev-secret \
--signing-secret dev-signing-key
# Start the server
basic-oauth2-server serve
# Get a token
curl http://localhost:8080/oauth/token \
-d "grant_type=client_credentials" \
-d "client_id=dev-client" \
-d "client_secret=$(echo -n 'dev-secret' | base64)"
Setup with RSA
# Generate an RSA private key
openssl genrsa -out private.pem 2048
# Create client with RSA signing
basic-oauth2-server clients create \
--client-id prod-service \
--client-secret prod-secret \
--algorithm RS256 \
--scope "read,write,admin" \
--audience "https://api.example.com"
# Start the server with the RSA private key
basic-oauth2-server serve --rsa-private-key @private.pem
Setup with Multiple Algorithm Support
# Generate keys for different algorithms
openssl genrsa -out rsa-private.pem 4096
openssl ecparam -name prime256v1 -genkey -noout -out es256-private.pem
openssl genpkey -algorithm Ed25519 -out ed25519-private.pem
# Create clients with different algorithms
basic-oauth2-server clients create --client-id client-rsa --client-secret secret1 --algorithm RS256
basic-oauth2-server clients create --client-id client-ecdsa --client-secret secret2 --algorithm ES256
basic-oauth2-server clients create --client-id client-eddsa --client-secret secret3 --algorithm EdDSA
basic-oauth2-server clients create --client-id client-hmac --client-secret secret4 --algorithm HS256 --signing-secret hmac-key
# Start server with all private keys (optionally with key IDs)
basic-oauth2-server serve \
--rsa-private-key @rsa-private.pem \
--rsa-key-id my-rsa-key-1 \
--ec-p256-private-key @es256-private.pem \
--ec-p256-key-id my-es256-key-1 \
--eddsa-private-key @ed25519-private.pem \
--eddsa-key-id my-eddsa-key-1
Running with Admin Dashboard
# Terminal 1: Start the main server
basic-oauth2-server serve --port 8080
# Terminal 2: Start the admin dashboard
basic-oauth2-server admin --port 8081
# Access the dashboard at http://localhost:8081
Using Environment Variables
export APP_KEY="$(openssl rand -base64 32)"
export APP_URL="https://auth.example.com"
export OAUTH_DB_PATH=/var/lib/oauth/oauth.db
export OAUTH_PORT=9000
# Set private keys for each algorithm family you want to support
export OAUTH_RSA_PRIVATE_KEY="@/etc/oauth/rsa-private.pem"
export OAUTH_EC_P256_PRIVATE_KEY="@/etc/oauth/es256-private.pem"
export OAUTH_EC_P384_PRIVATE_KEY="@/etc/oauth/es384-private.pem"
export OAUTH_EC_P521_PRIVATE_KEY="@/etc/oauth/es512-private.pem"
export OAUTH_EDDSA_PRIVATE_KEY="@/etc/oauth/ed25519-private.pem"
# Optionally set key IDs for JWT kid header
export OAUTH_RSA_KEY_ID="rsa-prod-2026"
export OAUTH_EC_P256_KEY_ID="es256-prod-2026"
basic-oauth2-server serve
Future Work
- Support for additional grant types (authorization code, refresh token, etc.)
- Token revocation endpoint (though that only makes sense when using opaque tokens instead of JWTs)
License
MIT
Metadata
Release files for basic-oauth2-server 0.1.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| basic_oauth2_server-0.1.6.tar.gz | 26.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| basic_oauth2_server-0.1.6-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 48.8 kB
Release files / basic_oauth2_server-0.1.6.tar.gz
| Download URL | basic_oauth2_server-0.1.6.tar.gz |
|---|---|
| Size | 26.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f86278f8b291c09c4db9319faa8c9af60edab12bed02502fc01c054e04db3609
|
|
BLAKE2b-256 checksum How to use checksums |
bebbd69e23e52bb5d00cff08fa98d80aa237dbdf7b793ca8bbc33128dcd79d7a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 15, 2026.
Transparency logRelease files / basic_oauth2_server-0.1.6-py3-none-any.whl
| Download URL | basic_oauth2_server-0.1.6-py3-none-any.whl |
|---|---|
| Size | 22.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7d569b6e78ad7e5168c55f87af63081f60c4e5b125e5ce0cbb304c03b6eb534b
|
|
BLAKE2b-256 checksum How to use checksums |
17c95181095999c1d1644f850f8b6d41f2e5fed1ccb6633b708bbf817435c73b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 15, 2026.
Transparency log