Skip to main content

incorta-auth (Python)

PyPI version license

Authenticate Incorta users in Python apps. The Python port of @incorta/auth — it gates your app behind the real Incorta login (no custom login page) using the OAuth 2.0 / OIDC authorization server built into Incorta.

Both SDKs implement one spec: the same OAuth authorization-code flow against {INCORTA_URL}/oauth/{tenant}, the same INCORTA_* configuration, and the same encrypted-cookie sealing (compact JWE, dir + A256GCM, key derived SHA-256("incorta-auth:" + secret)) — cookies sealed by either language unseal in the other, verified by fixtures generated with the TS implementation (tests/fixtures/).

Status

  • Core protocol layer: configuration, endpoint discovery with split-horizon support, authorization-code exchange, silent refresh with rotation, RS256/JWKS token verification, session sealing, and RFC 7591/7592 client registration as library functions.
  • ASGI / FastAPI adapter: IncortaAuthMiddleware gates any ASGI app behind Incorta login and serves /auth/*; incorta_auth.fastapi provides the get_session dependency. See examples/fastapi.
  • Streamlit adapter: incorta_auth.streamlit.gate() gates a Streamlit app (which has no routes and no response-cookie control) behind Incorta login. See examples/streamlit and the design notes below.

Usage (FastAPI / any ASGI app)

from fastapi import Depends, FastAPI
from incorta_auth import AuthSession
from incorta_auth.asgi import IncortaAuthMiddleware
from incorta_auth.fastapi import get_session

app = FastAPI()
app.add_middleware(IncortaAuthMiddleware)  # config from INCORTA_* env vars

@app.get("/api/me")
def me(session: AuthSession = Depends(get_session)):
    return {"user": session.user.sub, "roles": session.user.roles}
    # session.access_token → Authorization: Bearer for Incorta REST APIs

The middleware serves {base_path}/{login,callback,session,logout,signed-out} (+ /token when expose_access_token=True), attaches the session to request.state.incorta_auth, redirects signed-out browser navigations into Incorta login (relative Location — proxy-safe), 401s signed-out data requests, refuses signed-out websockets, and answers everything with an honest 503 while the INCORTA_* env is missing. public_paths=("/api/health",) exempts liveness probes from both the gate and the 503. For apps embedded in a cross-site iframe, spread embedded_cookie_config() into the middleware (SameSite=None + Partitioned).

Install

uv add incorta-auth        # or: pip install incorta-auth

Prerequisites

Same as the TS SDK (see the repo README): enable the authorization server on the cluster, then register an OAuth client. The registration CLI ships with the npm package —

npx incorta-auth register \
  --url http://localhost:8080/incorta \
  --tenant demo \
  --name "My App" \
  --redirect http://localhost:4000/auth/callback

— or use the library functions below from Python.

Usage (core)

Configuration comes from INCORTA_* env vars (INCORTA_URL, INCORTA_TENANT, INCORTA_CLIENT_ID, INCORTA_CLIENT_SECRET, INCORTA_AUTH_SECRET, optional INCORTA_APP_URL / INCORTA_INTERNAL_URL / INCORTA_COOKIE_SAMESITE / INCORTA_COOKIE_PARTITIONED) or explicit keyword arguments:

from incorta_auth import IncortaAuth

auth = IncortaAuth()  # reads INCORTA_* env vars

# Send the browser to Incorta login:
login = auth.begin_login(origin="http://localhost:4000", secure=False, redirect_to="/")
# → redirect to login.location, set login.set_cookies

# Complete the callback (?code&state):
result = auth.complete_callback(
    origin="http://localhost:4000", secure=False,
    cookie_header=request_cookie_header, query=dict(query_params),
)
# → redirect to result.location, set result.set_cookies

# Gate requests / read the user and token:
session = auth.get_session(request_cookie_header)
if session:
    session.user.sub, session.user.roles, session.access_token

# Refresh-aware load (rotates the refresh token, slides the session cookie):
loaded = auth.load_session(cookie_header=request_cookie_header, secure=False)

Client registration (RFC 7591/7592):

from incorta_auth import register_client, get_client, update_client_redirect_uris, delete_client

client = register_client(
    incorta_url="http://localhost:8080/incorta", tenant="demo",
    name="My App", redirect_uris=["http://localhost:4000/auth/callback"],
)

Usage (Streamlit)

import streamlit as st
import incorta_auth.streamlit as incorta

user = incorta.gate()        # first line of the entry script; st.stop()s until signed in
st.write(f"Hello {user.name}", user.roles)
incorta.access_token()        # Bearer for Incorta REST APIs (auto-refreshed)
if st.button("Sign out"):
    incorta.sign_out()        # interstitial — SSO would otherwise bounce right back in

The OAuth redirect_uri is the app root ({INCORTA_APP_URL}/) — register exactly that. Tokens live only in st.session_state; a hard refresh silently re-authenticates through Incorta SSO. Multi-page entry scripts can restore the originally requested page with pop_return_path().

Streamlit design notes (browser-spike findings, 2026-07-20)

Streamlit cannot host a callback route, set response cookies, or (it turns out) navigate from component JS — the flow is built from the three vehicles a real-browser spike (Playwright, Streamlit 1.59) validated:

  1. Navigation: components.html iframes are sandboxed allow-same-origin allow-scripts … without allow-top-navigation, so JS cannot redirect the app. st.html strips both scripts and meta tags (DOMPurify). The one silent vehicle is a st.markdown(unsafe_allow_html=True) meta-refresh, which navigates the app frame itself — allowed everywhere, including inside iframes.
  2. State cookie: components.html JS can write cookies on the app origin (allow-same-origin + srcdoc). The sealed login state (state/nonce/return target, same JWE sealing as the TS SDK's state cookie) is written in two variants — SameSite=Lax for top-level tabs and SameSite=None; Secure; Partitioned for cross-site iframes (the Incorta builder preview; verified in a genuine cross-site embed) — and read back after the round-trip via st.context.cookies.
  3. Ordering: the cookie write reliably precedes the meta navigation (5/5 in the spike) — both are same-turn DOM operations and the srcdoc iframe has no network dependency. Binding is strict: a missing state cookie fails the callback with state_missing (the same failure a cookie-blocked browser gets from the TS SDK) and offers a retry button.

Compatibility note: the meta-refresh passthrough and the component sandbox flags are Streamlit implementation details — the full flow is pinned by a browser e2e and the AppTest suite, so a Streamlit upgrade that changes either fails loudly, not silently.

Development

uv sync
uv run pytest
uv run ruff check . && uv run ruff format --check .
uv run mypy

The test suite runs against an in-process replica of Incorta's authorization server (tests/mock_incorta.py, a port of the TS suite's mock-incorta.ts) served through httpx.MockTransport — one-time 60s codes, rotating refresh tokens, and split-horizon simulation included. Regenerate the cross-language sealing fixtures with:

node tests/fixtures/generate-fixtures.mjs > tests/fixtures/sealed-fixtures.json

Metadata

Release files for incorta-auth 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 incorta-auth 0.1.0
File Size Uploaded Provenance
incorta_auth-0.1.0.tar.gz 203.7 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for incorta-auth 0.1.0
File Interpreter ABI Platform
incorta_auth-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 237.8 kB

Release files / incorta_auth-0.1.0.tar.gz

Download URL incorta_auth-0.1.0.tar.gz
Size 203.7 kB
Tags Source
SHA-256 checksum
How to use checksums
7a0e4694f95991dceef6b935ada9492b9d8f79a9365cefa231b7cce2fca81aac
BLAKE2b-256 checksum
How to use checksums
8de65c11aa7b1c64155e5cff5d038912e77ce1fedd437d2aaf62f2a144f7946f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

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

Download URL incorta_auth-0.1.0-py3-none-any.whl
Size 34.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
282205c1d10d6d2adcdb6a6b4faf571708f65ff568b8d2c0419d10ee7ae046f7
BLAKE2b-256 checksum
How to use checksums
118b533e77caf7fbcd44b01a071448d1dd0fd5fbba4483e4bd8c83e1147cd253
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for incorta-auth 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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

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.

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