A simple OAuth 2.0 client credentials server for testing and development
Project description
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file basic_oauth2_server-0.1.6.tar.gz.
File metadata
- Download URL: basic_oauth2_server-0.1.6.tar.gz
- Upload date:
- Size: 26.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f86278f8b291c09c4db9319faa8c9af60edab12bed02502fc01c054e04db3609
|
|
| MD5 |
f73725048101cb4cf5092a48d4502b7c
|
|
| BLAKE2b-256 |
bebbd69e23e52bb5d00cff08fa98d80aa237dbdf7b793ca8bbc33128dcd79d7a
|
Provenance
The following attestation bundles were made for basic_oauth2_server-0.1.6.tar.gz:
Publisher:
publish.yml on Mari6814/basic-oauth2-server
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
basic_oauth2_server-0.1.6.tar.gz -
Subject digest:
f86278f8b291c09c4db9319faa8c9af60edab12bed02502fc01c054e04db3609 - Sigstore transparency entry: 953610712
- Sigstore integration time:
-
Permalink:
Mari6814/basic-oauth2-server@9466cf29a25454831635edc92e2bac0a290c77fe -
Branch / Tag:
refs/tags/v0.1.6 - Owner: https://github.com/Mari6814
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9466cf29a25454831635edc92e2bac0a290c77fe -
Trigger Event:
push
-
Statement type:
File details
Details for the file basic_oauth2_server-0.1.6-py3-none-any.whl.
File metadata
- Download URL: basic_oauth2_server-0.1.6-py3-none-any.whl
- Upload date:
- Size: 22.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7d569b6e78ad7e5168c55f87af63081f60c4e5b125e5ce0cbb304c03b6eb534b
|
|
| MD5 |
690905677d14256a87e5af3378d8124f
|
|
| BLAKE2b-256 |
17c95181095999c1d1644f850f8b6d41f2e5fed1ccb6633b708bbf817435c73b
|
Provenance
The following attestation bundles were made for basic_oauth2_server-0.1.6-py3-none-any.whl:
Publisher:
publish.yml on Mari6814/basic-oauth2-server
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
basic_oauth2_server-0.1.6-py3-none-any.whl -
Subject digest:
7d569b6e78ad7e5168c55f87af63081f60c4e5b125e5ce0cbb304c03b6eb534b - Sigstore transparency entry: 953610714
- Sigstore integration time:
-
Permalink:
Mari6814/basic-oauth2-server@9466cf29a25454831635edc92e2bac0a290c77fe -
Branch / Tag:
refs/tags/v0.1.6 - Owner: https://github.com/Mari6814
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9466cf29a25454831635edc92e2bac0a290c77fe -
Trigger Event:
push
-
Statement type: