Skip to main content

QuantumAuth - Next-generation authentication and authorization protocol with post-quantum security

Project description

QAuth - Post-Quantum Authentication for Python

PyPI version Python 3.9+ License: MIT

Next-generation authentication protocol designed to replace OAuth 2.0 and JWT with post-quantum security.

Installation

# Using pip
pip install qauth

# Using pip3
pip3 install qauth

# Using pipx (for CLI tools)
pipx install qauth

# Using poetry
poetry add qauth

# Using pdm
pdm add qauth

# Using uv
uv pip install qauth

Quick Start

from qauth import QAuthServer, QAuthClient, PolicyEngine

# Create a server instance
server = QAuthServer(
    issuer="https://auth.example.com",
    audience="https://api.example.com"
)

# Create an access token
token = server.create_token(
    subject="user-123",
    policy_ref="urn:qauth:policy:default",
    validity_seconds=3600,
    claims={
        "email": "user@example.com",
        "roles": ["user", "premium"],
    },
)

# Validate a token
payload = server.validate_token(token)
print(f"Subject: {payload.sub.decode()}")
print(f"Expires: {payload.exp}")

Client-Side Usage

from qauth import QAuthClient

# Create a client instance (generates a new keypair)
client = QAuthClient()

# Get the client's public key (send to server during auth)
public_key = client.public_key

# Create proof of possession for API requests
proof = client.create_proof("GET", "/api/resource", token)

# Make API request with token and proof
import requests
response = requests.get(
    "https://api.example.com/resource",
    headers={
        "Authorization": f"QAuth {token}",
        "X-QAuth-Proof": proof,
    },
)

Server-Side Validation

from qauth import QAuthValidator, ProofValidator

# Create a validator with pre-shared issuer keys
validator = QAuthValidator(
    keys=issuer_keys,
    issuer="https://auth.example.com",
    audience="https://api.example.com",
)

# Validate token
try:
    payload = validator.validate(token)
    print(f"Token valid for user: {payload.sub}")
except Exception as e:
    print(f"Token validation failed: {e}")

# Validate proof of possession
proof_validator = ProofValidator(client_public_key)
try:
    proof_validator.validate(proof, "GET", "/api/resource", token)
    print("Proof valid")
except Exception as e:
    print(f"Proof validation failed: {e}")

Policy-Based Authorization

from qauth import PolicyEngine, Effect

engine = PolicyEngine()

# Load a policy
engine.load_policy({
    "id": "urn:qauth:policy:api-access",
    "version": "2026-01-30",
    "issuer": "https://auth.example.com",
    "rules": [
        {
            "id": "read-projects",
            "effect": "allow",
            "resources": ["projects/*"],
            "actions": ["read", "list"],
        },
        {
            "id": "admin-only",
            "effect": "allow",
            "resources": ["admin/**"],
            "actions": ["*"],
            "conditions": {
                "custom": {
                    "role": {"in": ["admin"]},
                },
            },
        },
    ],
})

# Evaluate authorization
result = engine.evaluate(
    "urn:qauth:policy:api-access",
    {
        "subject": {
            "id": "user-123",
            "attributes": {"role": "user"},
        },
        "resource": {
            "path": "projects/456",
        },
        "request": {
            "action": "read",
        },
    },
)

if result.effect == Effect.ALLOW:
    print("Access granted")
else:
    print(f"Access denied: {result.reason}")

FastAPI Integration

from fastapi import FastAPI, Depends, HTTPException, Header
from qauth import QAuthValidator, ProofValidator, IssuerKeys

app = FastAPI()

# Configure validator
validator = QAuthValidator(
    keys=issuer_keys,
    issuer="https://auth.example.com",
    audience="https://api.example.com",
)

async def verify_qauth(
    authorization: str = Header(...),
    x_qauth_proof: str = Header(...),
):
    if not authorization.startswith("QAuth "):
        raise HTTPException(401, "Invalid authorization header")

    token = authorization[6:]

    try:
        payload = validator.validate(token)
    except Exception as e:
        raise HTTPException(401, f"Invalid token: {e}")

    # In production, also verify the proof

    return payload

@app.get("/api/resource")
async def get_resource(payload = Depends(verify_qauth)):
    return {"user": payload.sub.decode()}

API Reference

QAuthServer

Server-side class for token creation and validation.

server = QAuthServer(issuer: str, audience: str)

# Get public keys for sharing with validators
keys = server.get_public_keys() -> IssuerKeys

# Create a token
token = server.create_token(
    subject: str | bytes,
    policy_ref: str,
    audience: str | list[str] | None = None,
    validity_seconds: int = 3600,
    client_key: bytes | None = None,
    device_key: bytes | None = None,
    claims: dict[str, Any] | None = None,
) -> str

# Validate a token
payload = server.validate_token(token: str) -> TokenPayload

QAuthClient

Client-side class for proof of possession.

client = QAuthClient()

# Get client's public key
public_key = client.public_key -> bytes

# Create proof for API request
proof = client.create_proof(
    method: str,
    uri: str,
    token: str,
    body: bytes | None = None,
) -> str

PolicyEngine

Evaluate authorization policies.

engine = PolicyEngine()

# Load a policy
engine.load_policy(policy: dict[str, Any]) -> None

# Evaluate authorization
result = engine.evaluate(
    policy_id: str,
    context: dict[str, Any],
) -> EvaluationResult

Data Classes

@dataclass
class TokenPayload:
    sub: bytes        # Subject identifier
    iss: str          # Issuer
    aud: list[str]    # Audiences
    exp: int          # Expiration time
    iat: int          # Issued at
    nbf: int          # Not before
    jti: bytes        # Token ID
    rid: bytes        # Revocation ID
    pol: str          # Policy reference
    ctx: bytes        # Context hash
    cst: dict         # Custom claims

@dataclass
class EvaluationResult:
    effect: Effect
    matched_rule: str | None
    reason: str

class Effect(Enum):
    ALLOW = "allow"
    DENY = "deny"

Requirements

  • Python 3.9+
  • cryptography >= 41.0.0
  • pynacl >= 1.5.0

Why QAuth over JWT?

JWT/OAuth Problem QAuth Solution
Algorithm confusion attacks Server-enforced, no client selection
Bearer tokens can be stolen Proof-of-possession mandatory
No built-in revocation Instant revocation system
Payload visible (base64) Encrypted with XChaCha20-Poly1305
Single signature Dual: Ed25519 + ML-DSA-65
No post-quantum security ML-DSA-65 (NIST FIPS 204)

Related Packages

  • Rust: cargo add qauth
  • TypeScript/Node.js: npm install @qauth/sdk
  • Go: go get github.com/tushar-agrawal/qauth

License

MIT License - LICENSE

Author

Tushar Agrawal - tusharagrawal.in

Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qauth-0.1.0.tar.gz (10.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qauth-0.1.0-py3-none-any.whl (7.1 kB view details)

Uploaded Python 3

File details

Details for the file qauth-0.1.0.tar.gz.

File metadata

  • Download URL: qauth-0.1.0.tar.gz
  • Upload date:
  • Size: 10.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for qauth-0.1.0.tar.gz
Algorithm Hash digest
SHA256 073dcb9b68754d80fefb31def85a39f40dc4bfb23056e30bcf50080d15495138
MD5 678fdb588cac350922a9571371d1364a
BLAKE2b-256 972d31399e679df8b0836b924e03ac54d8bc145554662f74cd36df7ebd216307

See more details on using hashes here.

Provenance

The following attestation bundles were made for qauth-0.1.0.tar.gz:

Publisher: qauth-ci.yml on Tushar010402/Tushar-Agrawal-Website

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qauth-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: qauth-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 7.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for qauth-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b370c4c30d2ee1c0b0c0bc7b6ce29b9c219ec56dc494b472cf0a8f7053e7e6f3
MD5 78a21af0e0243b8e901c7e35f576e058
BLAKE2b-256 e9bc694a90f1fd736a5b878f8441a706fe0b2c57dbc3f3e8a7d4586573323df1

See more details on using hashes here.

Provenance

The following attestation bundles were made for qauth-0.1.0-py3-none-any.whl:

Publisher: qauth-ci.yml on Tushar010402/Tushar-Agrawal-Website

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page