feat: support Snowflake OAuth for Posit Team Native Apps#321
Draft
kfeinauer wants to merge 1 commit into
Draft
Conversation
Add Snowflake as a headless-auth IdP and a pluggable HTTP client-auth seam so VIP can verify Posit Team deployed as a Snowflake Native App. - idp.py: `_fill_snowflake_login` drives Snowflake's OAuth sign-in form and walks the multi-hop chain (product-host SPCS ingress, then the controller host that acts as the products' OIDC IdP), filling each sign-in form and clicking the optional "Allow" consent until the flow leaves Snowflake. Registered as idp "snowflake". - client_auth.py: a generic registry (`register_client_auth` / `build_client_auth`) + an injectable `httpx.Auth` on the product clients, so a downstream extension can authenticate the HTTP clients with a non-static scheme (e.g. a Snowflake JWT) without VIP depending on Snowflake. Client fixtures skip the Connect API-key requirement when such a provider is registered. - cli.py / vip.toml.example: document idp "snowflake". Tests: selftests/test_idp.py, selftests/test_client_auth.py.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds Snowflake Native App support by (1) introducing a Snowflake-specific headless browser IdP automation strategy for Snowflake OAuth redirects, and (2) adding a pluggable httpx.Auth registry so product API clients can use non-static/per-host authorization schemes (while keeping VIP Snowflake-agnostic).
Changes:
- Add
idp="snowflake"Playwright form-fill strategy that can handle multi-hop Snowflake OAuth and optional consent. - Introduce
vip.client_authregistry and thread an optionalhttpx.AuththroughBaseClientand product clients, with fixtures consulting the registry. - Update CLI/config examples and add selftests for the new IdP strategy + auth registry.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
vip.toml.example |
Documents idp="snowflake" usage for Snowflake Native App deployments. |
src/vip/idp.py |
Adds Snowflake OAuth headless login strategy and registers it under supported IdPs. |
src/vip/clients/base.py |
Adds optional httpx.Auth support to the shared client base. |
src/vip/clients/connect.py |
Threads optional httpx.Auth into Connect client construction. |
src/vip/clients/workbench.py |
Threads optional httpx.Auth into Workbench client construction. |
src/vip/clients/packagemanager.py |
Threads optional httpx.Auth into Package Manager client construction. |
src/vip/client_auth.py |
New module: registry + builder for IdP-keyed httpx.Auth factories. |
src/vip/cli.py |
Updates --idp help text to include "snowflake". |
src/vip/auth.py |
Expands supported IdP messaging and includes "snowflake" in docs/errors. |
src/vip_tests/conftest.py |
Injects registry-provided httpx.Auth into product clients; relaxes Connect API-key requirement when auth is provided. |
selftests/test_idp.py |
Adds tests covering Snowflake IdP strategy dispatch and looping/consent behavior. |
selftests/test_client_auth.py |
Adds tests for registry behavior and BaseClient auth injection. |
Comments suppressed due to low confidence (1)
src/vip/clients/base.py:58
- BaseClient's docstring says
authtakes precedence overauth_header_value, but the implementation always sets theAuthorizationheader whenauth_header_valueis non-empty, even if a customhttpx.Authis provided. This can result in sending the static API key/token alongside (or instead of) the custom auth scheme, which is especially problematic for per-host schemes like Snowflake ingress auth.
headers: dict[str, str] = {}
if auth_header_value:
headers["Authorization"] = auth_header_value
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Identity provider for --headless-auth: "keycloak", "okta", "snowflake" | ||
| # Only required when provider is "oidc", "saml", or "oauth2". | ||
| # For Snowflake Native App deployments, use provider = "oauth2" and | ||
| # idp = "snowflake". |
|
Preview Links
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the auth plumbing VIP needs to verify Posit Team deployed as a Snowflake Native App, in two prongs.
1. Browser auth — Snowflake IdP strategy
idp.pygains_fill_snowflake_login, registered asidp = "snowflake". Posit Team products delegate OIDC to the deployment's controller, which itself sits behind the Snowpark Container Services ingress — so reaching a product bounces through Snowflake OAuth more than once (product-host ingress, then controller host). The strategy loops, filling each sign-in form in the chain (waiting for each hop to settle) and clicking the optional Allow consent, until the flow leaves Snowflake.2. HTTP client auth — pluggable seam
Snowflake's SPCS ingress expects a per-host
Authorization: Snowflake Token="…"(a key-pair JWT exchange), which a static API key can't express. Rather than teach VIP about Snowflake, this adds a small generic seam:client_auth.py:register_client_auth(idp, factory)/build_client_auth(...).auth: httpx.AuthonBaseClientand the product clients.A downstream extension (in
partnership-snowflake) registers a Snowflakehttpx.Authagainstidp="snowflake"; VIP stays Snowflake-agnostic.Validated
Live against a Snowflake posit-team deployment: headless double-bounce login succeeds; Workbench API/security/health tests pass via the JWT prong; the full Workbench UI subset passes (run serially — high xdist concurrency contends on session launches, unrelated to auth).
Tests
selftests/test_idp.py,selftests/test_client_auth.py; full selftest suite green, ruff + mypy clean. No new dependencies.