Skip to content
API Reference
Guides
Access Data Sources

Access Snowflake

Control Snowflake access through Keycard using Okta groups, JWT authentication, and policy-based authorization for AI agents.

In this tutorial, you’ll set up end-to-end access control for Snowflake using Keycard. Your AI agents will query Snowflake through a service account, but Keycard governs who can access the Snowflake MCP based on Okta group membership, even if those users don’t have direct Snowflake accounts.

User / Agent
MCP token
MCP Server
Snowflake token
Snowflake
KeycardAuth via Okta
KeycardToken exchange

Snowflake has a single service user with multiple roles. Keycard acts as the policy layer: it grants different scopes based on Okta group membership, which determines what the user can do in Snowflake. Users don’t need individual Snowflake accounts; Keycard decides who gets through and with what permissions.

By the end of this tutorial, you’ll have:

  • Okta configured as an identity provider with a data-analysts group
  • Keycard policies that grant write access to analysts; everyone else gets read-only
  • Snowflake configured with read-only and read-write roles, accessed via a single service user
  • An MCP server that connects AI agents to Snowflake with permissions matching their Okta groups

Before starting, you’ll need:

  • A Keycard account (sign up free). After signing up, go to Zone Settings to find your zone ID (you’ll need this throughout the tutorial)
  • An Okta account with admin access, or sign up for the free Integrator plan
  • Claude Code installed
  • A Snowflake account with ACCOUNTADMIN role (required for creating security integrations)
  • Docker and Docker Compose installed
  • Clone the tutorial repository:
    Terminal window
    git clone https://github.com/keycardai/tutorial-snowflake-mcp.git
    cd tutorial-snowflake-mcp
    cp .env.example .env

To find your Snowflake account URL:

  1. Sign in to Snowflake
  2. Click your account name in the bottom-left corner
  3. Hover over your account and click the link icon to copy the account URL

The URL looks like https://abc12345.us-east-1.snowflakecomputing.com.

Open .env in your editor. You’ll fill in values as you progress through the tutorial, but start by adding these now:

Terminal window
# Keycard zone (from Zone Settings in Keycard Console)
KEYCARD_ZONE_URL=https://<ZONE-ID>.keycard.cloud
# Snowflake connection
SNOWFLAKE_ACCOUNT_URL=<YOUR-SNOWFLAKE-ACCOUNT-URL>
SNOWFLAKE_DATABASE=GREENDALE
SNOWFLAKE_WAREHOUSE=COMPUTE_WH

The remaining values (KEYCARD_CLIENT_ID and KEYCARD_CLIENT_SECRET) will be added in Part 2 when you create the application.

Part 1: Configure Okta as an Identity Provider

Section titled “Part 1: Configure Okta as an Identity Provider”

First, we’ll connect Okta to Keycard so users can authenticate with their existing corporate credentials.

  1. Create an OIDC application in Okta

    In Okta Admin Console, navigate to Applications > Applications and click Create App Integration.

    Select OIDC - OpenID Connect and Web Application, then click Next.

    Configure the application:

    FieldValue
    App integration nameKeycard
    Grant typeAuthorization Code, Refresh Token
    Sign-in redirect URIhttps://<ZONE-ID>.keycard.cloud/oauth/2/redirect
    Sign-out redirect URIhttps://<ZONE-ID>.keycard.cloud/openid/connect/redirect/logout
    Controlled accessAllow everyone in your organization to access

    Click Save.

  2. Configure Okta to send group claims

    While still on the Sign On tab, scroll to Advanced Settings and expand advanced options.

    Under Group Claims, click Edit and configure:

    FieldValue
    Groups claim typeFilter
    Groups claim filterName: groups, Filter: Matches regex, Value: .*

    This sends all group memberships in the ID token. For production, narrow the regex to only the groups Keycard needs.

  3. Create an Okta group for analysts

    In Okta Admin Console, navigate to Directory > Groups and create a group:

    Group NameDescription
    data-analystsFull read-write access to Snowflake data

    Add users who need write access to this group. Users not in data-analysts automatically get read-only access (no separate group needed).

  4. Add Okta as a provider in Keycard

    In Keycard Console, navigate to Providers and click Add Provider.

    FieldValue
    NameOkta
    Issuer URLhttps://<YOUR-OKTA-DOMAIN> (e.g., https://acme.okta.com)
    Client IDFrom step 1
    Client SecretFrom step 1

    Click Create Provider.

    After creating the provider, click into its settings. Under Advanced Settings, add groups to Additional Scopes. This ensures Keycard requests group claims from Okta during authentication.

  5. Enable Okta for zone sign-in

    In Keycard Console, navigate to Zone Settings and scroll to Zone sign in configuration.

    Toggle Use an external Identity Provider on and select Okta from the dropdown.

    Click Save Changes.

Users can now sign in to your Keycard zone using their Okta credentials.

Part 2: Configure Keycard Resources and Application

Section titled “Part 2: Configure Keycard Resources and Application”

Now we’ll register everything in Keycard: the Snowflake API as a resource, the MCP server as a resource, and an application that ties them together.

  1. Create the Snowflake API resource

    In Keycard Console, navigate to Resources and click Add Resource > Add Manually.

    FieldValue
    Resource NameSnowflake API
    Resource Identifier<YOUR-SNOWFLAKE-ACCOUNT-URL>
    Credential ProviderZone Provider

    This resource represents the Snowflake API that the MCP server will call on behalf of users.

  2. Create the Snowflake MCP resource

    Click Add Resource > Add Manually again, this time to register the MCP server that users will authenticate against.

    FieldValue
    Resource NameSnowflake MCP
    Resource Identifierhttp://localhost:3100/
    Credential ProviderZone Provider
  3. Create the Snowflake MCP application

    Navigate to Applications and click Add Application > Add Manually.

    FieldValue
    NameSnowflake MCP
    Identifierhttp://localhost:3100/

    Click Create Application.

  4. Configure the application resources

    On the application details page:

    Under Provides, click Add Provided Resource and select Snowflake MCP (the resource your application exposes).

    Under Dependency, click Add Dependency and select Snowflake API (the resource the application accesses on behalf of users).

  5. Generate client credentials

    On the application details page, go to the Application Credentials tab and click Add Credential. In the modal, select Client ID & Secret.

  6. Create an access policy

    Navigate to Policies > All Policies and click Create Policy.

    Name it Snowflake MCP, then switch to the Cedar tab and add the following policy:

    // Allow any user to access the MCP server
    permit (
    principal is Keycard::User,
    action,
    resource
    )
    when
    {
    (resource has identifier) &&
    ((resource.identifier) == "http://localhost:3100/")
    };
    // Allow any user to access Snowflake, unless requesting READWRITE_ROLE
    permit (
    principal is Keycard::User,
    action,
    resource
    )
    when
    {
    (((resource has identifier) && (context has scopes)) &&
    ((resource.identifier) == "<YOUR-SNOWFLAKE-ACCOUNT-URL>")) &&
    (!((context.scopes).containsAny(["session:role:READWRITE_ROLE"])))
    };
    // Allow data-analysts group members to access Snowflake with READWRITE_ROLE
    permit (
    principal is Keycard::User,
    action,
    resource
    )
    when
    {
    ((((((resource has identifier) && (context has scopes)) &&
    (context has subject_claims)) &&
    ((context.subject_claims) has groups)) &&
    ((resource.identifier) == "<YOUR-SNOWFLAKE-ACCOUNT-URL>")) &&
    ((context.scopes).containsAny(["session:role:READWRITE_ROLE"]))) &&
    (((context.subject_claims).groups).contains("data-analysts"))
    };

    Click Validate to check the policy syntax, then click Publish Policy to save it.

  7. Create and activate a policy set

    Go back to Policies (this lands you on Policy Sets) and click New Policy Set.

    Name it Snowflake Access and add two policies:

    • default-app-delegation (the preconfigured policy that enables token exchange)
    • Snowflake MCP (the policy you just created)

    Click Publish as Candidate to create a version. This lands you on the candidate page where you can click Activate to make it live.

Part 3: Configure Snowflake for Keycard JWTs

Section titled “Part 3: Configure Snowflake for Keycard JWTs”

Snowflake needs to trust Keycard as an OAuth authorization server. We’ll create a single service user for the MCP server. Keycard handles user identity and access control, so individual Snowflake accounts aren’t needed.

  1. Create the MCP service user

    In Snowflake, click + Create in the top-left and select SQL File.

    In the top-right of the worksheet, select ACCOUNTADMIN as your role and COMPUTE_WH as your warehouse.

    Run the following SQL to create a service user and two roles for the MCP server. Replace <YOUR-KEYCARD-APPLICATION-ID> with your Application ID from the Keycard Console application details page:

    BEGIN
    -- Create roles with different permission levels
    CREATE ROLE READONLY_ROLE;
    CREATE ROLE READWRITE_ROLE;
    -- Create service user and grant both roles
    CREATE USER MCP_SERVICE_USER
    TYPE = SERVICE
    LOGIN_NAME = '<YOUR-KEYCARD-APPLICATION-ID>'
    DEFAULT_ROLE = READONLY_ROLE
    COMMENT = 'Service account for Keycard MCP server';
    GRANT ROLE READONLY_ROLE TO USER MCP_SERVICE_USER;
    GRANT ROLE READWRITE_ROLE TO USER MCP_SERVICE_USER;
    END;
  2. Create an OAuth security integration

    Configure Snowflake to accept Keycard-issued JWTs:

    CREATE SECURITY INTEGRATION keycard_oauth
    TYPE = EXTERNAL_OAUTH
    ENABLED = TRUE
    EXTERNAL_OAUTH_TYPE = CUSTOM
    EXTERNAL_OAUTH_ISSUER = 'https://<ZONE-ID>.keycard.cloud'
    EXTERNAL_OAUTH_JWS_KEYS_URL = 'https://<ZONE-ID>.keycard.cloud/openidconnect/jwks'
    EXTERNAL_OAUTH_AUDIENCE_LIST = ('<YOUR-SNOWFLAKE-ACCOUNT-URL>')
    EXTERNAL_OAUTH_TOKEN_USER_MAPPING_CLAIM = 'client_id'
    EXTERNAL_OAUTH_SNOWFLAKE_USER_MAPPING_ATTRIBUTE = 'LOGIN_NAME'
    EXTERNAL_OAUTH_SCOPE_MAPPING_ATTRIBUTE = 'scope'
    EXTERNAL_OAUTH_SCOPE_DELIMITER = ' '
    EXTERNAL_OAUTH_ANY_ROLE_MODE = 'ENABLE';
  3. Seed sample data

    Create a database, schema, and sample data:

    BEGIN
    CREATE DATABASE GREENDALE;
    CREATE SCHEMA GREENDALE.ENROLLMENT;
    USE DATABASE GREENDALE;
    USE SCHEMA ENROLLMENT;
    CREATE TABLE STUDENTS (
    ID INT AUTOINCREMENT PRIMARY KEY,
    NAME VARCHAR(100),
    EMAIL VARCHAR(100),
    MAJOR VARCHAR(50),
    GPA DECIMAL(3,2),
    ENROLLED_DATE DATE
    );
    INSERT INTO STUDENTS (NAME, EMAIL, MAJOR, GPA, ENROLLED_DATE) VALUES
    ('Troy Barnes', 'troy.barnes@greendale.edu', 'Air Conditioning Repair', 3.2, '2009-09-01'),
    ('Abed Nadir', 'abed.nadir@greendale.edu', 'Film Studies', 3.8, '2009-09-01'),
    ('Jeff Winger', 'jeff.winger@greendale.edu', 'Undeclared', 2.9, '2009-09-01'),
    ('Britta Perry', 'britta.perry@greendale.edu', 'Psychology', 3.1, '2009-09-01'),
    ('Annie Edison', 'annie.edison@greendale.edu', 'Healthcare Administration', 4.0, '2009-09-01'),
    ('Shirley Bennett', 'shirley.bennett@greendale.edu', 'Business', 3.5, '2009-09-01'),
    ('Pierce Hawthorne', 'pierce.hawthorne@greendale.edu', 'Undeclared', 2.1, '2009-09-01');
    -- READONLY_ROLE: read-only access
    GRANT USAGE ON WAREHOUSE COMPUTE_WH TO ROLE READONLY_ROLE;
    GRANT USAGE ON DATABASE GREENDALE TO ROLE READONLY_ROLE;
    GRANT USAGE ON SCHEMA GREENDALE.ENROLLMENT TO ROLE READONLY_ROLE;
    GRANT SELECT ON ALL TABLES IN SCHEMA GREENDALE.ENROLLMENT TO ROLE READONLY_ROLE;
    -- READWRITE_ROLE: read-write access
    GRANT USAGE ON WAREHOUSE COMPUTE_WH TO ROLE READWRITE_ROLE;
    GRANT USAGE ON DATABASE GREENDALE TO ROLE READWRITE_ROLE;
    GRANT USAGE ON SCHEMA GREENDALE.ENROLLMENT TO ROLE READWRITE_ROLE;
    GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA GREENDALE.ENROLLMENT TO ROLE READWRITE_ROLE;
    END;

In the tutorial-snowflake-mcp directory you cloned during prerequisites, run the pre-built MCP server. The server uses the MCP service account you created, but Keycard validates each user’s identity and group membership before allowing access.

  1. Run with Docker

    Terminal window
    docker compose up

    The MCP server is now running at http://localhost:3100/mcp.

  2. Add the MCP server to Claude Code

    In a new terminal, add the Snowflake MCP server to Claude Code:

    Terminal window
    claude mcp add --transport http snowflake-mcp http://localhost:3100/mcp
  3. Authenticate with the MCP server

    Start Claude Code:

    Terminal window
    claude

    Type /mcp and select the snowflake-mcp server. This will open your browser and take you through the Keycard authentication flow (backed by Okta).

  4. Test read-only access

    Sign in as a user who is not in the data-analysts group.

    Ask Claude to read data:

    List all students at Greendale

    This should succeed: the user has READONLY_ROLE access.

    Now ask Claude to write data:

    Add a new student named Ben Chang with email ben.chang@greendale.edu, major Spanish, GPA 2.5

    This should fail: READONLY_ROLE doesn’t have INSERT permissions.

  5. Test read-write access

    Add yourself to the data-analysts group in Okta, then sign out of Keycard to refresh your session:

    https://<ZONE-ID>.keycard.cloud/logout

    In Claude Code, re-authenticate by typing /mcp, selecting snowflake-mcp, and completing the login flow again.

    Ask Claude to write data:

    Add a new student named Ben Chang with email ben.chang@greendale.edu, major Spanish, GPA 2.5

    This should succeed: the user has READWRITE_ROLE access with INSERT permissions.

    Verify the insert worked:

    Show me all students including Ben Chang

You now have Snowflake connected through Keycard with Okta-based identity and group-based policies. From here, you can:

  • Deploy to higher environments with workload identity federation to eliminate client secrets
  • Create per-operation roles (e.g., INSERT_ROLE, UPDATE_ROLE, DELETE_ROLE) for even more granular access control
  • Add more granular policies (e.g., restrict specific warehouses or databases)
  • Expand the MCP server with additional tools (schema inspection, write operations)
  • Set up audit log export to track all Snowflake access
  • Configure additional identity providers alongside Okta