Skip to content

feat(bfabric): OAuth webapp launch-token primitives#527

Draft
leoschwarz wants to merge 3 commits into
mainfrom
feature/oauth-webapp-primitives
Draft

feat(bfabric): OAuth webapp launch-token primitives#527
leoschwarz wants to merge 3 commits into
mainfrom
feature/oauth-webapp-primitives

Conversation

@leoschwarz

@leoschwarz leoschwarz commented Jun 25, 2026

Copy link
Copy Markdown
Member

Core, opt-in OAuth 2.0 primitives for the webapp launch-token flow, the WebappClient dedupe onto them, and auth-doc signposting. Foundation for migrating B-Fabric webapps to OAuth; the bfabric_asgi_auth adapter that consumes them is a stacked follow-up.

Primitives (additive; defaults preserve current behavior):

  • Bfabric.connect_oauth_token — client from an already-exchanged token (refresh-token grant), with an on_token_refresh callback.
  • OAuthCredentialProvider(on_token_update=…) — fires after each refresh; dropped on pickle (honors the picklability contract).
  • verify_jwt(audience=…, issuer=…) — opt-in aud/iss validation, off by default.
  • bfabric.experimental.webapp_oauthexchange_launch_token, OAuthClientCredentials, WebappOAuthSettings.

Dedupe: the RFC 8693 launch exchange now lives once in _oauth/launch_token.py; WebappClient.create and the public facade both consume it. _oauth no longer depends on experimental.

Docs: AGENTS.md gains an auth entry-point map and a doc-maintenance rule; oauth_integration.md drift fixed.

Why this stays draft: the OAuth exchange and aud/iss validation are not yet verified against a live B-Fabric server — aud/iss ship off by default with TODOs, to be confirmed on a test server before any consumer enables the OAuth path.

Testing: pytest tests/bfabric (587) and basedpyright(bfabric) green; no baseline edits.

🤖 Prepared with assistance from Claude Code.

Core, additive + opt-in primitives that are the foundation for migrating
B-Fabric webapps to OAuth (the bfabric_asgi_auth adapter and the rest_proxy
service-account migration both build on these). Harvested from the closed
PR #513 (Appendix A1-A5 of the asgi-auth OAuth design doc).

- OAuthCredentialProvider gains an optional on_token_update callback, fired
  after each token refresh; deliberately dropped on pickle (not picklable).
- verify_jwt gains opt-in audience/issuer validation; defaults preserve the
  previous behavior (aud/iss not validated).
- Bfabric.connect_oauth_token: build a client from an already-exchanged token
  via the refresh-token grant, with an on_token_refresh passthrough.
- bfabric.experimental.webapp_oauth.exchange_launch_token: RFC 8693 launch-JWT
  exchange + local verify/decode to UrlTokenContext (aud/iss TODOs left as the
  real-server merge gate for the later webapp-wiring PR).
- bfabric.experimental.webapp_oauth_settings: OAuthClientCredentials and
  WebappOAuthSettings config models.
@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

📝 "TODO" Changes Detected

Summary: ➕ 2 "TODO"s added

➕ Added "TODO"s (2)

  • bfabric/src/bfabric/_oauth/launch_token.py:39: # TODO(confirm iss): pass issuer once confirmed via {base_url}/rest/oauth/.well-known/openid-configuration.
  • bfabric/src/bfabric/_oauth/launch_token.py:38: # TODO(confirm aud): pass audience=client_id once the server's real aud claim is confirmed.

This comment is automatically updated when "TODO" changes are detected.

@leoschwarz
leoschwarz marked this pull request as draft June 25, 2026 15:03
@leoschwarz

Copy link
Copy Markdown
Member Author

Held as draft — do not merge yet.

On review, these primitives are premature and partly duplicative:

  • The whole webapp-OAuth layer is currently unconsumedWebappClient, connect_oauth, and these additions have no production callers; the actual webapp apps still use the legacy URL-token → ws-password path.
  • exchange_launch_token (A4) duplicates WebappClient.create's exchange + verify_jwt steps. connect_oauth_token (A3) fills a real user-only, rebuild-from-stored-token niche WebappClient can't — but that niche only exists once the asgi-auth OAuth adapter (Effort 1) exists, and it doesn't.

Plan: build these with Effort 1 (the bfabric_asgi_auth OAuth adapter), and at that point refactor WebappClient.create to call exchange_launch_token so there is one exchange path, not two. Branch kept as a ready starting point (tests already written).

Prompted by an agent (me) duplicating WebappClient's launch-token exchange
because nothing pointed at the existing auth surface and a migration *sketch*
was mistaken for current state.

- AGENTS.md: add an 'Authentication & OAuth' map naming the existing entry
  points, with WebappClient.create called out as the webapp launch-token
  entry point to reuse rather than reimplement; link the design doc and flag
  the asgi-auth migration doc as a plan, not current state.
- AGENTS.md: add a 'doc types & source of truth' note (design content lives in
  the human docs; plan/migration docs are not current behavior) and a
  metacognition rule to fix docs that misled you (near-duplicate capability,
  or a documented method that no longer exists).
- oauth_integration.md: fix drift (parse_url_token -> verify_jwt; remove the
  non-existent Bfabric.from_url_token row) and add a 'defer to code for exact
  signatures' note.
…ient)

WebappClient.create and exchange_launch_token both implemented the RFC 8693
launch-JWT exchange + verify_jwt decode. Move that composition into one place
(bfabric._oauth.launch_token.exchange_launch_token) and have both consume it:

- New _oauth/launch_token.py holds the canonical exchange_launch_token.
- experimental/webapp_oauth.py re-exports it (public path unchanged), so _oauth
  no longer depends on experimental.
- WebappClient.create now calls exchange_launch_token + Bfabric.connect_oauth_token
  instead of re-implementing the exchange and the user-client provider inline.

No behavior change; resolves the duplication flagged in review of #527.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant