Skip to main content

Stackure Python SDK

Check build PyPI version Python versions PyPI - Downloads Trusted publisher License: MIT

Passwordless magic-link authentication SDK for Python — drop-in ASGI and WSGI middleware, zero dependencies.

Protect an app with one line, or verify sessions and send magic links directly against the Stackure auth API.

Install

pip install stackure

Requires Python 3.14+.

Configure

export STACKURE_APP_SECRET=...   # from the app page in Stackure, shown once

Sent as X-App-Secret on every call except sign-out. The first call that needs it raises StackureError("validation") if it is missing. STACKURE_BASE_URL optionally overrides the API host.

A newly registered app is not usable by anyone, even its creator, until it is shared with the organization or assigned to a team in Stackure. Do that before testing sign-in.

Protect an app

import stackure

app_id = "7f3c1a2e-9b4d-4e6f-8a1b-2c3d4e5f6071"  # your app's UUID in Stackure

# ASGI — FastAPI, Starlette, Quart
app = stackure.auth(app_id, "can_approve_invoice")(app)

# WSGI — Flask, Django
flask_app.wsgi_app = stackure.auth(app_id, "can_approve_invoice")(flask_app.wsgi_app)

The same wrapper handles both; it detects the protocol it was called under.

Access the authenticated user in your view:

user = stackure.user_from_request(request)
print(user.user_email, user.account_id, user.user_permissions)
  • API requests get JSON errors
  • Browser requests get redirected to sign-in
  • The sign-in handoff is automatic: Stackure POSTs a session_token (an app-scoped session token valid only for this app) back to your app, the middleware validates it and stores it as a stackure_session cookie on your domain (not session, which Flask uses for its own session). Handoff bodies over 4 KB are ignored.

MCP

# Wrap the MCP app itself, then route to it beside the rest of your app
mcp = stackure.mcp(app_id, "can_approve_invoice")(mcp_app)

# Starlette / FastAPI
routes = [Mount("/mcp", mcp)]

AI clients (Claude, Claude Code, VS Code, Cursor) sign users in through Stackure. This one line checks every MCP request in real time with the same app secret; there is no extra setup. The MCP endpoint must be served from the same site as the app's registered URL unless an MCP URL is set for the app in Stackure.

  • Wraps ASGI and WSGI apps like auth, with the same optional permissions and user_from_request
  • Reads Authorization: Bearer and ignores cookies, so keep the MCP route outside stackure.auth
  • With Starlette or FastAPI Mount, the address AI clients use ends in a slash: /mcp/
  • Not signed in gets a 401 with WWW-Authenticate, a missing permission a 403, a failed check a 503; never a redirect

Requirements

Sessions are not bound to the browser's user agent or IP. The SDK still forwards the original User-Agent and X-Forwarded-For when validating from your server, but they are informational only.

Every request with a session token is validated against Stackure, so revocation is immediate. Requests without a well-formed token get the sign-in URL without a Stackure call.

Every call has one 2-second deadline covering connect, headers, body and the single retry. Calls retry once after 500 ms on a 5xx or a connection failure, never on a timeout. A timeout anywhere, including while reading the body, raises StackureError("timeout").

Verify manually

result = stackure.verify(app_id, request)

if not result.authenticated:
    # result.error.code, result.error.message, result.error.sign_in_url
    ...

# result.user

verify never raises — transport and API failures come back as a 500 result. It accepts a WSGI environ, an ASGI scope, or a framework request object (Starlette, FastAPI, Flask, Django).

resp = stackure.send_magic_link("user@example.com", app_id)
# resp.message

Log out

METHODS = ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]

# Flask
@app.route("/logout", methods=METHODS)
def logout():
    r = stackure.logout(request)
    return "", r.status, r.headers

# FastAPI
@app.api_route("/logout", methods=METHODS, include_in_schema=False)
async def logout(request: Request):
    r = await asyncio.to_thread(stackure.logout, request)
    return Response(status_code=r.status, headers=dict(r.headers))

# Starlette: the same function, routed with
Route("/logout", logout, methods=METHODS)
<form method="post" action="/logout"><button>Sign out</button></form>

Mount it for every method on the logout path. Trigger it with a form or button that POSTs from the app's own page; a link or any other request is sent to Stackure's sign-out page, where the user confirms.

A same-origin POST signs the user out of Stackure everywhere with one server-side call, clears the app's cookie and redirects to Stackure. If that call fails, the redirect goes to Stackure's sign-out page instead, where the user can finish signing out.

logout is synchronous and returns a Redirect (status, headers) for your framework to send; it never raises. It blocks for up to 2 seconds, hence asyncio.to_thread in the async def view.

Errors

Everything except verify and logout raises StackureError; those two never raise. Switch on .code:

from stackure import StackureError

try:
    stackure.send_magic_link(email)
except StackureError as err:
    match err.code:
        case "validation": ...  # bad input
        case "auth": ...        # 401 from the API
        case "forbidden": ...   # 403 from the API
        case "timeout": ...     # request exceeded the 2s deadline
        case "network": ...     # everything else

Contributing

Open a PR.

Security

Report vulnerabilities via GitHub Security Advisories. Releases publish to PyPI via OIDC trusted publishing with GitHub build-provenance attestations.

License

MIT

Metadata

Release files for stackure 1.20261005.13

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

Source distribution (sdist)

Source distribution for stackure 1.20261005.13
File Size Uploaded Provenance Actions
stackure-1.20261005.13.tar.gz 24.3 kB With provenance Details for stackure-1.20261005.13.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for stackure 1.20261005.13
File Interpreter ABI Platform Actions
stackure-1.20261005.13-py3-none-any.whl Python 3 none any Details for stackure-1.20261005.13-py3-none-any.whl

Total release size: 44.3 kB

Release files / stackure-1.20261005.13.tar.gz

Download URL stackure-1.20261005.13.tar.gz
Size 24.3 kB
Tags Source
SHA-256 checksum
How to use checksums
f460babe8d9bb01309816ba6850a4898d30cd0adaa8aba4b5f69955e5c34bfc5
BLAKE2b-256 checksum
How to use checksums
41c070343ea855a418206591ac2c1a78ad7a47b518b745b4b29da6c4b13a0357
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 Oct 5, 2026.

Transparency log

Release files / stackure-1.20261005.13-py3-none-any.whl

Download URL stackure-1.20261005.13-py3-none-any.whl
Size 20.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
198330c957e6f8f1145532e249383aca883ee4dccf6e3803d9b74616ab2f5bda
BLAKE2b-256 checksum
How to use checksums
1ad6e99f2f6068667425d2d81fa700de00c0d15550f01c6808e23f8457b6ec67
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 Oct 5, 2026.

Transparency log

Security for stackure 1.20261005.13

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 stackure-1.20261005.13.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 5, 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.

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