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-knowndiscovery 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_accesswith 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.
Consent state
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 diffsharness:session— create and continue isolated sessionsharness: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.
Metadata
Release files for archolith-oauth 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| archolith_oauth-0.3.1.tar.gz | 53.9 kB | With provenance | Details for archolith_oauth-0.3.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| archolith_oauth-0.3.1-py3-none-any.whl | Python 3 | none | any | Details for archolith_oauth-0.3.1-py3-none-any.whl |
Total release size: 100.7 kB
Release files / archolith_oauth-0.3.1.tar.gz
| Download URL | archolith_oauth-0.3.1.tar.gz |
|---|---|
| Size | 53.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f8c335fe04e5fcb52eb021d4522a094952ea72db180b46f7c063699afc034ea4
|
|
BLAKE2b-256 checksum How to use checksums |
c180c439b592e821c898c82e2d515f5608a017b5cf2fa137f5137be1d0619c3d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Sep 15, 2026.
Transparency logRelease files / archolith_oauth-0.3.1-py3-none-any.whl
| Download URL | archolith_oauth-0.3.1-py3-none-any.whl |
|---|---|
| Size | 46.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9d59592c49d7636b3001fad192d7c3e586fb31e11d334f8100616fcf4ecce4e5
|
|
BLAKE2b-256 checksum How to use checksums |
e5dc12a3c34baf14381888258dd45b48afe758d3135132aced20cbc34201b441
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Sep 15, 2026.
Transparency logSecurity for archolith-oauth 0.3.1
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for archolith_oauth-0.3.1.tar.gz
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 Sep 15, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.