Skip to content

Commit fcc6e98

Browse files
mlvogel1214Michael Vogel
andauthored
Key Pair Auth (#11)
Co-authored-by: Michael Vogel <[email protected]>
1 parent ab6de55 commit fcc6e98

File tree

4 files changed

+665
-39
lines changed

4 files changed

+665
-39
lines changed

README.md

Lines changed: 243 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,248 @@
11
# Snowflake DB Connector
22

3+
Database connector wrapper to work with Snowflake database from nodejs applications. Supports key pair authentication with private keys stored in files or AWS Secrets Manager.
34

4-
Wrapper utility to easily manage multiple data sources and pooled connections.
5+
## Installation
56

6-
Docs
7-
======================================================================
7+
```bash
8+
npm install @softrams/nodejs-snowflake-connector
9+
```
810

9-
For detailed documentation and basic usage examples, please see the documentation
10-
at `NodeJS Driver for Snowflake <https://docs.snowflake.com/en/user-guide/nodejs-driver-use.html>`_
11+
## Features
12+
13+
-**Key Pair Authentication** - Secure JWT-based authentication using RSA private keys
14+
-**AWS Secrets Manager Integration** - Store private keys securely in AWS Secrets Manager
15+
-**File-based Private Keys** - Load private keys from local files
16+
-**Connection Pooling** - Efficient connection management with configurable pool settings
17+
-**Multiple Data Sources** - Support for multiple Snowflake connections in one application
18+
-**Parameterized Queries** - Safe query execution with parameter binding
19+
20+
## Authentication Methods
21+
22+
This connector **only supports key pair authentication** using RSA private keys. Username/password authentication is not supported.
23+
24+
### Option A: Private Key from File Path
25+
```javascript
26+
const config = {
27+
DATASOURCES: {
28+
mySnowflakeDB: {
29+
DB_HOST: "your-account.snowflakecomputing.com",
30+
DB_USER: "your-username",
31+
PRIVATE_KEY_PATH: "/path/to/your/private-key.pem",
32+
PRIVATE_KEY_PASSPHRASE: "your-passphrase", // optional, only if private key is encrypted
33+
DB_DATABASE: "your-database",
34+
SCHEMA: "your-schema",
35+
WAREHOUSE: "your-warehouse", // optional
36+
ROLE: "your-role", // optional
37+
PORT: 443, // optional
38+
POOL_MAX: 10, // optional, default 10
39+
POOL_MIN: 0 // optional, default 0
40+
}
41+
}
42+
};
43+
```
44+
45+
### Option B: Private Key from AWS Secrets Manager
46+
```javascript
47+
const config = {
48+
DATASOURCES: {
49+
mySnowflakeDB: {
50+
DB_HOST: "your-account.snowflakecomputing.com",
51+
DB_USER: "your-username",
52+
PRIVATE_KEY_SECRET_NAME: "my-snowflake-private-key",
53+
PRIVATE_KEY_FIELD_NAME: "privateKey", // required - exact field name in the secret
54+
PRIVATE_KEY_PASSPHRASE: "your-passphrase", // optional, only if private key is encrypted
55+
DB_DATABASE: "your-database",
56+
SCHEMA: "your-schema",
57+
WAREHOUSE: "your-warehouse", // optional
58+
ROLE: "your-role", // optional
59+
PORT: 443, // optional
60+
POOL_MAX: 20, // optional, default 10
61+
POOL_MIN: 2 // optional, default 0
62+
}
63+
}
64+
};
65+
```
66+
67+
## Setting up Key Pair Authentication in Snowflake
68+
69+
1. **Generate RSA Key Pair:**
70+
```bash
71+
# Generate private key (2048-bit minimum)
72+
openssl genrsa -out rsa_key.pem 2048
73+
74+
# Generate public key
75+
openssl rsa -in rsa_key.pem -pubout -out rsa_key.pub
76+
77+
# Extract public key content (remove header/footer lines)
78+
openssl rsa -in rsa_key.pem -pubout -outform DER | base64 | tr -d '\n'
79+
```
80+
81+
2. **Configure Snowflake User:**
82+
```sql
83+
-- Set the public key for your user (use the base64 content from step 1)
84+
ALTER USER your_username SET RSA_PUBLIC_KEY='MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...';
85+
```
86+
87+
3. **For AWS Secrets Manager setup:**
88+
```json
89+
{
90+
"privateKey": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC...\n-----END PRIVATE KEY-----"
91+
}
92+
```
93+
94+
## Basic Usage
95+
96+
```javascript
97+
const snowflakeConnector = require('@softrams/nodejs-snowflake-connector');
98+
99+
async function main() {
100+
try {
101+
// Initialize with configuration
102+
await snowflakeConnector.init(config);
103+
104+
// Execute simple query
105+
const results = await snowflakeConnector.execute(
106+
'mySnowflakeDB',
107+
'SELECT CURRENT_VERSION() as version, CURRENT_USER() as user'
108+
);
109+
console.log('Connection info:', results);
110+
111+
// Execute parameterized query
112+
const userData = await snowflakeConnector.execute(
113+
'mySnowflakeDB',
114+
'SELECT * FROM users WHERE id = ? AND status = ?',
115+
[123, 'active']
116+
);
117+
console.log('User data:', userData);
118+
119+
} catch (error) {
120+
console.error('Database operation failed:', error);
121+
} finally {
122+
// Clean up connections
123+
await snowflakeConnector.closeAllPools();
124+
}
125+
}
126+
127+
main();
128+
```
129+
130+
## API Reference
131+
132+
### `init(config)`
133+
Initialize the connector with configuration.
134+
- **config**: Configuration object containing DATASOURCES
135+
136+
### `execute(dataSourceName, query, params?)`
137+
Execute a SQL query on the specified data source.
138+
- **dataSourceName**: Name of the configured data source
139+
- **query**: SQL query string with `?` placeholders for parameters
140+
- **params**: Array of parameter values (optional)
141+
- **Returns**: Promise<Array> - Query results
142+
143+
### `createSnowPool(poolName)`
144+
Create a connection pool for the specified data source.
145+
- **poolName**: Name of the data source
146+
- **Returns**: Promise<boolean> - Success status
147+
148+
### `connect(poolName)`
149+
Get a connection from the specified pool.
150+
- **poolName**: Name of the data source
151+
- **Returns**: Promise<Pool> - Connection pool
152+
153+
### `closePool(poolName)`
154+
Close a specific connection pool.
155+
- **poolName**: Name of the data source
156+
- **Returns**: Promise<boolean> - Success status
157+
158+
### `closeAllPools()`
159+
Close all connection pools.
160+
- **Returns**: Promise<boolean> - Success status
161+
162+
## Configuration Parameters
163+
164+
| Parameter | Required | Description |
165+
|-----------|----------|-------------|
166+
| `DB_HOST` | ✅ Yes | Snowflake account URL (e.g., `account.snowflakecomputing.com`) |
167+
| `DB_USER` | ✅ Yes | Snowflake username |
168+
| `DB_DATABASE` | ✅ Yes | Database name |
169+
| `SCHEMA` | ✅ Yes | Schema name |
170+
| `PRIVATE_KEY_PATH` | ⚠️ One Required | Path to private key file (mutually exclusive with SECRET_NAME) |
171+
| `PRIVATE_KEY_SECRET_NAME` | ⚠️ One Required | AWS Secrets Manager secret name (mutually exclusive with PATH) |
172+
| `PRIVATE_KEY_FIELD_NAME` | ⚠️ Required for AWS | Exact field name in the secret containing the private key |
173+
| `PRIVATE_KEY_PASSPHRASE` | ❌ Optional | Passphrase for encrypted private key |
174+
| `WAREHOUSE` | ❌ Optional | Warehouse name |
175+
| `ROLE` | ❌ Optional | Role name |
176+
| `PORT` | ❌ Optional | Port number (default: 443) |
177+
| `POOL_MAX` | ❌ Optional | Maximum pool connections (default: 10) |
178+
| `POOL_MIN` | ❌ Optional | Minimum pool connections (default: 0) |
179+
180+
## AWS Secrets Manager Setup
181+
182+
1. **Create a secret in AWS Secrets Manager:**
183+
```bash
184+
aws secretsmanager create-secret \
185+
--name "my-snowflake-private-key" \
186+
--description "Snowflake private key for authentication" \
187+
--secret-string '{"privateKey":"-----BEGIN PRIVATE KEY-----\nYOUR_PRIVATE_KEY_CONTENT\n-----END PRIVATE KEY-----"}'
188+
```
189+
190+
2. **Ensure your application has IAM permissions:**
191+
```json
192+
{
193+
"Version": "2012-10-17",
194+
"Statement": [
195+
{
196+
"Effect": "Allow",
197+
"Action": [
198+
"secretsmanager:GetSecretValue"
199+
],
200+
"Resource": "arn:aws:secretsmanager:region:account:secret:my-snowflake-private-key*"
201+
}
202+
]
203+
}
204+
```
205+
206+
3. **Configure AWS credentials** (one of the following):
207+
- Environment variables: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`
208+
- IAM roles (recommended for EC2/ECS/Lambda)
209+
- AWS credentials file (`~/.aws/credentials`)
210+
211+
## Error Handling
212+
213+
The connector provides detailed error messages for common configuration issues:
214+
215+
```javascript
216+
// Missing required configuration
217+
// Error: "Missing required configuration fields for myPool: DB_HOST, DB_USER"
218+
219+
// Authentication not configured
220+
// Error: "Authentication configuration missing for myPool. Must provide either PRIVATE_KEY_PATH or PRIVATE_KEY_SECRET_NAME"
221+
222+
// AWS Secrets Manager field not found
223+
// Error: "Private key not found in secret my-secret. Field 'wrongField' does not exist. Available fields: privateKey, otherField"
224+
225+
// Private key file not found
226+
// Error: "ENOENT: no such file or directory, open '/wrong/path/key.pem'"
227+
```
228+
229+
## Dependencies
230+
231+
- **snowflake-sdk**: Snowflake's official Node.js driver
232+
- **aws-sdk**: AWS SDK for Secrets Manager integration
233+
234+
## Requirements
235+
236+
- Node.js >= 14.0.0
237+
- Snowflake account with key pair authentication enabled
238+
- RSA private key (2048-bit minimum recommended)
239+
240+
## License
241+
242+
MIT
243+
244+
## Support
245+
246+
For issues and questions:
247+
- [GitHub Issues](https://github.com/softrams/nodejs-snowflake-connector/issues)
248+
- [Snowflake Documentation](https://docs.snowflake.com/en/user-guide/nodejs-driver-use.html)

0 commit comments

Comments
 (0)