qrauth
Official Python SDK for QRAuth -- cryptographic QR code verification and anti-fraud infrastructure.
Install
pip install qrauth
Quick Start
from qrauth import QRAuth
qr = QRAuth(api_key="qrauth_xxx")
# Generate a verified QR code
code = qr.create(
"https://municipal-parking.com/pay",
location={"lat": 40.63, "lng": 22.94},
expires_in="1y",
)
print(code["verification_url"])
# -> https://qrauth.io/v/xK9m2pQ7
# Verify a scanned QR code
result = qr.verify("xK9m2pQ7")
print(result["verified"]) # True
print(result["security"]["trustScore"]) # 94
API
QRAuth(api_key=None, *, client_id=None, client_secret=None, base_url="https://qrauth.io")
At least one of api_key or client_id must be provided.
| Parameter | Type | Description |
|---|---|---|
api_key |
str |
API key (starts with qrauth_). Required for QR management calls |
client_id |
str |
App client ID (starts with qrauth_app_). Required for auth sessions |
client_secret |
str |
App client secret. Required alongside client_id |
base_url |
str |
API base URL. Defaults to https://qrauth.io |
qr.create(destination, **kwargs)
Generate a cryptographically signed QR code.
code = qr.create(
"https://example.com/pay",
label="Parking Meter #42",
location={"lat": 40.63, "lng": 22.94, "radiusM": 100},
expires_in="30d",
)
| Parameter | Type | Description |
|---|---|---|
destination |
str |
Target URL |
label |
str |
Human-readable label |
location |
dict |
{"lat": ..., "lng": ..., "radiusM": ...} geo-fence |
expires_in |
str |
Duration (30s, 5m, 6h, 30d, 1y) or ISO date |
content_type |
str |
Content type: url, event, coupon, vcard, etc. |
content |
dict |
Structured content (for non-URL types) |
qr.verify(token, *, client_lat=None, client_lng=None)
Verify a QR code and get its trust score.
result = qr.verify("xK9m2pQ7", client_lat=40.63, client_lng=22.94)
qr.list(*, page=1, page_size=20, status=None)
List QR codes for your organization.
response = qr.list(page=1, page_size=20, status="ACTIVE")
for item in response["data"]:
print(item["token"], item["status"])
qr.get(token)
Get details of a specific QR code.
qr.revoke(token)
Revoke a QR code so it no longer verifies.
qr.bulk(items)
Create up to 64 QR codes in a single request.
result = qr.bulk([
{"destination": "https://example.com/1", "label": "Meter 1"},
{"destination": "https://example.com/2", "label": "Meter 2"},
])
QR-Based Authentication (Auth Sessions)
Use auth sessions to let users authenticate on your site by scanning a QR code with the QRAuth app.
Requires an App client_id and client_secret (configured in your QRAuth dashboard).
QRAuth(client_id, client_secret, *, base_url="https://qrauth.io")
from qrauth import QRAuth
qr = QRAuth(client_id="qrauth_app_xxx", client_secret="your_secret")
You can combine both modes in a single instance:
qr = QRAuth(
api_key="qrauth_xxx",
client_id="qrauth_app_xxx",
client_secret="your_secret",
)
qr.create_auth_session(**kwargs) → AuthSessionResponse
Create a new auth session. Display session["qr_url"] as a QR code for the user to scan.
session = qr.create_auth_session(scopes=["identity", "email"])
print(session["qr_url"]) # encode this as a QR code
print(session["session_id"]) # use to poll status
| Parameter | Type | Description |
|---|---|---|
scopes |
list[str] |
Requested scopes (e.g. ["identity", "email"]) |
redirect_url |
str |
URL to redirect after approval (deep-link flows) |
metadata |
dict |
Arbitrary metadata attached to the session |
code_challenge |
str |
PKCE code challenge (S256). Required for public clients |
code_challenge_method |
str |
Code challenge method. Only "S256" is supported |
qr.get_auth_session(session_id, *, code_verifier=None) → AuthSessionStatus
Poll the session status. Call this on an interval (e.g. every 2 seconds) until
status is "APPROVED", "DENIED", or "EXPIRED".
import time
while True:
status = qr.get_auth_session(session["session_id"])
if status["status"] == "APPROVED":
print(status["user"]) # {"id": "...", "name": "...", "email": "..."}
break
if status["status"] in ("DENIED", "EXPIRED"):
break
time.sleep(2)
qr.verify_auth_result(session_id, signature) → AuthSessionVerifyResult
Verify the approved session on your backend. Call this after your frontend
receives onSuccess from the browser SDK to confirm the session is genuine
before issuing your own session or JWT.
result = qr.verify_auth_result(session_id, signature)
if result["valid"]:
user = result["session"]["user"]
# issue your own session for the user
Full flow example
from qrauth import QRAuth
import time
qr = QRAuth(client_id="qrauth_app_xxx", client_secret="your_secret")
# 1. Create session and show QR code to user
session = qr.create_auth_session(scopes=["identity", "email"])
print(f"Scan this URL: {session['qr_url']}")
# 2. Poll until resolved
while True:
s = qr.get_auth_session(session["session_id"])
if s["status"] == "APPROVED":
break
if s["status"] in ("DENIED", "EXPIRED"):
raise Exception("Auth failed")
time.sleep(2)
# 3. Verify on the backend
result = qr.verify_auth_result(session["session_id"], s["signature"])
if result["valid"]:
print(result["session"]["user"]) # log in the user
Context Manager
The client can be used as a context manager to ensure the HTTP connection pool is properly closed:
with QRAuth(api_key="qrauth_xxx") as qr:
code = qr.create("https://example.com")
Error Handling
from qrauth import QRAuth, AuthenticationError, RateLimitError
try:
qr.create("https://example.com")
except AuthenticationError:
print("Bad API key")
except RateLimitError as err:
print(f"Rate limited. Retry after {err.retry_after}s")
| Exception | HTTP Status | Description |
|---|---|---|
ValidationError |
400 | Request validation failed |
AuthenticationError |
401 | Invalid or missing API key |
AuthorizationError |
403 | Insufficient permissions |
NotFoundError |
404 | Resource not found |
RateLimitError |
429 | Rate limit exceeded |
QuotaExceededError |
429 | Plan quota exceeded |
QRAuthError |
* | Base class for all errors |
License
MIT
Metadata
Release files for qrauth 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| qrauth-0.3.0.tar.gz | 14.4 kB | Without provenance | Details for qrauth-0.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| qrauth-0.3.0-py3-none-any.whl | Python 3 | none | any | Details for qrauth-0.3.0-py3-none-any.whl |
Total release size: 30.5 kB
Release files / qrauth-0.3.0.tar.gz
| Download URL | qrauth-0.3.0.tar.gz |
|---|---|
| Size | 14.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e199855f92f7f14d57b862c9bc10541ed604260fa31b5f77e50323fde710058f
|
|
BLAKE2b-256 checksum How to use checksums |
61b5ae2eebc20c755dbd4098ad38c57c61157cd6ebacedafc133470d6e6d2f41
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Release files / qrauth-0.3.0-py3-none-any.whl
| Download URL | qrauth-0.3.0-py3-none-any.whl |
|---|---|
| Size | 16.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a9dd8244e431ac99db2a63d77d740e5aa5e1bc8311638dcdf9958937a6a7d8f5
|
|
BLAKE2b-256 checksum How to use checksums |
8a33ffaf53e2d96f2f2db85a8d7e10110e2495d34c64061bf5181f137e4ecea7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Security for qrauth 0.3.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.
To learn more about protecting yourself from malicious actors, visit our package security documentation.