Add native EmbeddedStreamlit helper to generate SiS embed URLs via OAuth token-exchange#2913
Draft
sfc-gh-aamadhavan wants to merge 1 commit into
Draft
Conversation
…en-exchange Native, session-free replacement for the SYSTEM$STREAMLIT_GENERATE_EMBED_URL SQL system function: exchanges a service credential (PAT / key-pair / session) at POST /oauth/token for a single-use embed authorization code and assembles the Streamlit embed URL. DRAFT - depends on the GS server-side session:streamlit:<fqn> token-exchange endpoint (not yet in prod). Adds embedded_streamlit.py, the __all__ export, and 29 unit tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
I have read the CLA Document and I hereby sign the CLA You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot. |
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.
Summary
Adds a native
EmbeddedStreamlithelper to the Python connector that generates aStreamlit-in-Snowflake (SiS) embed URL by calling Snowflake's OAuth 2.0 token-exchange
endpoint (
POST /oauth/token) with a service credential and assembling the final embedURL. This is the native, session-free replacement for the
SYSTEM$STREAMLIT_GENERATE_EMBED_URLSQL system function.Motivation
Customer backends (e.g.
analytics.bmw.com) embed SiS apps into 3rd-party pages and mustmint a short-lived, single-use embed URL from a service credential (PAT / key-pair /
session) without opening an interactive SQL session to call the system function. This
helper performs the token-exchange directly from the driver and never exposes the service
credential to the browser.
Design docs: Streamlit Drivers
and Embedded Streamlit Authentication.
What changed
src/snowflake/connector/embedded_streamlit.py(new):class EmbeddedStreamlit.src/snowflake/connector/__init__.py: exportEmbeddedStreamlit(added to__all__).test/unit/test_embedded_streamlit.py(new): 29 unit tests, HTTP layer fully mocked.prepare()takes exactly one primary credential source (pat|conn|key_pair|explicit
subject_token), plusaccount/host/useras needed for credential-only modes,and an overridable
token_endpoint/session_managerfor tests. HTTP goes through theconnector's
SessionManager; the key-pair JWT is minted viaAuthByKeyPair.prepare(account=, user=)(or a pre-signed JWT may be passed);
connmode pulls the session token and host from the liveSnowflakeConnection(conn.rest.token,conn.host).Wire contract
POST https://<host>/oauth/token—Content-Type: application/x-www-form-urlencoded; charset=UTF-8,Accept: application/json, and crucially noAuthorizationheader (the streamlit embedtoken-exchange path has no OAuth client identity). Body:
grant_type=urn:ietf:params:oauth:grant-type:token-exchange,subject_token=<credential>,subject_token_type=<URN>,scope=session:streamlit:<db.schema.app>(FQN contains no:).subject_token_typemapping (verified against GSSFOAuthTokenType.java):subject_token_typeurn:snowflake:token-type:sessionprogrammatic_access_tokenurn:snowflake:token-type:wifurn:ietf:params:oauth:token-type:access_tokenurn:ietf:params:oauth:token-type:jwt(provisional, overridable)Response
200 { "redirect_uri": "...", "expires_in": <int> }; the single-use code may be afragment (
#code=) or query (?code=/&code=).URL assembly reproduces
StreamlitGenerateEmbedUrl.java: extract the code (fragment first),compute the base (
scheme://host[:port]/path) minus the code and any echoed__embeddedApp/__parentOrigin, then buildbase + "?__parentOrigin=" + urlencode(parentOrigin) + "&__embeddedApp=true" + "#code=" + code(joining with
&when the base already has a query). The authorization code is readverbatim (raw split on
code=), not form-decoded — the GS system function emits it viaURIBuilder.setFragment("code=" + azCode)with no percent-encoding, and base64url codes cancontain
+///=; form-decoding would turn+into a space and corrupt the single-use code.The credential and the code are treated as secrets and never logged.
Server dependency / open questions
EntityType.STREAMLITand thesession:streamlit:<fqn>scope handler to thetoken-exchange path before this works against a real account (not in prod yet).
subject_token_typeis provisional — there is no GS enum value for akey-pair JWT today; the default
urn:ietf:params:oauth:token-type:jwtis overridable, andan explicit
(subject_token, subject_token_type)escape hatch is provided./oauth/tokenpath and that the single-use azcode is delivered viaredirect_uri(fragment or query) un-percent-encoded — the driver reads the code verbatim,matching the system function's fragment output.
Testing
test/unit/test_embedded_streamlit.py— 29 tests, HTTP mocked via an injected fakeSessionManager: each credential mode +subject_token_type; scope string; endpoint, headers,and absence of any Authorization header; default-endpoint-from-account; code extraction from
fragment, query, and
&code=with fragment precedence; regression tests that a code with+///=round-trips verbatim from both fragment and query; URL assembly (parentOriginurl-encoding,
__embeddedApp=true, base-with-query, stripping echoed managed params, evil-originnegative check);
expires_incoercion; and the full error matrix.Commands run (observed):
Run with
PYTHONPATH=src(package not pip-installed here); the benignFailed to import ArrowResultwarning is unrelated (Arrow C-ext not compiled). No live integration/e2e — the GSendpoint is not in production yet.
Out of scope
SYSTEM$STREAMLIT_GENERATE_EMBED_URLSQL system function.🤖 Generated with Claude Code