Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

snowflake oauth connection #749

Merged
merged 6 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,11 @@ You can directly "Test connection" if needed.

### Snowflake

To be able to connect to a [Snowflake](https://www.snowflake.com/) instance, we'll need to define a Resource with the `Snowflake` Resource Type first.
To be able to connect to [Snowflake](https://www.snowflake.com/), you can choose to either setup [OAuth for Snowflake](/docs/misc/setup_oauth#oauth) or by defining a Snowflake Resource.

Head to the <a href="https://app.windmill.dev/resources" rel="nofollow">Resources</a> page in the Windmill app, click on
"Add a resource/API" in the top right corner and select the `Snowflake` type.
If a Snowflake OAuth connection is present, you can create a new Resource by heading to <a href="https://app.windmill.dev/resources" rel="nofollow">Resources</a>, clicking on "Add Resource" in the top right corner and selecting `snowflake_oauth`.

If you do not wish to use OAuth, click on "Add Resource" in the top right corner and select the `Snowflake` type instead.

![Select Snowflake Resource Type](./select_snowflake.png.webp)

Expand Down
33 changes: 33 additions & 0 deletions docs/misc/2_setup_oauth/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,39 @@ The same steps apply to enable more APIs (**Gmail**, **Gdrive**, etc.) on your G
- Copy the **Client ID** and **Client Secret**.
- [Instance settings](../../advanced/18_instance_settings/index.mdx) -> Resources -> Add OAuth "visma" and set the client id and client secret.

### Snowflake

- Go to [Snowflake](https://app.snowflake.com/) and open a new worksheet.
- Edit and run the following query to create an OAuth integration:
```sql
CREATE OR REPLACE SECURITY INTEGRATION <enter a name for the OAuth integration>
TYPE = OAUTH
OAUTH_CLIENT = CUSTOM
OAUTH_CLIENT_TYPE = 'CONFIDENTIAL'
OAUTH_REDIRECT_URI = 'https://<public url of your Windmill instance>/oauth/callback/snowflake_oauth'
OAUTH_USE_SECONDARY_ROLES = IMPLICIT
ENABLED = TRUE
COMMENT = '<enter a description of your security integration>'
```
- Retrieve the **client_id** and **client_secret** from the following query:
```sql
-- NOTE: replace <OAuth integration name> with the name of the OAuth integration you created above.
WITH oauth_data AS (
SELECT PARSE_JSON(SYSTEM$SHOW_OAUTH_CLIENT_SECRETS('<OAuth integration name>')) AS oauth_json
)
SELECT
oauth_json:OAUTH_CLIENT_ID::string AS client_id,
oauth_json:OAUTH_CLIENT_SECRET::string AS client_secret
FROM
oauth_data;
```
- Retrieve your [snowflake account identifier](https://docs.snowflake.com/en/user-guide/admin-account-identifier#finding-the-organization-and-account-name-for-an-account) from the URL of your snowflake account or from your account settings. You can also retrieve the account identifier by running this query:
```sql
SELECT CURRENT_ORGANIZATION_NAME() || '-' || CURRENT_ACCOUNT_NAME() AS org_account_formatted;
```

- [Instance settings](../../advanced/18_instance_settings/index.mdx) -> Resources -> Add OAuth "snowflake_oauth" and set the **client id**, **client secret** and **account identifier**.

### Custom OAuth

Under [Enterprise Edition](/pricing), you can add a completely custom OAuth without requiring a dev setup. The item accepts an extra optional field: `connect_config` or `login_config` of type OAuthConfig:
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/misc/9_guides/snowflake/hr_view.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
128 changes: 128 additions & 0 deletions docs/misc/9_guides/snowflake/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
---
title: Build an App accessing Snowflake with end-user Roles
---

import DocCard from '@site/src/components/DocCard';

# Build an App Accessing Snowflake with End-User Roles

This guide walks you through building an application that accesses Snowflake data based on the end-user’s role, using OAuth in Windmill. By leveraging dynamic role-based credentials from Snowflake’s OAuth integration, we avoid static credentials and enable secure data access customized for each user.

The tutorial includes steps to set up Snowflake OAuth, configure user roles, and create UI components in Windmill for a seamless, role-specific data experience.

Note that the mechanism of using the end-user's role demonstated here with Snowflake can be used for any [OAuth-supported resource in Windmill](/docs/misc/setup_oauth#oauth) such as GitHub, Slack, or Google Workspace.

<div className="grid grid-cols-2 gap-6 mb-4">
<DocCard
title="Resources and resource types"
description="Resources are structured configurations and connections to third-party systems, with Resource Types defining the schema for each Resource."
href="/docs/core_concepts/resources_and_types"
/>
<DocCard
title="Snowflake OAuth"
description="Setup a Snowflake OAuth connection"
href="/docs/misc/setup_oauth#snowflake"
target="_blank"
/>
</div>

---
## Video Tutorial

For a visual walkthrough of building this app, watch the tutorial below:

- 00:00 [Create a New Snowflake OAuth User Resource](https://www.youtube.com/watch?v=8Mk_4ErioeE&t=0s)
- 00:37 [Background Runnable to query Available Tables](https://www.youtube.com/watch?v=8Mk_4ErioeE&t=37s)
- 02:04 [Display Table Content](https://www.youtube.com/watch?v=8Mk_4ErioeE&t=124s)
- 02:47 [Test the App](https://www.youtube.com/watch?v=8Mk_4ErioeE&t=167s)

<iframe
style={{ aspectRatio: '16/9' }}
src="https://www.youtube.com/embed/8Mk_4ErioeE"
title="Small tutorial on how to build a Windmill App that uses Snowflake as a data source with OAuth roles and permissions"
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowFullScreen
className="border-2 rounded-lg object-cover w-full dark:border-gray-800"
></iframe>

## Prerequisites

1. **Set up Snowflake OAuth**: Follow the [Snowflake OAuth guide](/docs/misc/setup_oauth#snowflake).
2. **Configure User Roles in Snowflake**: In the [Snowflake console](https://app.snowflake.com/), ensure that user roles connected through OAuth have access to the relevant tables.

## Sample App Setup

For this example, we created a new Snowflake organization with a `WINDMILL` database, a `PUBLIC` schema, and two user roles:

- **hr_user** with the role `PRIVILEGED`
- **support_user** with the role `RESTRICTED`

The database contains the following tables:
- **SALARIES** – accessible only to the `PRIVILEGED` role
- **LIMITED_SALARIES** – accessible to both roles

The goal is to use OAuth to dynamically retrieve the credentials for the end-user connecting to the app rather than using static credentials.

### Step 1: Create a New Snowflake OAuth User Resource
1. After creating a new app in your Windmill workspace, add a **User Resource Input** component.
2. For "Resource Type," enter `snowflake_oauth`.
3. Enable **Express OAuth Setup** by toggling the option.
![Settings for User Resource Input](./user_resource_input.png)

4. In the UI editor, click the plus icon (+) to authenticate with your Snowflake account and test the connection.

This component allows the app to use end-user credentials via an interactive OAuth connection rather than relying on static resources defined in the workspace.

### Step 2: Background Runnable to Query Available Tables

Next, create a **Background Runnable** to retrieve the available tables based on the user’s Snowflake role.

1. Create a new **Background Runnable** of type "Snowflake."
![Settings for Background Runnable](./background_runnable_1.png)

2. Enter a Snowflake query to list available tables:

```sql
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'PUBLIC';
```

3. Connect the **Background Runnable** to the **User Resource Input** component from Step 1 by clicking the connect icon and selecting the result field of the **User Resource Input**.

4. Enable the toggle **resource from users allowed** to grant access to user-linked resources. Note that this resource is passed as a reference and won’t be accessible to the app publisher.

![Settings for Background Runnable](./background_runnable_2.png)

5. Click the **Run** button to test the query and view the results.

6. Create a **Select** component and connect it to the **Background Runnable** output to populate the dropdown menu. Map the output to `label` and `value` fields as follows:

```js
bg_0.result.map(_ => ({ value: _.TABLE_NAME, label: _.TABLE_NAME }))
```

### Step 3: Display Table Content

Now, add a **Database Studio Table** component to show the table content based on the selected table.

1. Create a **Database Studio Table** component.
2. Connect the **Database Studio Table**’s "resource" field to the **User Resource Input** component from Step 1.
3. Connect the **Database Studio Table**’s "table" field to the **Select** component created in Step 2.

![Settings for Database Studio](./database_studio.png)

4. The component will automatically populate with data from the selected table.

### Step 4: Test the App

Now, we’ll see how the displayed data changes based on the logged-in user’s role.

1. Click the **Preview** button to switch to the end-user preview mode.
2. Use the plus icon (+) to log in as a privileged user (e.g., **hr_user**) and view the content of the `SALARIES` table.

![View as HR user](./hr_view.png)

3. Log out by clicking the logout button next to the plus icon, then log in as the restricted user (e.g., **support_user**). You should now see only the `LIMITED_SALARIES` table.

![View as Support user](./support_view.png)

Binary file added docs/misc/9_guides/snowflake/support_view.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ const sidebars = {
label: 'AgGrid Table'
},
'misc/guides/table/index',
'misc/guides/sequin/index'
'misc/guides/sequin/index',
'misc/guides/snowflake/index'
]
}
]
Expand Down
Loading