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 onlyPOST /auth/token— exchangecode+code_verifier→id_token/access_token/refresh_tokenGET /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)
| File | Size | Uploaded | |
|---|---|---|---|
| obelisk_auth-1.0.0.tar.gz | 15.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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
|