Skip to content

Conversation

ChinmayBansal
Copy link
Contributor

Related Issues

Proposed Changes:

This PR implements multi-factor authentication (MFA) support for the Snowflake
integration to address Snowflake's enhanced security requirements that mandate
MFA for connections. The implementation adds non-interactive authentication
methods while maintaining full backward compatibility.

Key Features Added:

  • Key-pair JWT Authentication (SNOWFLAKE_JWT) that supports:
    • Pre-configured private key file paths via private_key_file parameter
    • Private key passphrases via private_key_file_pwd parameter
    • JWT-based authentication without user interaction prompts
  • OAuth 2.0 Authentication (OAUTH) supporting:
    • Client Credentials flow with oauth_client_id and oauth_client_secret
    • Configurable token request URLs via oauth_token_request_url
    • Authorization URLs via oauth_authorization_url for future extensibility
  • Enhanced Security Features:
    • Credential masking in URI logging via _create_masked_uri() method
    • Secure handling of all sensitive data using Haystack's Secret class
    • Comprehensive parameter validation with clear error messages
  • Non-interactive Design: All authentication credentials sourced from
    environment variables, eliminating user prompts as specifically requested
  • Backward Compatibility: Existing password-based authentication (SNOWFLAKE)
    remains unchanged with no breaking changes

How did you test it?

  • Unit Tests: Added comprehensive test suite with 11 new test cases (36 total
    tests passing)
    • Code Quality: hatch run fmt and hatch run test:types both pass
    • Security Testing: Verified credential masking in logs
    • Manual verification: Tested all authentication methods and parameter
      validation

Notes for the reviewer

  • The core authentication logic is in _snowflake_uri_constructor() method
    (lines ~257-301)
    • Parameter validation logic is centralized in _validate_auth_params() method
      (lines ~152-185)
    • Security enhancement via _create_masked_uri() method (lines ~303-329) ensures
      sensitive data never appears in logs
    • The implementation specifically addresses the "no user authentication
      prompts" requirement

Checklist

@ChinmayBansal ChinmayBansal requested a review from a team as a code owner September 23, 2025 17:48
@ChinmayBansal ChinmayBansal requested review from sjrl and removed request for a team September 23, 2025 17:48
@github-actions github-actions bot added integration:snowflake type:documentation Improvements or additions to documentation labels Sep 23, 2025
@sjrl sjrl requested a review from medsriha October 6, 2025 08:29
Copy link
Member

@medsriha medsriha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you so much for your effort and contribution, @ChinmayBansal! I left a few comments, but overall this looks great. Would you mind moving the authentication code to a separate file outside of the main component to help keep things tidy? Also, were you able to test JWT and OAuth with real credentials?

self.return_markdown = return_markdown

# Authentication parameters
self.authenticator = authenticator or "SNOWFLAKE"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a user doesn't specify authenticator but provides JWT credentials, it will default to "SNOWFLAKE" and then validation will require api_key. I suggest making authenticator required rather than optional.

return_markdown: bool = True,
authenticator: Optional[Literal["SNOWFLAKE", "SNOWFLAKE_JWT", "OAUTH"]] = None,
private_key_file: Optional[Union[str, Secret]] = None,
private_key_file_pwd: Optional[Union[str, Secret]] = None,
Copy link
Member

@medsriha medsriha Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sensitive credentials such as passwords and Secrets should always use Secrets and not combination of string and Secrets

private_key_file: Optional[Union[str, Secret]] = None,
private_key_file_pwd: Optional[Union[str, Secret]] = None,
oauth_client_id: Optional[Union[str, Secret]] = None,
oauth_client_secret: Optional[Union[str, Secret]] = None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

if value is None:
return None
if isinstance(value, Secret):
return value.resolve_value()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea to add an exception handling here

deserialize_secrets_inplace(init_params, secret_fields)
return default_from_dict(cls, data)

def _snowflake_uri_constructor(self) -> str:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happen if api_key, user, account etc have special charaters? Would it be a good idea if we encode the URI before sending it?


# Validate authentication parameters
self._validate_auth_params()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of adding a test_connection function during initialization? It allows us to verify the credentials are valid without executing an actual query at runtime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
integration:snowflake type:documentation Improvements or additions to documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

update: update Snowflake integration to support MFA
2 participants