UseThatApp Logo
    Python/Streamlit

    Streamlit

    Sign users in with their UseThatApp account and read their live plan from a Streamlit app, over OAuth 2.0 / OpenID Connect. Streamlit has no request/route model, so it can't be a confidential OIDC client on its own. The realistic pattern is a small companion server (a backend-for-frontend, or BFF) that runs the SDK and handles /login, /callback, and /logout; Streamlit reads the resulting session and entitlement from it. The same BFF wires your app into the whole selling loop: a buyer who purchases from your own website lands back signed in, entitlement already live — and marketplace launches arrive the same way. For the conceptual model, see OpenID Connect.

    ⚠ This is a sidecar / BFF setup.

    The usethatapp SDK is a confidential client — it holds the client secret and validates ID tokens, so it must run server-side. A Streamlit script restarts top-to-bottom on every interaction and has no place to receive the OAuth redirect, so the OIDC flow lives in a small companion server (Flask or FastAPI) and Streamlit just consumes its result.

    This guide covers the Hosted sign-in path

    It has UseThatApp run login, so buyers return signed in and the same session answers what they bought — the Hosted sign-in add-on. If your app already has its own accounts, you do not need any of the sign-in wiring below: keep your login and verify purchases with a license key your server validates in one call, included in the per-sale fee.

    The shape

    1. BFF server (Flask/FastAPI). Owns the OIDC flow: /login → begin_login, /callback → complete_login, /logout → logout_url. It also exposes a tiny /me endpoint that returns the current sub and entitlement for the browser's session cookie. This is the same Flask integration documented on the Flask page, with one extra read endpoint.
    2. Streamlit app. Links to the BFF's /login and /logout, and calls /me to find out who the user is and what plan they're on. It never touches the SDK or the client secret.

    Step 1 — Install

    terminal
    pip install usethatapp flask streamlit requests

    Step 2 — The BFF read endpoint

    Add /login, /callback, and /logout exactly as on the Flask page. Then add a JSON /me endpoint that reports the live entitlement for the current session, reconciling a revoked token.

    bff.py
    from flask import session
    from usethatapp import UtaTokenError, get_entitlement
    
    @app.get("/me")
    def me():
        token = session.get("uta_access_token")
        if not token:
            return {"signed_in": False}            # anonymous = your free tier
        try:
            ent = get_entitlement(token)
        except UtaTokenError:
            # Token revoked/expired (signed out of UseThatApp). Reconcile.
            for k in ("uta_access_token", "uta_sub", "uta_id_token"):
                session.pop(k, None)
            return {"signed_in": False}
        return {
            "signed_in": True,
            "sub": session.get("uta_sub"),
            "entitled": ent.entitled,
            "product_public_id": ent.product_public_id,
            "version": ent.version,
            "status": ent.status,
        }

    Step 3 — Streamlit reads the BFF

    Point Streamlit at the BFF. Forward the browser's cookies so the BFF can identify the session, then gate the UI on the stable product_public_id, not the mutable version display name. Link out to the BFF for sign-in and sign-out — those are full-page redirects the companion server owns.

    streamlit_app.py
    import requests
    import streamlit as st
    
    BFF = "http://localhost:5000"
    
    me = requests.get(f"{BFF}/me", cookies=st.context.cookies).json()
    
    if not me.get("signed_in"):
        st.markdown(f"[Log in with UseThatApp]({BFF}/login)")
        st.info("You're on the free tier. Log in to see your plan.")
    else:
        st.markdown(f"[Log out]({BFF}/logout)")
        if me["entitled"] and me["product_public_id"] == "prod_<your-pro-id>":
            st.success(f"Pro features unlocked — {me['version']}")
        else:
            st.write(f"Plan: {me['version']} (status={me['status']})")

    Use sub as the user key

    The BFF returns the user's pairwise sub — stable per app, uncorrelatable across apps, no PII. Key your user records off sub, never an email. The entitlement response does not include sub; it comes from the login, which the BFF holds.

    Offer the upgrade: purchase links

    When /me says the user is not entitled — or is on the free tier — send them to hosted checkout. UseThatApp is the merchant of record (payment, tax, refunds), and the buyer comes back signed in with the entitlement already live: no webhooks, no polling. It fits the BFF split cleanly: purchase_url() and manage_url() are pure URL builders — no network call — so the BFF hands ready-made URLs to Streamlit, which just renders them.

    bff.py
    from usethatapp import manage_url, purchase_url
    
    PRO_PRICE_ID = "<prc-price-id>"   # from get_prices() or your dashboard
    
    @app.get("/me")
    def me():
        # ... token check and entitlement read exactly as in Step 2 ...
        return {
            "signed_in": True,
            "sub": session.get("uta_sub"),
            "entitled": ent.entitled,
            "product_public_id": ent.product_public_id,
            "version": ent.version,
            "status": ent.status,
            # Pure URL builders — no network call. Streamlit just renders them.
            "buy_url": purchase_url(PRO_PRICE_ID),
            "manage_url": manage_url(),
        }
    streamlit_app.py
    if me.get("signed_in"):
        if me["entitled"] and me["product_public_id"] == "prod_<your-pro-id>":
            st.link_button("Manage subscription", me["manage_url"])
        else:
            # Hosted checkout; the buyer returns signed in, entitled.
            st.link_button("Upgrade to Pro", me["buy_url"])

    For a live pricing page, have the BFF render from get_prices() — never hardcode prices. Each Price carries a ready-made buy_url and the product_id that matches the entitlement’s product_public_id. See the Pricing API and Sell from your own website.

    Run it

    Run the BFF and Streamlit side by side. Register the BFF's /callback as the redirect URI on the UseThatApp dashboard.

    terminal
    # terminal 1 — the BFF
    export UTA_CLIENT_ID=... UTA_CLIENT_SECRET=...
    export UTA_REDIRECT_URI=http://localhost:5000/callback
    # Pointing at a non-production instance? Set BOTH of these
    # (each defaults to production):
    # export UTA_ISSUER=http://localhost:8000/o
    # export UTA_API_URL=http://localhost:8000
    python bff.py
    
    # terminal 2 — Streamlit
    streamlit run streamlit_app.py

    Errors

    Error handling lives in the BFF, at the entitlement endpoint: 401 raises UtaTokenError (re-authenticate or refresh) and 403 raises UtaPermissionError (a valid token missing the entitlements scope). Every SDK error inherits from UtaError. See Error Handling and Gotchas.

    AI agents: this site is fully pre-rendered — fetch /llms.txt for an indexed summary of every page, or /llms-full.txt for the whole documentation as one Markdown file.