Skip to main content

obelisk-auth (Python)

A drop-in OIDC client for the Obelisk Gate identity platform (https://obeliskgate.com). It is the Python counterpart of the JavaScript @obeliskgate/obelisk-auth package and speaks the exact same contract: login + tokens + refresh + userinfo + local ES256 token verification.

Obelisk is a conformant OpenID Connect provider:

  • GET /.well-known/openid-configuration — discovery (RFC 8414)
  • GET /.well-known/jwks.json — JWKS (ES256 / P-256 public keys)
  • GET /auth/authorize — authorization-code flow, PKCE S256 only
  • POST /auth/token — exchange code+code_verifier → id_token / access_token / refresh_token
  • GET /auth/userinfo — Bearer-authenticated claims

Tokens are ES256 JWTs, so they can be verified locally against the cached JWKS with no network round-trip per request — the property that makes Obelisk useful as an agent-era auth plane.

The agent-era angle

Every token is locally verifiable, and the id_token carries an obelisk claim block that proves how the principal authenticated:

"obelisk": {
  "v": "obelisk-claims-v1",
  "assurance": "passkey",
  "anchor": "0a1b2c3d...",
  "rating": { "...": "live issuer security posture" }
}

assurance distinguishes operator-proof factors (passkey) from weaker ones, and anchor ties the issuance into Obelisk's tamper-evident receipt chain. Pull it out of verified claims with obelisk_block(claims).

Install

pip install obelisk-auth

Dependencies are stdlib urllib for HTTP plus PyJWT and cryptography for ES256 verification (no hand-rolled crypto).

Login (copy-paste)

from obelisk_auth import ObeliskAuth, obelisk_block

auth = ObeliskAuth(
    issuer="https://obeliskgate.com",
    client_id="rp-statvault",
    redirect_uri="https://statvault.org/auth/callback",
)

# 1) Start the login. Persist code_verifier + state + nonce in the user session,
#    then redirect the browser to the authorization URL.
login = auth.begin_login(scope="openid profile offline_access")
session["pkce"] = {
    "code_verifier": login.code_verifier,
    "state": login.state,
    "nonce": login.nonce,
}
redirect(login.authorization_url)

# 2) On the callback (e.g. /auth/callback?code=...&state=...):
#    First confirm the returned `state` matches what you stashed (CSRF defense).
assert request.args["state"] == session["pkce"]["state"]

result = auth.complete_login(
    code=request.args["code"],
    code_verifier=session["pkce"]["code_verifier"],
    nonce=session["pkce"]["nonce"],
)

print(result.claims["sub"])                 # the user's stable subject id
print(obelisk_block(result.claims))         # assurance + anchor + rating

access_token = result.tokens["access_token"]
refresh_token = result.tokens.get("refresh_token")  # present with offline_access

Verify a token locally (no network per request)

from obelisk_auth import ObeliskAuth, obelisk_block

auth = ObeliskAuth(
    issuer="https://obeliskgate.com",
    client_id="rp-statvault",
    redirect_uri="https://statvault.org/auth/callback",
)

# On a protected route, given a Bearer access token:
v = auth.verify_access_token(bearer_token)
if not v.ok:
    raise Unauthorized(v.reason)            # e.g. "expired", "issuer-mismatch"

print(v.claims["sub"])

# Gate capability on how the user proved themselves:
block = obelisk_block(v.claims)
if block and block.get("assurance") != "passkey":
    raise Forbidden("this action requires a passkey-proven session")

The first verify_* call fetches the JWKS once and caches it by kid; later calls verify in-process. If a key rotates (unknown kid), the SDK refetches the JWKS exactly once and retries.

Refresh + userinfo

rotated = auth.refresh(refresh_token)       # rotating refresh tokens
new_access = rotated["access_token"]

info = auth.get_userinfo(new_access)        # Bearer-authenticated userinfo
print(info["sub"])

Reusing a superseded refresh token revokes the whole token family on the server (OAuth 2.0 BCP reuse detection); be prepared to force re-authentication.

API

Method Description
discover() Fetch + cache the discovery document.
get_jwks(force=False) Fetch + cache the JWKS.
begin_login(scope=...) → LoginStart PKCE S256 authorize URL + code_verifier/state/nonce.
complete_login(code, code_verifier, nonce=...) → LoginResult Exchange + verify id_token.
verify_id_token(id_token, nonce=...) Local ES256 verify (raises on failure).
verify_access_token(token) → VerifyResult Local ES256 verify (never raises).
refresh(refresh_token) Rotate a refresh token.
get_userinfo(access_token) Bearer userinfo.
obelisk_block(claims) Extract the verified obelisk assurance block.

Testing

pip install -e ".[test]"
pytest

The bundled tests are pure unit tests (no live server): PKCE-S256 challenge correctness, authorization-URL construction, full login flow against a mocked token endpoint, and round-trip ES256 sign/verify with a generated P-256 key.

Metadata

Release files for obelisk-auth 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for obelisk-auth 1.0.0
File Size Uploaded
obelisk_auth-1.0.0.tar.gz 15.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for obelisk-auth 1.0.0
File Interpreter ABI Platform
obelisk_auth-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 27.5 kB

Release files / obelisk_auth-1.0.0.tar.gz

Download URL obelisk_auth-1.0.0.tar.gz
Size 15.3 kB
Tags Source
SHA-256 checksum
How to use checksums
00365d7d5308234c78039e2a51f04f8a26a2a779e4259b13f14cbb1dd6420173
BLAKE2b-256 checksum
How to use checksums
f3ad551ec9b5faf0d33cb5a1f1467eb1933f3dbed19490822704cf3547946773
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release files / obelisk_auth-1.0.0-py3-none-any.whl

Download URL obelisk_auth-1.0.0-py3-none-any.whl
Size 12.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4dd5fb90128209c924c69bede119b23ef629f120cf8542eafa5f5a3c820d7413
BLAKE2b-256 checksum
How to use checksums
11cf29ed367ac045be8f362130dcd9b512caf92fe68ea06cb84e16f6811e63d6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release 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