Skip to content

Commit 624371e

Browse files
committed
Add token federation examples and public exports
This PR adds usage examples and exports token provider types for public use. This is the third of three PRs implementing token federation support. Examples added (examples/tokenFederation/): - staticToken.ts: Simple static token usage - externalToken.ts: Dynamic token from callback - federation.ts: Token federation with external IdP - m2mFederation.ts: Service principal federation with clientId - customTokenProvider.ts: Custom ITokenProvider implementation Public API exports: - Token: Token class with JWT handling - StaticTokenProvider: Static token provider - ExternalTokenProvider: Callback-based token provider - CachedTokenProvider: Caching decorator - FederationProvider: Token exchange decorator - ITokenProvider: Interface type (TypeScript) Also: - Updated tsconfig.build.json to exclude examples from build
1 parent 8279fa1 commit 624371e

22 files changed

Lines changed: 769 additions & 1 deletion
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* Example: Custom Token Provider Implementation
3+
*
4+
* This example demonstrates how to create a custom token provider by
5+
* implementing the ITokenProvider interface. This gives you full control
6+
* over token management, including custom caching, refresh logic, and
7+
* error handling.
8+
*/
9+
export {};

examples/tokenFederation/customTokenProvider.js

Lines changed: 123 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/tokenFederation/customTokenProvider.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/**
2+
* Example: Custom Token Provider Implementation
3+
*
4+
* This example demonstrates how to create a custom token provider by
5+
* implementing the ITokenProvider interface. This gives you full control
6+
* over token management, including custom caching, refresh logic, and
7+
* error handling.
8+
*/
9+
10+
import { DBSQLClient } from '@databricks/sql';
11+
import {
12+
ITokenProvider,
13+
Token,
14+
} from '../../lib/connection/auth/tokenProvider';
15+
16+
/**
17+
* Custom token provider that refreshes tokens from a custom OAuth server.
18+
*/
19+
class CustomOAuthTokenProvider implements ITokenProvider {
20+
private readonly oauthServerUrl: string;
21+
private readonly clientId: string;
22+
private readonly clientSecret: string;
23+
24+
constructor(oauthServerUrl: string, clientId: string, clientSecret: string) {
25+
this.oauthServerUrl = oauthServerUrl;
26+
this.clientId = clientId;
27+
this.clientSecret = clientSecret;
28+
}
29+
30+
async getToken(): Promise<Token> {
31+
console.log('Fetching token from custom OAuth server...');
32+
33+
// Example: Fetch token using client credentials grant
34+
const response = await fetch(`${this.oauthServerUrl}/oauth/token`, {
35+
method: 'POST',
36+
headers: {
37+
'Content-Type': 'application/x-www-form-urlencoded',
38+
},
39+
body: new URLSearchParams({
40+
grant_type: 'client_credentials',
41+
client_id: this.clientId,
42+
client_secret: this.clientSecret,
43+
scope: 'sql',
44+
}).toString(),
45+
});
46+
47+
if (!response.ok) {
48+
throw new Error(`OAuth token request failed: ${response.status}`);
49+
}
50+
51+
const data = await response.json() as {
52+
access_token: string;
53+
token_type?: string;
54+
expires_in?: number;
55+
};
56+
57+
// Calculate expiration
58+
let expiresAt: Date | undefined;
59+
if (typeof data.expires_in === 'number') {
60+
expiresAt = new Date(Date.now() + data.expires_in * 1000);
61+
}
62+
63+
return new Token(data.access_token, {
64+
tokenType: data.token_type ?? 'Bearer',
65+
expiresAt,
66+
});
67+
}
68+
69+
getName(): string {
70+
return 'CustomOAuthTokenProvider';
71+
}
72+
}
73+
74+
/**
75+
* Simple token provider that reads from a file (for development/testing).
76+
*/
77+
class FileTokenProvider implements ITokenProvider {
78+
private readonly filePath: string;
79+
80+
constructor(filePath: string) {
81+
this.filePath = filePath;
82+
}
83+
84+
async getToken(): Promise<Token> {
85+
const fs = await import('fs/promises');
86+
const tokenData = await fs.readFile(this.filePath, 'utf-8');
87+
const parsed = JSON.parse(tokenData);
88+
89+
return Token.fromJWT(parsed.access_token, {
90+
refreshToken: parsed.refresh_token,
91+
});
92+
}
93+
94+
getName(): string {
95+
return 'FileTokenProvider';
96+
}
97+
}
98+
99+
async function main() {
100+
const host = process.env.DATABRICKS_HOST!;
101+
const path = process.env.DATABRICKS_HTTP_PATH!;
102+
103+
const client = new DBSQLClient();
104+
105+
// Option 1: Use a custom OAuth token provider
106+
const oauthProvider = new CustomOAuthTokenProvider(
107+
process.env.OAUTH_SERVER_URL!,
108+
process.env.OAUTH_CLIENT_ID!,
109+
process.env.OAUTH_CLIENT_SECRET!,
110+
);
111+
112+
await client.connect({
113+
host,
114+
path,
115+
authType: 'token-provider',
116+
tokenProvider: oauthProvider,
117+
// Optionally enable federation if your OAuth server issues non-Databricks tokens
118+
enableTokenFederation: true,
119+
});
120+
121+
console.log('Connected successfully with custom token provider');
122+
123+
// Open a session and run a query
124+
const session = await client.openSession();
125+
const operation = await session.executeStatement('SELECT 1 AS result');
126+
const result = await operation.fetchAll();
127+
128+
console.log('Query result:', result);
129+
130+
await operation.close();
131+
await session.close();
132+
await client.close();
133+
}
134+
135+
main().catch(console.error);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* Example: Using an external token provider
3+
*
4+
* This example demonstrates how to use a callback function to provide
5+
* tokens dynamically. This is useful for integrating with secret managers,
6+
* vaults, or other token sources that may refresh tokens.
7+
*/
8+
export {};

examples/tokenFederation/externalToken.js

Lines changed: 45 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/tokenFederation/externalToken.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* Example: Using an external token provider
3+
*
4+
* This example demonstrates how to use a callback function to provide
5+
* tokens dynamically. This is useful for integrating with secret managers,
6+
* vaults, or other token sources that may refresh tokens.
7+
*/
8+
9+
import { DBSQLClient } from '@databricks/sql';
10+
11+
// Simulate fetching a token from a secret manager or vault
12+
async function fetchTokenFromVault(): Promise<string> {
13+
// In a real application, this would fetch from AWS Secrets Manager,
14+
// Azure Key Vault, HashiCorp Vault, or another secret manager
15+
console.log('Fetching token from vault...');
16+
17+
// Simulated token - replace with actual vault integration
18+
const token = process.env.DATABRICKS_TOKEN!;
19+
return token;
20+
}
21+
22+
async function main() {
23+
const host = process.env.DATABRICKS_HOST!;
24+
const path = process.env.DATABRICKS_HTTP_PATH!;
25+
26+
const client = new DBSQLClient();
27+
28+
// Connect using an external token provider
29+
// The callback will be called each time a new token is needed
30+
// Note: The token is automatically cached, so the callback won't be
31+
// called on every request
32+
await client.connect({
33+
host,
34+
path,
35+
authType: 'external-token',
36+
getToken: fetchTokenFromVault,
37+
});
38+
39+
console.log('Connected successfully with external token provider');
40+
41+
// Open a session and run a query
42+
const session = await client.openSession();
43+
const operation = await session.executeStatement('SELECT current_user() AS user');
44+
const result = await operation.fetchAll();
45+
46+
console.log('Query result:', result);
47+
48+
await operation.close();
49+
await session.close();
50+
await client.close();
51+
}
52+
53+
main().catch(console.error);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Example: Token Federation with an External Identity Provider
3+
*
4+
* This example demonstrates how to use token federation to automatically
5+
* exchange tokens from external identity providers (Azure AD, Google, Okta,
6+
* Auth0, AWS Cognito, GitHub) for Databricks-compatible tokens.
7+
*
8+
* Token federation uses RFC 8693 (OAuth 2.0 Token Exchange) to exchange
9+
* the external JWT token for a Databricks access token.
10+
*/
11+
export {};

0 commit comments

Comments
 (0)