Skip to main content

archolith-oauth

Reusable OAuth 2.1 building blocks extracted from Menhir for Archolith services and remote MCP protected resources.

Included

  • RFC 9728 protected-resource and RFC 8414 authorization-server metadata
  • Path-aware .well-known discovery for issuers/resources with URL paths
  • Dynamic client registration for public PKCE clients
  • Exact redirect, scope, and RFC 8707 resource validation
  • Menhir-compatible SQLite client and single-use authorization-code stores
  • Persistent RS256 signing keys with minimal active/previous-key rotation
  • Access-token issuance and JWT/JWKS verification
  • Opt-in offline_access with rotating, replay-detecting refresh tokens and encrypted durable exact-retry receipts for lost responses
  • Prefixed environment settings, redacted diagnostics, and deployment preflight
  • One-call construction with OAuthRuntime.from_settings()
  • Signed consent-state primitives plus durable, atomic single-use consent registration and authorization-code issuance without prescribing a UI
  • Declarative scope policy for routes and MCP tool catalog filtering
  • Optional FastAPI routes and ASGI bearer middleware
  • Node.js resource-server example using jose
  • Dependency-free operator CLI for config, preflight, and key rotation

Install

pip install archolith-oauth

For FastAPI routes and middleware:

pip install 'archolith-oauth[fastapi]'

New-project quickstart

from archolith_oauth import OAuthRuntime, OAuthSettings

settings = OAuthSettings.from_env("MYAPP_OAUTH_")
settings.preflight(require_consent_secret=True).raise_for_errors()
runtime = OAuthRuntime.from_settings(settings)
MYAPP_OAUTH_ISSUER=https://auth.example.com/myapp
MYAPP_OAUTH_RESOURCE=https://api.example.com/mcp
MYAPP_OAUTH_SCOPES=myapp:read myapp:write myapp:admin
MYAPP_OAUTH_DEFAULT_SCOPES=myapp:read myapp:write
MYAPP_OAUTH_DATA_DIR=/var/lib/myapp/oauth
MYAPP_OAUTH_REFRESH_TOKENS_ENABLED=true
MYAPP_OAUTH_CONSENT_SECRET=<at-least-32-random-bytes>

Validate a deployment without printing secrets:

archolith-oauth --prefix MYAPP_OAUTH_ show-config --json
archolith-oauth --prefix MYAPP_OAUTH_ preflight --require-consent-secret

The same CLI is available as python -m archolith_oauth.

FastAPI protocol routes:

from fastapi import FastAPI
from archolith_oauth.fastapi import create_protocol_router

app = FastAPI()
app.include_router(create_protocol_router(runtime))

Your application still owns login and the consent page. After approval, call authorize_and_redirect(...) to validate the exact redirect, scope, PKCE, and resource parameters and issue the authorization code.

Scope policy

from archolith_oauth import ScopePolicy, ScopeRequirement

policy = ScopePolicy({
    "list_sessions": "harness:read",
    "start_session": ("harness:read", "harness:session"),
    "delete_worktree": "harness:admin",
    "operate": ScopeRequirement(
        frozenset({"harness:session", "harness:admin"}),
        "any",
    ),
})

visible_tools = policy.filter_items(
    tools,
    principal.scopes,
    name=lambda tool: tool.name,
)
policy.require("start_session", principal.scopes)

Use the same policy for tools/list filtering and invocation enforcement.

ConsentTokenManager signs the exact authorization parameters shown to the user. ConsentNonceStore atomically consumes each approval transaction so a consent form cannot be replayed. create_session() can remember explicitly approved client IDs without coupling the package to any login or HTML system.

Signing-key rotation

The active private-key file remains a single JWK compatible with Menhir. On rotation, only the retired public key is retained in a sibling file and exposed through JWKS, allowing existing one-hour access tokens to finish naturally. The default keeps one previous key:

archolith-oauth --prefix MYAPP_OAUTH_ rotate-key

Use --retain-previous 0 to retire the old key immediately or a larger value only when the access-token lifetime genuinely requires it. A running OAuthRuntime can call rotate_signing_key() directly.

Node resource servers

examples/node/oauth-middleware.mjs shows issuer, audience, JWKS, expiry, and scope validation for the existing Node harness. The inbound bearer token must never be forwarded into OpenCode, provider configuration, logs, or child-process environments; only the verified principal is trusted.

Protocol notes

The client must send the same canonical resource value in both the authorization request and token request. Access tokens are audience-bound to that resource. offline_access is advertised when refresh tokens are enabled but is granted only when the client requests it.

For an issuer at https://auth.example.com/harness and resource at https://harness.example.com/mcp, discovery URLs are:

  • Authorization server: https://auth.example.com/.well-known/oauth-authorization-server/harness
  • Protected resource: https://harness.example.com/.well-known/oauth-protected-resource/mcp

Menhir adoption

Menhir keeps its existing settings adapter, consent screen, rate limits, singleton wiring, and menhir:* scope-to-tier mapping. The package preserves Menhir's current client database columns, client-store operations, keyword-based authorization-code issuance, and active private-key file shape, so migration can retain existing OAuth state. Refresh tokens remain disabled unless Menhir explicitly enables them.

Menhir's authorize and token routes must begin requiring the same canonical resource value before switching to this package; this is stricter than its current implementation and is required by remote MCP authorization.

Harness adoption

cth.harness remains Node.js. A small Python authorization service uses this package to perform registration, consent, token issuance, and refresh rotation. The Node MCP server validates the resulting JWT against the service's JWKS, issuer, audience, and harness:* scopes.

Recommended scopes:

  • harness:read — inspect sessions, output, status, and diffs
  • harness:session — create and continue isolated sessions
  • harness:admin — destructive session/worktree administration

Menhir and Harness should use separate authorization-server configurations and resource audiences. They may share a host, but should use distinct issuer paths or separate authorization-server deployments.

Download files

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

Source Distribution

archolith_oauth-0.3.1.tar.gz (53.9 kB view details)

Uploaded Source

Built Distribution

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

archolith_oauth-0.3.1-py3-none-any.whl (46.8 kB view details)

Uploaded Python 3

File details

Details for the file archolith_oauth-0.3.1.tar.gz.

File metadata

  • Download URL: archolith_oauth-0.3.1.tar.gz
  • Upload date:
  • Size: 53.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for archolith_oauth-0.3.1.tar.gz
Algorithm Hash digest
SHA256 f8c335fe04e5fcb52eb021d4522a094952ea72db180b46f7c063699afc034ea4
MD5 bdce993d62c3668cf9e2cb972c5733f3
BLAKE2b-256 c180c439b592e821c898c82e2d515f5608a017b5cf2fa137f5137be1d0619c3d

See more details on using hashes here.

Provenance

The following attestation bundles were made for archolith_oauth-0.3.1.tar.gz:

Publisher: publish.yml on Archolith/archolith_oauth

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

File details

Details for the file archolith_oauth-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: archolith_oauth-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 46.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for archolith_oauth-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9d59592c49d7636b3001fad192d7c3e586fb31e11d334f8100616fcf4ecce4e5
MD5 58a136260d6254d43ee462d20f78f890
BLAKE2b-256 e5dc12a3c34baf14381888258dd45b48afe758d3135132aced20cbc34201b441

See more details on using hashes here.

Provenance

The following attestation bundles were made for archolith_oauth-0.3.1-py3-none-any.whl:

Publisher: publish.yml on Archolith/archolith_oauth

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

Release history Release notifications | RSS feed

This release

0.3.1 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page