hyperdb-api-salesforce
Salesforce Data Cloud OAuth authentication for Hyper database.
This crate implements the two-stage OAuth token flow for connecting to Salesforce Data Cloud via gRPC. It supports the JWT Bearer Token Flow, username-password flow, and refresh token flow, with automatic token caching and refresh.
Overview
hyperdb-api-salesforce is a companion crate that provides Salesforce-specific
authentication, separate from the core hyperdb-api and hyperdb-api-core::client crates.
- Three OAuth flows -- JWT Bearer Token (recommended), username-password, refresh token
- Automatic token refresh -- Proactive refresh before expiration, reactive on auth errors
- Thread-safe --
SharedTokenProviderfor concurrent access viaArc<Mutex<>> - Secure memory -- Private keys and secrets stored with
zeroize - gRPC integration -- Works with
AuthenticatedGrpcClientfromhyperdb-api-core::client
Quick Start (JWT Bearer Token)
use ;
let private_key_pem = read_to_string?;
let config = new?
.auth_mode;
let mut provider = new?;
let token = provider.get_token.await?;
println!;
println!;
Authentication Flows
JWT Bearer Token (Recommended)
Best for server-to-server authentication. Does not require a client secret.
let config = new?
.auth_mode;
| Parameter | Description |
|---|---|
login_url |
Salesforce login URL (e.g., https://login.salesforce.com) |
client_id |
External Client App Consumer Key |
username |
Salesforce username that authorized the app |
private_key_pem |
RSA private key in PKCS#8 PEM format |
Username-Password
Requires client_secret. The password may need a security token suffix.
let config = new?
.client_secret
.auth_mode;
Refresh Token
Uses a long-lived OAuth refresh token. Requires client_secret.
let config = new?
.client_secret
.auth_mode;
Token Management
DataCloudTokenProvider caches both the OAuth Access Token and the DC JWT
independently. The OAuth Access Token (~2-hour lifetime) is only refreshed when
genuinely expired, avoiding unnecessary refresh token rotation. The DC JWT is
refreshed proactively based on both expiry and age. get_token() returns a
cached token when valid, or transparently performs the full flow when needed.
SharedTokenProvider
For concurrent applications, wrap the provider in SharedTokenProvider:
use SharedTokenProvider;
let provider = new?;
// Clone and share across tasks
let provider_clone = provider.clone;
spawn;
SharedTokenProvider uses Arc<Mutex<DataCloudTokenProvider>> internally.
All token operations are serialized to prevent concurrent refresh races.
Usage with AuthenticatedGrpcClient
Enable the salesforce-auth feature on hyperdb-api-core::client:
[]
= { = "0.1", = ["salesforce-auth"] }
= "0.1"
AuthenticatedGrpcClient handles token refresh automatically, including
reconnection with new tokens:
use ;
use AuthenticatedGrpcClient;
let private_key_pem = read_to_string?;
let auth_config = new?
.auth_mode;
let token_provider = new?;
let mut client = connect.await?;
// Token refresh is handled automatically, even for long-running queries
let result = client.execute_query.await?;
// Built-in catalog operations
let tables = client.list_tables.await?;
Refresh strategy:
- Proactive -- Refreshes the DC JWT before expiration (5-minute buffer) and when it exceeds a maximum age (15 minutes by default)
- Reactive -- On gRPC auth errors (UNAUTHENTICATED), refreshes and retries once
Setup Guide
Step 1: Generate RSA Key Pair
# Generate a 2048-bit RSA private key
# Create a self-signed certificate (valid 1 year)
# Convert to PKCS#8 format (required by this crate)
certificate.crt-- Upload to Salesforce External Client Appprivate.key-- Use in your application (keep secure)
Step 2: Create External Client App in Salesforce
- Setup > External Client App Manager > New External Client App
- Flow Type: App acts on behalf of a user
- Add OAuth scopes:
- Access the identity URL service (
id,profile,email,address,phone) - Manage user data via APIs (
api) - Perform requests at any time (
refresh_token,offline_access) - Perform ANSI SQL queries on Data Cloud data (
cdp_query_api) -- required
- Access the identity URL service (
- Callback URL:
https://localhost:1717/OauthRedirect - Enable JWT Bearer Flow and upload
certificate.crt
Step 3: Pre-authorize Users
- External Client App Manager > Your app > Policies tab
- Set Permitted Users to Admin approved users are pre-authorized
- Manage Profiles > Add profiles that should have access
Step 4: Get Consumer Key
- External Client App Manager > Your app > OAuth Settings
- Click Manage Consumer Details (verify identity)
- Copy the Consumer Key (this is the
client_id)
Environment Variables
The included example reads configuration from environment variables:
# optional
Configuration Options
| Builder method | Default | Description |
|---|---|---|
.client_secret(s) |
None |
Required for Password and RefreshToken modes |
.dataspace(s) |
None |
Data Cloud dataspace name |
.timeout_secs(n) |
30 |
HTTP request timeout |
.max_retries(n) |
3 |
Max retries for transient (5xx) failures |
Login URLs: https://login.salesforce.com (production),
https://test.salesforce.com (sandbox), or
https://mydomain.my.salesforce.com (custom domain).
Troubleshooting
| Error | Cause and Solution |
|---|---|
invalid_grant: user hasn't approved this consumer |
Pre-authorize the user (Step 3 above) |
invalid_client_id |
Verify Consumer Key matches exactly |
Private key error: failed to parse private key |
Convert to PKCS#8: openssl pkcs8 -topk8 -nocrypt -in keypair.key -out private.key |
invalid_grant: authentication failure |
Check username, login URL, and certificate match |
DC JWT exchange failed |
Verify Data Cloud license and cdp_query_api scope |
client_secret is required for Password and RefreshToken auth modes |
Add .client_secret(...) to your config |
License
Apache-2.0 — see LICENSE-APACHE.