Add native EmbeddedStreamlit helper to generate SiS embed URLs via OAuth token-exchange#2679
Draft
sfc-gh-aamadhavan wants to merge 1 commit into
Draft
Conversation
…uth token-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 api/streamlit/EmbeddedStreamlit.java, the internal token-exchange facade + response DTO, a dedicated ErrorCode, and 30 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 JDBC-driver helper,
EmbeddedStreamlit, that generates a Streamlit-in-Snowflake(SiS) embed URL by calling Snowflake's OAuth 2.0 token-exchange endpoint (
POST /oauth/token)with a service credential and the
session:streamlit:<fqn>scope, then assembling the final embedURL from the returned single-use authorization code. This is the native replacement for the
SYSTEM$STREAMLIT_GENERATE_EMBED_URLSQL system function, so a customer backend can mint an embedURL without an interactive SQL session.
Motivation
Customer backends (e.g.
analytics.bmw.com) embed SiS apps into 3rd-party pages and must mint ashort-lived embed URL using a service credential (PAT / key-pair / session token) without
opening a SQL session. This helper provides that as a credential-only side-channel utility.
Design docs: Streamlit Drivers
and Embedded Streamlit Authentication.
What changed
api/streamlit/EmbeddedStreamlit.java(new, public): builder with static factoryforApp(streamlitId, parentOrigin), credential modeswithPat/withConnection/withKeyPairJwtplus escape hatcheswithSubjectToken/withSubjectTokenType, endpoint configwithAccount/withHost/withTokenEndpoint, and a package-privatewithHttpSenderseam fortests. Holds the pure logic: scope build, credential→
subject_token_typemapping, endpointderivation, code extraction (fragment-first then query), and URL assembly. Terminal ops
getEmbedUrl()andgetExpiresInSeconds().internal/core/auth/oauth/StreamlitEmbedTokenResponseDTO.java(new): response DTO carryingredirect_uri+expires_in(the existingTokenResponseDTOrequiresaccess_tokenandcannot represent this response).
internal/core/auth/oauth/StreamlitEmbedTokenExchange.java(new, internal facade): builds +sends the urlencoded token-exchange POST behind an injectable
HttpSenderseam (default routesthrough
HttpUtil.executeGeneralRequestOmitSnowflakeHeaders), parses the DTO, and extracts thesession token/server URL from a live connection for
withConnection.api/exception/ErrorCode.java+jdbc_error_messages.properties: addedSTREAMLIT_EMBED_URL_ERROR(200075)=Error generating Streamlit embed URL: {0}, a cleansingle-placeholder template (mirrors the existing
OAUTH_*_FLOW_ERRORcodes) so the module nolonger overloads positional templates.
test/.../api/streamlit/EmbeddedStreamlitTest.java(new): 30 unit tests, HTTP layer mocked.Wire contract
POST https://<account>.snowflakecomputing.com/oauth/token— headersContent-Type: application/x-www-form-urlencoded; charset=UTF-8,Accept: application/json, andno
Authorization(the streamlit embed token-exchange 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 dots only, no:).subject_token_typemapping (verified againstSFOAuthTokenType.java): session →urn:snowflake:token-type:session; PAT →programmatic_access_token; key-pair JWT (provisional)→
urn:ietf:params:oauth:token-type:jwt. WIF (urn:snowflake:token-type:wif) and any future typeare reachable via
withSubjectToken(token, type).Response (200, JSON):
{ "redirect_uri": "...", "expires_in": <int> }; the single-use code may bea fragment (
#code=) or query (?code=/&code=).URL assembly (reproduces the system function): extract the code (fragment first, then query)
verbatim (no percent-decoding, so base64url codes survive); compute
base = scheme://host[:port]/pathofredirect_uriwith the code and any pre-existing__embeddedApp/__parentOriginremoved; buildbase + "?__parentOrigin=" + urlencode(parentOrigin) + "&__embeddedApp=true" + "#code=" + code(append with
&if base already has a query). The credential and code are treated as secrets andnever logged at info level, nor echoed into error messages.
Server dependency / open questions
EntityType.STREAMLITand thesession:streamlit:<fqn>scope handler atPOST /oauth/tokenbefore this works end-to-end.subject_token_typeis provisional — no GS enum value today; defaulturn:ietf:params:oauth:token-type:jwtis overridable viawithSubjectTokenType/withSubjectToken. The driver does not sign the JWT (caller supplies a pre-signed one;SessionUtilKeyPairis package-private)./oauth/tokenpath andredirect_uricode placement are assumptions from the verifiedcontract; the endpoint is overridable via
withTokenEndpoint.Testing
EmbeddedStreamlitTest— 30 JUnit 5 tests, HTTP mocked via an injectableHttpSender: eachcredential mode + correct
subject_token_type(PAT / key-pair-provisional / override /escape-hatch / session); scope; headers incl. the exact
Content-Typeand the absence ofan
Authorizationheader; code extraction from both fragment and query (incl. fragment precedenceand not-first-param); URL assembly from clean base, query-carried code, and base-with-query
(managed params stripped); endpoint derivation from account and scheme-less host; insecure-
http://warn branch;
withConnectionsession-token extraction via a mockedSnowflakeConnectionImpl/SFSessionplus non-Impl rejection;getExpiresInSeconds()throwing beforegetEmbedUrl();end-to-end URL +
expires_in; and error cases.Commands run (observed):
No full
mvn verify/packageor integration tests (heavy; the GS streamlit-scope endpoint is notin prod). The default production sender path (FAIL_OPEN +
executeGeneralRequestOmitSnowflakeHeaders,sfSession=null) is verified by reading (matches theWorkloadIdentityUtilpattern); it needs alive endpoint to exercise and will be validated when the GS endpoint lands.
Out of scope
SYSTEM$STREAMLIT_GENERATE_EMBED_URLSQL system function.🤖 Generated with Claude Code