Python SDK for usethatapp.com — OpenID Connect login and live license entitlement queries.
Project description
usethatapp
Python SDK for usethatapp.com. usethatapp.com is
an OpenID Connect provider: this SDK logs a user in through the
marketplace, identifies them by a privacy-preserving sub, and tells you
their live license entitlement for your app.
Framework-agnostic. The SDK never touches your web framework — it
takes and returns plain strings and one JSON-able flow_state dict. You
wire the three framework-specific bits yourself (read the callback query
params, store flow_state in your session, issue the redirect). Works
with Django, Flask, FastAPI, Starlette, Dash, Streamlit, plain WSGI, a
CLI — anything. Runtime deps: httpx, joserfc.
v2.0 is a breaking rewrite. The v1 launch-envelope /
user_key/get_versionhandoff is replaced by standard OAuth2/OIDC. See CHANGELOG.md and Migrating from v1 below.
How it works
- Login (redirect). Start a login with
begin_login(), send the user to usethatapp.com to authenticate, and finish in your callback withcomplete_login(). You get aUtaSessioncarrying the user'ssub(a stable, per-app, pseudonymous id — no PII) and OAuth tokens. - Entitlement (Bearer query). Call
get_entitlement(access_token)whenever you need the user's current license. It's always authoritative — a canceled license stops being entitled immediately.
sub is pairwise: stable for a user within your app, but different
in every other app, so it can't be correlated across apps. Use it as your
local user key — and key off sub, never an email (we never share one).
Install
pip install usethatapp
Python 3.9+. Runtime deps: httpx, joserfc. No web framework required.
Settings
Read from django.conf.settings if Django is installed and configured,
otherwise from environment variables.
| Name | Required | Purpose |
|---|---|---|
UTA_CLIENT_ID |
yes | Your app's OAuth client id (from the usethatapp.com dashboard). |
UTA_REDIRECT_URI |
yes | Your registered callback URL. |
UTA_CLIENT_SECRET |
yes* | Client secret. *Omit for a public (browser/native) PKCE client. |
UTA_CLIENT_SECRET_PATH |
no | Read the secret from a mounted file instead (Render/k8s/Fly). |
UTA_ISSUER |
no | Defaults to https://www.usethatapp.com/o. |
UTA_API_URL |
no | Defaults to https://www.usethatapp.com. |
UTA_SCOPES |
no | Defaults to openid entitlements. |
UTA_CLOCK_SKEW_SECONDS |
no | ID-token validation leeway. Defaults to 60. |
UTA_REQUEST_TIMEOUT_SECONDS |
no | Defaults to 10. |
Public API
from usethatapp import (
begin_login, # -> (authorization_url, flow_state)
complete_login, # (code=, state=, flow_state=) -> UtaSession
get_entitlement, # (access_token) -> Entitlement
get_entitlement_async,
refresh, # (refresh_token) -> UtaSession
userinfo, # (access_token) -> {"sub": ...}
logout_url, # (id_token=, post_logout_redirect_uri=) -> str
UtaSession, # sub, access_token, refresh_token, id_token, expires_at, ...
Entitlement, # entitled, version, product_id, status, is_free, period_end
# errors:
UtaError, UtaConfigError, UtaDiscoveryError, UtaAuthError,
UtaTokenError, UtaPermissionError, UtaServerError,
)
Quickstart — any framework
from usethatapp import begin_login, complete_login, get_entitlement
# 1) Start login — however your framework spells "redirect":
auth_url, flow_state = begin_login()
save_to_session("uta_flow", flow_state) # JSON-able dict
return redirect(auth_url)
# 2) In your callback (reads ?code=...&state=... off the request).
# On cancel/deny the provider sends ?error=... and no code — handle it first:
if read_query("error"):
return redirect("/") # login was canceled
session = complete_login(
code=read_query("code"),
state=read_query("state"),
flow_state=load_from_session("uta_flow"),
)
save_to_session("uta_sub", session.sub)
save_to_session("uta_access_token", session.access_token)
# 3) Anywhere you gate features:
ent = get_entitlement(load_from_session("uta_access_token"))
if ent.entitled and ent.product_id == "...":
...
Runnable per-framework demos live under examples/. They
are documentation only — nothing framework-specific ships in the package.
Error mapping
get_entitlement maps status codes to typed exceptions:
| Status | Exception | Meaning |
|---|---|---|
| 401 | UtaTokenError |
Access token invalid/expired — re-auth/refresh. |
| 403 | UtaPermissionError |
Token lacks the entitlements scope. |
| 400 | UtaError |
Client not linked to an app (misconfig). |
| 5xx | UtaServerError |
Retriable with backoff. |
All inherit from UtaError — catch that for a single except clause.
Signing out
Sign-out is RP-initiated: redirect the user to logout_url(id_token=…). Both
outcomes — they confirm, or they choose "Stay signed in" — return to your
post_logout_redirect_uri, so you can't tell which happened from the
redirect alone.
So don't clear your session when you start logout. Reconcile on return
using the token instead: a confirmed logout revokes it, so your next
get_entitlement() raises UtaTokenError (401) — drop the token then. If they
stayed signed in, the token is still valid and they keep their session.
Clearing eagerly logs the user out of your app even when they chose to stay.
Migrating from v1
| v1 | v2 |
|---|---|
get_user(payload) (decrypt envelope) |
begin_login() + complete_login() (OIDC) |
UtaUser.user_key |
UtaSession.sub (pairwise, stable per app) |
get_version(user_key) -> str |
get_entitlement(access_token) -> Entitlement |
RSA keys (UTA_PRIVATE_KEY, market key) |
OAuth client (UTA_CLIENT_ID/UTA_CLIENT_SECRET) |
UTA_APP_ID |
(gone — the client id identifies your app) |
uta_launch_view Django decorator |
(gone — wire your own callback view) |
Register an OAuth client and redirect URI in your usethatapp.com
developer dashboard to get UTA_CLIENT_ID / UTA_CLIENT_SECRET.
License
MIT — see LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file usethatapp-2.0.0.tar.gz.
File metadata
- Download URL: usethatapp-2.0.0.tar.gz
- Upload date:
- Size: 17.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
40a0d2e261e0d37d46c0f0a11707f33920804136b16455a0ecf6f6f9fe3aa8da
|
|
| MD5 |
e2b9036809463bd75e1149d7ac7c399b
|
|
| BLAKE2b-256 |
4cdfdd6fef33e2c11f74d7306053c714cf34fbb8787eaf5cfea1d17851bcc406
|
File details
Details for the file usethatapp-2.0.0-py3-none-any.whl.
File metadata
- Download URL: usethatapp-2.0.0-py3-none-any.whl
- Upload date:
- Size: 15.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3dea9aa5695b1b5b5ee19ee1950dd72efef6d5839e43b0b5ec4f6ce2063a0835
|
|
| MD5 |
aaea1b98f6387e51ba6d19c70ae9e1ee
|
|
| BLAKE2b-256 |
f15868667d6605e47fdf792728a803fd487c629a7f4299510ffea0765e325879
|