Playwright-based browser authentication bridge for HTTP APIs with complex auth flows
Project description
web-auth-bridge
A Python library for authenticating against HTTP APIs whose login flows require a real browser — SSO, SAML, Cloudflare, or stateful web portals — and then reusing the resulting session for fast programmatic access.
Overview
web-auth-bridge separates authentication from execution:
- A Playwright-driven browser performs the login once.
- The resulting cookies and tokens are extracted into a typed
AuthResultand cached on disk. - Subsequent calls use a plain
httpxclient with those cookies, or, when a site requires a browser beyond login, a pool of pre-authenticated headless contexts.
The library is intended for CLIs, agents, and integrations that need to script against sites with complex or interactive authentication.
Installation
pip install web-auth-bridge
playwright install chromium
For sites that reject Python's default TLS fingerprint (for example, Garmin's OAuth2 token exchange):
pip install "web-auth-bridge[tls-impersonation]"
Quick start
import asyncio
from web_auth_bridge import WebAuthBridge, AuthResult, Credentials
class ExampleCallback:
async def authenticate(self, page, credentials):
await page.goto("https://example.com/login")
await page.fill('input[name="email"]', credentials.username)
await page.fill('input[name="password"]', credentials.password)
async with page.expect_navigation():
await page.click('button[type="submit"]')
return AuthResult()
async def is_authenticated(self, result):
return not result.is_expired
async def main():
bridge = WebAuthBridge(
app_name="example",
auth_callback=ExampleCallback(),
credentials=Credentials(username="me@example.com", password="..."),
)
await bridge.ensure_authenticated()
async with bridge.http_client() as client:
resp = await client.get("https://example.com/api/me")
print(resp.json())
asyncio.run(main())
On the first run the browser opens to log in. On subsequent runs the cached session is reused until it expires.
When to use it
- Sites guarded by Cloudflare or other WAFs that challenge non-browser clients.
- SAML or OAuth portals without a public token endpoint.
- Internal applications requiring MFA or interactive SSO.
- Stateful web portals (for example, ASP.NET) with no HTTP API surface.
- CLI tools for personal or private APIs where reimplementing the auth flow would cost more than the tool itself.
If a site already offers a clean API token, use httpx directly.
Alternatives
A managed browser is not always the right tool. Lighter-weight approaches exist and, when they work, cost a fraction of the memory and startup time:
cloudscraper,curl_cffi, andtls-clientimpersonate real browser TLS and HTTP/2 fingerprints to defeat fingerprint-based WAFs without running a browser.hrequestsandbotasauruswrap similar techniques with higher-level scraping APIs.- Hand-crafted HTTP flows against documented OAuth, OIDC, or token endpoints remain the best option whenever a site exposes them.
web-auth-bridge exists for the cases those approaches do not cover:
interactive SSO, MFA, SAML, stateful web portals, JavaScript-rendered
challenges, and WAF rulesets that evolve faster than any fingerprint library
can track. The tradeoff is resource cost for resilience — running an actual
browser is heavier than a spoofed request, but it behaves like a browser
because it is one, and it continues to work when the underlying site
changes.
Modes
| Mode | Credentials source | Browser visible | Typical use case |
|---|---|---|---|
| Headless + stored creds | .env / config file |
No | Fully automated CLIs and agents |
| Headed + stored creds | .env / config file |
Yes | Debugging; sites requiring a visible UI |
| Headed + manual entry | User types into page | Yes | Secrets that must not be stored on disk |
| Headless + no creds | Cached session | No | Every run after the first, while cached |
Parallel headless browsers
For sites without a usable API, the bridge can produce a pool of pre-authenticated browser contexts:
await bridge.ensure_authenticated()
async with bridge.context_pool(size=4) as pool:
pages = [await ctx.new_page() for ctx in pool]
...
Documentation
- Architecture — components, flows, and diagrams
- Design notes — decisions, alternatives, and constraints
- Examples — minimal snippets for common flows
- Testing — unit, integration, and live-site tests
- Garmin example — end-to-end Cloudflare-guarded SSO plus OAuth2 Bearer token flow
Development
git clone https://github.com/mikejhill/web-auth-bridge
cd web-auth-bridge
uv sync --all-extras
uv run playwright install chromium
uv run poe test
uv run poe cov
uv run poe lint
Contributions are welcome. Please use Conventional Commits; the release workflow derives versions from commit messages.
License
MIT
Project details
Release history Release notifications | RSS feed
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 web_auth_bridge-0.1.1.tar.gz.
File metadata
- Download URL: web_auth_bridge-0.1.1.tar.gz
- Upload date:
- Size: 25.7 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bc6be36f25ffeb858cd0ca50bf0b4f32701779c9c94db92b097e32811885f9ee
|
|
| MD5 |
41b9b2a3b182b6994e1dfd0b6caf4de9
|
|
| BLAKE2b-256 |
04b3036f0fc4a8212c63ad02d348caaf7c0c2bdd4d5b13f87abf08c1ac2b2748
|
Provenance
The following attestation bundles were made for web_auth_bridge-0.1.1.tar.gz:
Publisher:
release.yml on mikejhill/web-auth-bridge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
web_auth_bridge-0.1.1.tar.gz -
Subject digest:
bc6be36f25ffeb858cd0ca50bf0b4f32701779c9c94db92b097e32811885f9ee - Sigstore transparency entry: 1323106802
- Sigstore integration time:
-
Permalink:
mikejhill/web-auth-bridge@79ef722f00e6fa62570d473198f53efb13494381 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/mikejhill
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@79ef722f00e6fa62570d473198f53efb13494381 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file web_auth_bridge-0.1.1-py3-none-any.whl.
File metadata
- Download URL: web_auth_bridge-0.1.1-py3-none-any.whl
- Upload date:
- Size: 26.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
de6f9cdfb0ab64c3f88dbb17cced913840ba4d5c90e5690ca2887426028ce02f
|
|
| MD5 |
356f0950f59abf029e70537155a0475c
|
|
| BLAKE2b-256 |
8e1767f36fdadbeed30b02b0707058bc9c46cdfbce91087a1f5da0eecaea63ca
|
Provenance
The following attestation bundles were made for web_auth_bridge-0.1.1-py3-none-any.whl:
Publisher:
release.yml on mikejhill/web-auth-bridge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
web_auth_bridge-0.1.1-py3-none-any.whl -
Subject digest:
de6f9cdfb0ab64c3f88dbb17cced913840ba4d5c90e5690ca2887426028ce02f - Sigstore transparency entry: 1323106926
- Sigstore integration time:
-
Permalink:
mikejhill/web-auth-bridge@79ef722f00e6fa62570d473198f53efb13494381 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/mikejhill
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@79ef722f00e6fa62570d473198f53efb13494381 -
Trigger Event:
workflow_dispatch
-
Statement type: