Skip to main content

anointedautomation-sso

Jesus is King ✝️

"Sign in with Anointed Automation" for Python: a server-side OpenID Connect kit with a framework-neutral core and thin adapters for Django, FastAPI/Starlette and Flask. Authorization code + PKCE (S256), state, nonce, RS256 ID token verification through the issuer's JWKS, refresh, revoke, userinfo, logout, back-channel logout (validation plus replay protection) and a fail-open session status client. Python 3.10+, MIT.

Server only: the client holds your client secret and does the token exchange. Never ship it to a browser or a mobile app. Get your client_id and client_secret from the partner portal; the full guide is Sign in with Anointed Automation.

pip install anointedautomation-sso                # core
pip install "anointedautomation-sso[flask]"       # or [django], [fastapi]
ANOINTED_OAUTH_CLIENT_ID=aa_<your key id>
ANOINTED_OAUTH_CLIENT_SECRET=<server only>
ANOINTED_OAUTH_REDIRECT_URI=https://yourapp.example/auth/anointed/callback

Register the callback URL (exact https match) and, optionally, the back-channel logout URL on the portal's Sign in tab. Nothing has a hidden default: a missing required setting raises AnointedSsoError with code config.

Core

from anointedautomation_sso import ANOINTED_ISSUER, AnointedClient, require_env

client = AnointedClient(
    issuer=ANOINTED_ISSUER,
    client_id=require_env("ANOINTED_OAUTH_CLIENT_ID"),
    client_secret=require_env("ANOINTED_OAUTH_CLIENT_SECRET"),
    redirect_uri=require_env("ANOINTED_OAUTH_REDIRECT_URI"),
    # scope="openid email",   # the default; add "profile" or "offline_access" only if you need them
)

auth = client.create_authorize_request(return_to="/account")
# 302 to auth.url and set the flow cookie: auth.cookie_name / auth.cookie_value / auth.cookie_options
# (or send auth.set_cookie_header as is).

result = client.handle_callback(query=request_query_dict, cookie_value=flow_cookie_value)
result.user.sub        # key YOUR users on this
result.user.email      # verified email only, trimmed and lowercase
result.return_to       # safe same-site path or None
# Always clear the flow cookie afterwards (client.clear_flow_cookie_header()), on success and failure.

What handle_callback enforces: the flow cookie exists, state matches (constant time) before anything else, the flow is not older than 10 minutes, the RFC 9207 iss parameter (when sent) is the issuer, then the code exchange with the PKCE verifier and a fully verified ID token (RS256 only, iss, aud/azp, exp/iat with skew, nonce, sub, verified email when the email scope is requested). Failures raise AnointedSsoError; branch on err.code (state_mismatch, flow_expired, access_denied, id_token_invalid, email_not_verified, token_exchange, ...) and never show str(err) to users.

Other calls: refresh(refresh_token, expected_sub=...) (a new refresh token each time; invalid_grant means signed out), revoke(token), fetch_user_info(access_token), build_logout_url(id_token_hint=..., post_logout_redirect_uri=...), take_new_user_password(sub) (the one-time password generated for a brand new account on your site; hash it yourself).

return_to is open-redirect safe: same-site paths only, unless you list allowed_return_origins (for example ["https://www.example.com", "https://*.example.com"]) or an is_allowed_return_to predicate. Even then it is https only.

Flask

from flask import Flask, redirect, session
from anointedautomation_sso import ANOINTED_ISSUER, AnointedClient, require_env
from anointedautomation_sso.adapters.flask import create_blueprint

app = Flask(__name__)
client = AnointedClient(
    issuer=ANOINTED_ISSUER,
    client_id=require_env("ANOINTED_OAUTH_CLIENT_ID"),
    client_secret=require_env("ANOINTED_OAUTH_CLIENT_SECRET"),
    redirect_uri=require_env("ANOINTED_OAUTH_REDIRECT_URI"),
)

def on_signed_in(result):
    session["user_sub"] = result.user.sub      # find or create YOUR user by sub
    return redirect(result.return_to or "/")

def on_logout(logout):
    ...  # end every session for logout.sub (or logout.sid)

app.register_blueprint(create_blueprint(client, on_signed_in=on_signed_in, on_logout=on_logout), url_prefix="/auth/anointed")
# GET /auth/anointed/start?returnTo=/account, GET /auth/anointed/callback, POST /auth/anointed/backchannel-logout

Django

# myapp/anointed.py
from django.contrib.auth import get_user_model, login
from django.http import HttpResponseRedirect
from anointedautomation_sso import ANOINTED_ISSUER, AnointedClient, require_env
from anointedautomation_sso.adapters.django import create_views

client = AnointedClient(
    issuer=ANOINTED_ISSUER,
    client_id=require_env("ANOINTED_OAUTH_CLIENT_ID"),
    client_secret=require_env("ANOINTED_OAUTH_CLIENT_SECRET"),
    redirect_uri=require_env("ANOINTED_OAUTH_REDIRECT_URI"),
)

def on_signed_in(request, result):
    user, _ = get_user_model().objects.get_or_create(username=f"aa_{result.user.sub}", defaults={"email": result.user.email or ""})
    login(request, user)
    return HttpResponseRedirect(result.return_to or "/")

def on_logout(logout):
    ...  # delete the sessions of logout.sub (or logout.sid)

views = create_views(client, on_signed_in=on_signed_in, on_logout=on_logout)

# urls.py
urlpatterns = [path("auth/anointed/", include(views.urls()))]

The back-channel route is CSRF-exempt by design (the provider posts without your cookies); everything on it is verified by the Logout Token signature instead. Behind a proxy set SECURE_PROXY_SSL_HEADER as usual; the redirect URI is the fixed value you configured, not derived from the request.

FastAPI / Starlette

from fastapi import FastAPI
from fastapi.responses import RedirectResponse
from anointedautomation_sso import ANOINTED_ISSUER, AnointedClient, require_env
from anointedautomation_sso.adapters.fastapi import create_router   # Starlette: create_routes

app = FastAPI()
client = AnointedClient(
    issuer=ANOINTED_ISSUER,
    client_id=require_env("ANOINTED_OAUTH_CLIENT_ID"),
    client_secret=require_env("ANOINTED_OAUTH_CLIENT_SECRET"),
    redirect_uri=require_env("ANOINTED_OAUTH_REDIRECT_URI"),
)

async def on_signed_in(request, result):
    request.session["user_sub"] = result.user.sub      # needs SessionMiddleware, or use your own session
    return RedirectResponse(result.return_to or "/", status_code=302)

async def on_logout(logout):
    ...  # end every session for logout.sub (or logout.sid)

app.include_router(create_router(client, on_signed_in=on_signed_in, on_logout=on_logout), prefix="/auth/anointed")

Callbacks may be sync or async. The blocking core runs in a worker thread, so the event loop is never blocked. For plain Starlette use create_routes(...) and mount the returned routes.

Back-channel logout

When someone signs out or disconnects your app, Anointed Automation POSTs a signed logout_token to your back-channel logout URL. process_backchannel_logout (used by every adapter) validates it (RS256 via JWKS, typ: logout+jwt, iss, aud, iat, the logout event, no nonce, a jti, sub and/or sid) and answers: 400 for a bad token, 200 for a repeated jti (your handler is not run twice), 500 when the keys cannot be read or your handler raised (the provider retries, and the jti is forgotten so the retry runs). The default replay store is in-memory; with several processes pass a shared LogoutReplayStore (try_add(jti, remember_until) and remove(jti)) backed by Redis or your database.

Session status

End a local session when the user's Anointed Automation account or grant ends. It needs a partner API key with the sessions:status permission (ask us to add it) and fails open: an outage never signs your users out.

from anointedautomation_sso import SessionStatusClient

status = SessionStatusClient(api_key=require_env("ANOINTED_API_KEY"), app_name="My App")
check = status.check(user_sub, grant=True)
if check.should_end_session:
    ...  # sign the user out locally; check.reason says why

Security checklist

  • Keep client_secret and API keys in your secret store, never in source control, logs or a browser bundle.
  • Redirect URIs must be exact https matches; use an https tunnel or staging host to test.
  • Key your users on sub, not on email.
  • Run the callback and the back-channel route over HTTPS only; send Cache-Control: no-store (the adapters do).
  • Use a shared replay store when you run more than one process.

Development

python -m venv .venv && .venv/bin/pip install -e ".[test]" && .venv/bin/python -m pytest

Source: python/anointed-sso. The Node kit is @anointedautomation/sso and the .NET kit is AnointedAutomation.SSO.

Metadata

Release files for anointedautomation-sso 0.1.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 anointedautomation-sso 0.1.0
File Size Uploaded Provenance
anointedautomation_sso-0.1.0.tar.gz 36.9 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for anointedautomation-sso 0.1.0
File Interpreter ABI Platform
anointedautomation_sso-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 71.1 kB

Release files / anointedautomation_sso-0.1.0.tar.gz

Download URL anointedautomation_sso-0.1.0.tar.gz
Size 36.9 kB
Tags Source
SHA-256 checksum
How to use checksums
d63df6edffd475338e6126de48b45fcc838bae7c8921898225a987ad2ec8e17f
BLAKE2b-256 checksum
How to use checksums
5d730cfb2003049ba9fba24f3928b05eb9d670f745ce18cc0aaed73cf2f7aaa6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 24, 2026.

Transparency log

Release files / anointedautomation_sso-0.1.0-py3-none-any.whl

Download URL anointedautomation_sso-0.1.0-py3-none-any.whl
Size 34.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a3e6eda3f594eb831b9e827e80f45692b82e9bf97aaa7c2f7eae4446805bfa38
BLAKE2b-256 checksum
How to use checksums
949c633952fd3005cc93e398c338ee59ee81247ff4d680fc79787ec5b3dd59cc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 24, 2026.

Transparency log

Security for anointedautomation-sso 0.1.0

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.

Select from 2 files

Attestation for anointedautomation_sso-0.1.0.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 Oct 24, 2026.

Transparency log

A 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.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

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