@databricks/sdk-auth 0.0.0-dev → 0.1.0-dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +203 -0
- package/README.md +11 -1
- package/dist/auth.d.ts +81 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +47 -0
- package/dist/auth.js.map +1 -0
- package/dist/credentials/default/chain.d.ts +28 -0
- package/dist/credentials/default/chain.d.ts.map +1 -0
- package/dist/credentials/default/chain.js +62 -0
- package/dist/credentials/default/chain.js.map +1 -0
- package/dist/credentials/default/default-credentials.d.ts +25 -0
- package/dist/credentials/default/default-credentials.d.ts.map +1 -0
- package/dist/credentials/default/default-credentials.js +23 -0
- package/dist/credentials/default/default-credentials.js.map +1 -0
- package/dist/credentials/default/errors.d.ts +13 -0
- package/dist/credentials/default/errors.d.ts.map +1 -0
- package/dist/credentials/default/errors.js +15 -0
- package/dist/credentials/default/errors.js.map +1 -0
- package/dist/credentials/default/u2m-strategy.d.ts +9 -0
- package/dist/credentials/default/u2m-strategy.d.ts.map +1 -0
- package/dist/credentials/default/u2m-strategy.js +20 -0
- package/dist/credentials/default/u2m-strategy.js.map +1 -0
- package/dist/credentials/errors.d.ts +28 -0
- package/dist/credentials/errors.d.ts.map +1 -0
- package/dist/credentials/errors.js +32 -0
- package/dist/credentials/errors.js.map +1 -0
- package/dist/credentials/host-metadata.d.ts +45 -0
- package/dist/credentials/host-metadata.d.ts.map +1 -0
- package/dist/credentials/host-metadata.js +122 -0
- package/dist/credentials/host-metadata.js.map +1 -0
- package/dist/credentials/index.browser.d.ts +11 -0
- package/dist/credentials/index.browser.d.ts.map +1 -0
- package/dist/credentials/index.browser.js +9 -0
- package/dist/credentials/index.browser.js.map +1 -0
- package/dist/credentials/index.d.ts +14 -0
- package/dist/credentials/index.d.ts.map +1 -0
- package/dist/credentials/index.js +10 -0
- package/dist/credentials/index.js.map +1 -0
- package/dist/credentials/m2m.d.ts +40 -0
- package/dist/credentials/m2m.d.ts.map +1 -0
- package/dist/credentials/m2m.js +91 -0
- package/dist/credentials/m2m.js.map +1 -0
- package/dist/credentials/pat.d.ts +14 -0
- package/dist/credentials/pat.d.ts.map +1 -0
- package/dist/credentials/pat.js +41 -0
- package/dist/credentials/pat.js.map +1 -0
- package/dist/credentials/u2m.d.ts +31 -0
- package/dist/credentials/u2m.d.ts.map +1 -0
- package/dist/credentials/u2m.js +157 -0
- package/dist/credentials/u2m.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/oidc/env.d.ts +10 -0
- package/dist/oidc/env.d.ts.map +1 -0
- package/dist/oidc/env.js +19 -0
- package/dist/oidc/env.js.map +1 -0
- package/dist/oidc/file.d.ts +7 -0
- package/dist/oidc/file.d.ts.map +1 -0
- package/dist/oidc/file.js +28 -0
- package/dist/oidc/file.js.map +1 -0
- package/dist/oidc/index.browser.d.ts +13 -0
- package/dist/oidc/index.browser.d.ts.map +1 -0
- package/dist/oidc/index.browser.js +11 -0
- package/dist/oidc/index.browser.js.map +1 -0
- package/dist/oidc/index.d.ts +14 -0
- package/dist/oidc/index.d.ts.map +1 -0
- package/dist/oidc/index.js +12 -0
- package/dist/oidc/index.js.map +1 -0
- package/dist/oidc/oidc.d.ts +21 -0
- package/dist/oidc/oidc.d.ts.map +1 -0
- package/dist/oidc/oidc.js +10 -0
- package/dist/oidc/oidc.js.map +1 -0
- package/dist/oidc/tokensource.d.ts +56 -0
- package/dist/oidc/tokensource.d.ts.map +1 -0
- package/dist/oidc/tokensource.js +62 -0
- package/dist/oidc/tokensource.js.map +1 -0
- package/package.json +52 -4
- package/src/auth.ts +117 -0
- package/src/credentials/default/chain.ts +75 -0
- package/src/credentials/default/default-credentials.ts +40 -0
- package/src/credentials/default/errors.ts +18 -0
- package/src/credentials/default/u2m-strategy.ts +20 -0
- package/src/credentials/errors.ts +51 -0
- package/src/credentials/host-metadata.ts +166 -0
- package/src/credentials/index.browser.ts +11 -0
- package/src/credentials/index.ts +14 -0
- package/src/credentials/m2m.ts +156 -0
- package/src/credentials/pat.ts +44 -0
- package/src/credentials/u2m.ts +212 -0
- package/src/index.ts +19 -0
- package/src/oidc/env.ts +21 -0
- package/src/oidc/file.ts +29 -0
- package/src/oidc/index.browser.ts +16 -0
- package/src/oidc/index.ts +17 -0
- package/src/oidc/oidc.ts +26 -0
- package/src/oidc/tokensource.ts +133 -0
- package/index.js +0 -1
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Databricks CLI ("U2M") credentials. Obtains access tokens by shelling out
|
|
3
|
+
* to the `databricks` CLI binary (>= 0.100.0). The CLI must have been logged
|
|
4
|
+
* in ahead of time via `databricks auth login`.
|
|
5
|
+
*
|
|
6
|
+
* Node.js only. Not exported from the browser entry point.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {execFile} from 'node:child_process';
|
|
10
|
+
import {stat} from 'node:fs/promises';
|
|
11
|
+
import {join, sep} from 'node:path';
|
|
12
|
+
import {env, platform} from 'node:process';
|
|
13
|
+
import {promisify} from 'node:util';
|
|
14
|
+
|
|
15
|
+
import {z} from 'zod';
|
|
16
|
+
|
|
17
|
+
import type {Token, TokenCredentials} from '../auth';
|
|
18
|
+
import {newTokenCredentials, tokenProviderFn} from '../auth';
|
|
19
|
+
|
|
20
|
+
import {U2mCredentialsError} from './errors';
|
|
21
|
+
|
|
22
|
+
const execFileAsync = promisify(execFile);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Distinguishes the modern Go-based Databricks CLI (>= 0.100.0) from the
|
|
26
|
+
* legacy Python CLI by minimum file size.
|
|
27
|
+
*/
|
|
28
|
+
const DATABRICKS_CLI_MIN_SIZE = 1024 * 1024;
|
|
29
|
+
|
|
30
|
+
/** Options for the Databricks CLI auth strategy. */
|
|
31
|
+
export interface U2mCredentialsOptions {
|
|
32
|
+
/**
|
|
33
|
+
* The Databricks CLI profile name, as configured via `databricks auth
|
|
34
|
+
* login`.
|
|
35
|
+
*/
|
|
36
|
+
profile: string;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Path to the `databricks` CLI binary. If omitted, the binary is searched
|
|
40
|
+
* for in `PATH`.
|
|
41
|
+
*/
|
|
42
|
+
cliPath?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Creates a TokenCredentials that obtains Databricks access tokens by
|
|
47
|
+
* shelling out to the Databricks CLI.
|
|
48
|
+
*
|
|
49
|
+
* @param options - CLI profile (required) and optional CLI binary path.
|
|
50
|
+
* @throws U2mCredentialsError when `profile` is empty, or when the CLI cannot
|
|
51
|
+
* be located, is out of date, or fails to return a usable token.
|
|
52
|
+
*/
|
|
53
|
+
export function newU2mCredentials(
|
|
54
|
+
options: U2mCredentialsOptions
|
|
55
|
+
): TokenCredentials {
|
|
56
|
+
if (options.profile === '') {
|
|
57
|
+
throw new U2mCredentialsError('PROFILE_REQUIRED', 'profile is required');
|
|
58
|
+
}
|
|
59
|
+
const provider = tokenProviderFn(() => fetchCliToken(options));
|
|
60
|
+
return newTokenCredentials('databricks-cli', provider);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function fetchCliToken(options: U2mCredentialsOptions): Promise<Token> {
|
|
64
|
+
const cliPath = await findDatabricksCli(options.cliPath);
|
|
65
|
+
return execCliCommand([
|
|
66
|
+
cliPath,
|
|
67
|
+
'auth',
|
|
68
|
+
'token',
|
|
69
|
+
'--profile',
|
|
70
|
+
options.profile,
|
|
71
|
+
]);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const cliTokenResponseSchema = z.object({
|
|
75
|
+
access_token: z.string().min(1),
|
|
76
|
+
token_type: z.string().optional(),
|
|
77
|
+
expiry: z.string(),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
async function execCliCommand(args: string[]): Promise<Token> {
|
|
81
|
+
const [cliPath, ...rest] = args;
|
|
82
|
+
|
|
83
|
+
let stdout: string;
|
|
84
|
+
try {
|
|
85
|
+
const result = await execFileAsync(cliPath, rest);
|
|
86
|
+
stdout = result.stdout;
|
|
87
|
+
} catch (e) {
|
|
88
|
+
throw new U2mCredentialsError(
|
|
89
|
+
'TOKEN_FETCH_FAILED',
|
|
90
|
+
`cannot get access token: ${cliErrorMessage(e)}`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let raw: unknown;
|
|
95
|
+
try {
|
|
96
|
+
raw = JSON.parse(stdout);
|
|
97
|
+
} catch (e) {
|
|
98
|
+
const cause = e instanceof Error ? e.message : String(e);
|
|
99
|
+
throw new U2mCredentialsError(
|
|
100
|
+
'INVALID_RESPONSE',
|
|
101
|
+
`cannot parse CLI response: ${cause}`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const result = cliTokenResponseSchema.safeParse(raw);
|
|
106
|
+
if (!result.success) {
|
|
107
|
+
throw new U2mCredentialsError(
|
|
108
|
+
'INVALID_RESPONSE',
|
|
109
|
+
`invalid CLI response: ${result.error.message}`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
const parsed = result.data;
|
|
113
|
+
|
|
114
|
+
const expiry = new Date(parsed.expiry);
|
|
115
|
+
if (Number.isNaN(expiry.getTime())) {
|
|
116
|
+
throw new U2mCredentialsError(
|
|
117
|
+
'INVALID_RESPONSE',
|
|
118
|
+
`cannot parse token expiry: ${parsed.expiry}`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
value: parsed.access_token,
|
|
124
|
+
...(parsed.token_type !== undefined && {type: parsed.token_type}),
|
|
125
|
+
expiry,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
interface ExecFileError {
|
|
130
|
+
stderr?: string | Buffer;
|
|
131
|
+
message: string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function cliErrorMessage(e: unknown): string {
|
|
135
|
+
if (typeof e === 'object' && e !== null) {
|
|
136
|
+
const err = e as ExecFileError;
|
|
137
|
+
if (err.stderr !== undefined) {
|
|
138
|
+
return err.stderr.toString().trim();
|
|
139
|
+
}
|
|
140
|
+
return err.message;
|
|
141
|
+
}
|
|
142
|
+
return String(e);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Locates the `databricks` CLI binary, either at `cliPath` (if provided) or
|
|
147
|
+
* by searching `PATH`. Validates that the binary is the modern Go CLI and
|
|
148
|
+
* not the legacy Python one, via a minimum-size check.
|
|
149
|
+
*/
|
|
150
|
+
async function findDatabricksCli(cliPath?: string): Promise<string> {
|
|
151
|
+
if (cliPath !== undefined) {
|
|
152
|
+
if (cliPath.includes(sep) || cliPath.includes('/')) {
|
|
153
|
+
return validateCliPath(cliPath);
|
|
154
|
+
}
|
|
155
|
+
return findInPath(cliPath);
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
return await findInPath('databricks');
|
|
159
|
+
} catch (e) {
|
|
160
|
+
if (platform === 'win32') {
|
|
161
|
+
return findInPath('databricks.exe');
|
|
162
|
+
}
|
|
163
|
+
throw e;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function findInPath(name: string): Promise<string> {
|
|
168
|
+
const pathEnv = env.PATH ?? '';
|
|
169
|
+
if (pathEnv === '') {
|
|
170
|
+
throw new U2mCredentialsError('CLI_NOT_FOUND', 'databricks CLI not found');
|
|
171
|
+
}
|
|
172
|
+
const delim = platform === 'win32' ? ';' : ':';
|
|
173
|
+
let legacyError: U2mCredentialsError | undefined;
|
|
174
|
+
for (const dir of pathEnv.split(delim)) {
|
|
175
|
+
if (dir === '') {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
return await validateCliPath(join(dir, name));
|
|
180
|
+
} catch (e) {
|
|
181
|
+
if (
|
|
182
|
+
e instanceof U2mCredentialsError &&
|
|
183
|
+
e.code === 'LEGACY_CLI_DETECTED'
|
|
184
|
+
) {
|
|
185
|
+
legacyError = e;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
throw (
|
|
190
|
+
legacyError ??
|
|
191
|
+
new U2mCredentialsError('CLI_NOT_FOUND', 'databricks CLI not found')
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function validateCliPath(path: string): Promise<string> {
|
|
196
|
+
let info;
|
|
197
|
+
try {
|
|
198
|
+
info = await stat(path);
|
|
199
|
+
} catch {
|
|
200
|
+
throw new U2mCredentialsError('CLI_NOT_FOUND', 'databricks CLI not found');
|
|
201
|
+
}
|
|
202
|
+
if (info.isDirectory()) {
|
|
203
|
+
throw new U2mCredentialsError('CLI_NOT_FOUND', 'databricks CLI not found');
|
|
204
|
+
}
|
|
205
|
+
if (info.size < DATABRICKS_CLI_MIN_SIZE) {
|
|
206
|
+
throw new U2mCredentialsError(
|
|
207
|
+
'LEGACY_CLI_DETECTED',
|
|
208
|
+
'legacy databricks CLI detected; upgrade to >= 0.100.0'
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
return path;
|
|
212
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Databricks authentication library for JavaScript/TypeScript.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import pkgJson from '../package.json' with {type: 'json'};
|
|
8
|
+
|
|
9
|
+
export type {
|
|
10
|
+
Header,
|
|
11
|
+
Token,
|
|
12
|
+
Credentials,
|
|
13
|
+
TokenProvider,
|
|
14
|
+
TokenCredentials,
|
|
15
|
+
} from './auth';
|
|
16
|
+
export {newTokenCredentials, tokenProviderFn} from './auth';
|
|
17
|
+
|
|
18
|
+
/** Version of this auth library, sourced from package.json. */
|
|
19
|
+
export const VERSION: string = pkgJson.version;
|
package/src/oidc/env.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {env} from 'node:process';
|
|
2
|
+
|
|
3
|
+
import type {IdTokenProvider} from './oidc';
|
|
4
|
+
import {idTokenProviderFn} from './oidc';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Returns an IdTokenProvider that reads the ID token from environment variable
|
|
8
|
+
* `name`.
|
|
9
|
+
*
|
|
10
|
+
* Note that the IdTokenProvider does not cache the token and will read the
|
|
11
|
+
* token from environment variable `name` each time.
|
|
12
|
+
*/
|
|
13
|
+
export function newEnvIdTokenProvider(name: string): IdTokenProvider {
|
|
14
|
+
return idTokenProviderFn(() => {
|
|
15
|
+
const t = env[name];
|
|
16
|
+
if (t === undefined || t === '') {
|
|
17
|
+
return Promise.reject(new Error(`missing env var "${name}"`));
|
|
18
|
+
}
|
|
19
|
+
return Promise.resolve({value: t});
|
|
20
|
+
});
|
|
21
|
+
}
|
package/src/oidc/file.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {readFile} from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
import type {IdTokenProvider} from './oidc';
|
|
4
|
+
import {idTokenProviderFn} from './oidc';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Returns an IdTokenProvider that reads the ID token from a file. The file
|
|
8
|
+
* should contain a single line with the token.
|
|
9
|
+
*/
|
|
10
|
+
export function newFileTokenProvider(path: string): IdTokenProvider {
|
|
11
|
+
return idTokenProviderFn(async () => {
|
|
12
|
+
if (path === '') {
|
|
13
|
+
throw new Error('missing path');
|
|
14
|
+
}
|
|
15
|
+
let content: string;
|
|
16
|
+
try {
|
|
17
|
+
content = await readFile(path, 'utf-8');
|
|
18
|
+
} catch (e: unknown) {
|
|
19
|
+
if (e instanceof Error && 'code' in e && e.code === 'ENOENT') {
|
|
20
|
+
throw new Error(`file "${path}" does not exist`);
|
|
21
|
+
}
|
|
22
|
+
throw e;
|
|
23
|
+
}
|
|
24
|
+
if (content.length === 0) {
|
|
25
|
+
throw new Error(`file "${path}" is empty`);
|
|
26
|
+
}
|
|
27
|
+
return {value: content};
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser entry point for OIDC ID token utilities and the Databricks OIDC
|
|
3
|
+
* token-exchange provider.
|
|
4
|
+
*
|
|
5
|
+
* This package is experimental and subject to change.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type {IdToken, IdTokenProvider} from './oidc';
|
|
11
|
+
export {idTokenProviderFn} from './oidc';
|
|
12
|
+
export type {
|
|
13
|
+
DatabricksOidcTokenProviderConfig,
|
|
14
|
+
OAuthAuthorizationServer,
|
|
15
|
+
} from './tokensource';
|
|
16
|
+
export {newDatabricksOidcTokenProvider} from './tokensource';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OIDC ID token utilities and Databricks OIDC token-exchange provider.
|
|
3
|
+
*
|
|
4
|
+
* This package is experimental and subject to change.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type {IdToken, IdTokenProvider} from './oidc';
|
|
10
|
+
export {idTokenProviderFn} from './oidc';
|
|
11
|
+
export {newEnvIdTokenProvider} from './env';
|
|
12
|
+
export {newFileTokenProvider} from './file';
|
|
13
|
+
export type {
|
|
14
|
+
DatabricksOidcTokenProviderConfig,
|
|
15
|
+
OAuthAuthorizationServer,
|
|
16
|
+
} from './tokensource';
|
|
17
|
+
export {newDatabricksOidcTokenProvider} from './tokensource';
|
package/src/oidc/oidc.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IdToken represents an OIDC ID token that can be exchanged for a Databricks
|
|
3
|
+
* access token.
|
|
4
|
+
*/
|
|
5
|
+
export interface IdToken {
|
|
6
|
+
value: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* IdTokenProvider is anything that returns an IdToken given an audience.
|
|
11
|
+
*/
|
|
12
|
+
export interface IdTokenProvider {
|
|
13
|
+
idToken(audience: string): Promise<IdToken>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Adapter to allow the use of ordinary functions as IdTokenProvider.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* const provider = idTokenProviderFn(async () => ({ value: 'my-id-token' }));
|
|
21
|
+
*/
|
|
22
|
+
export function idTokenProviderFn(
|
|
23
|
+
fn: (audience: string) => Promise<IdToken>
|
|
24
|
+
): IdTokenProvider {
|
|
25
|
+
return {idToken: fn};
|
|
26
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Databricks OIDC token-exchange provider. Exchanges an OIDC ID token for a
|
|
3
|
+
* Databricks access token using the OAuth 2.0 token-exchange grant.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {z} from 'zod';
|
|
7
|
+
|
|
8
|
+
import type {Token, TokenProvider} from '../auth';
|
|
9
|
+
import {tokenProviderFn} from '../auth';
|
|
10
|
+
|
|
11
|
+
import type {IdTokenProvider} from './oidc';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* OAuthAuthorizationServer describes the OAuth endpoints used to mint
|
|
15
|
+
* Databricks access tokens.
|
|
16
|
+
*/
|
|
17
|
+
export interface OAuthAuthorizationServer {
|
|
18
|
+
tokenEndpoint: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* DatabricksOidcTokenProviderConfig is the configuration for a Databricks OIDC
|
|
23
|
+
* TokenProvider.
|
|
24
|
+
*/
|
|
25
|
+
export interface DatabricksOidcTokenProviderConfig {
|
|
26
|
+
/**
|
|
27
|
+
* Client ID of the Databricks OIDC application. It corresponds to the
|
|
28
|
+
* Application ID of the Databricks Service Principal.
|
|
29
|
+
*
|
|
30
|
+
* This field is only required for Workload Identity Federation and should
|
|
31
|
+
* be empty for Account-wide token federation.
|
|
32
|
+
*/
|
|
33
|
+
clientId?: string;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Account ID of the Databricks Account. This field is only required for
|
|
37
|
+
* Account-wide token federation.
|
|
38
|
+
*/
|
|
39
|
+
accountId?: string;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Host is the host of the Databricks account or workspace.
|
|
43
|
+
*/
|
|
44
|
+
host: string;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* TokenEndpointProvider returns the token endpoint for the Databricks OIDC
|
|
48
|
+
* application.
|
|
49
|
+
*/
|
|
50
|
+
tokenEndpointProvider: () => Promise<OAuthAuthorizationServer>;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Audience is the audience of the Databricks OIDC application.
|
|
54
|
+
* This is only used for Workspace level tokens.
|
|
55
|
+
*/
|
|
56
|
+
audience?: string;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* IdTokenProvider returns the ID token to be used for the token exchange.
|
|
60
|
+
*/
|
|
61
|
+
idTokenProvider: IdTokenProvider;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Returns a new Databricks OIDC TokenProvider that exchanges an OIDC ID token
|
|
66
|
+
* for a Databricks access token using the OAuth 2.0 token-exchange grant.
|
|
67
|
+
*/
|
|
68
|
+
export function newDatabricksOidcTokenProvider(
|
|
69
|
+
config: DatabricksOidcTokenProviderConfig
|
|
70
|
+
): TokenProvider {
|
|
71
|
+
return tokenProviderFn(() => exchangeIdToken(config));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function exchangeIdToken(
|
|
75
|
+
config: DatabricksOidcTokenProviderConfig
|
|
76
|
+
): Promise<Token> {
|
|
77
|
+
if (config.host === '') {
|
|
78
|
+
throw new Error('missing Host');
|
|
79
|
+
}
|
|
80
|
+
const endpoints = await config.tokenEndpointProvider();
|
|
81
|
+
const audience = determineAudience(config, endpoints);
|
|
82
|
+
const idToken = await config.idTokenProvider.idToken(audience);
|
|
83
|
+
|
|
84
|
+
const params = new URLSearchParams();
|
|
85
|
+
if (config.clientId !== undefined && config.clientId !== '') {
|
|
86
|
+
params.set('client_id', config.clientId);
|
|
87
|
+
}
|
|
88
|
+
params.set('scope', 'all-apis');
|
|
89
|
+
params.set('subject_token_type', 'urn:ietf:params:oauth:token-type:jwt');
|
|
90
|
+
params.set('subject_token', idToken.value);
|
|
91
|
+
params.set('grant_type', 'urn:ietf:params:oauth:grant-type:token-exchange');
|
|
92
|
+
|
|
93
|
+
const response = await fetch(endpoints.tokenEndpoint, {
|
|
94
|
+
method: 'POST',
|
|
95
|
+
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
|
96
|
+
body: params.toString(),
|
|
97
|
+
});
|
|
98
|
+
if (!response.ok) {
|
|
99
|
+
const text = await response.text();
|
|
100
|
+
throw new Error(
|
|
101
|
+
`token request failed with status ${response.status.toString()}: ${text}`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
const parsed = tokenResponseSchema.parse(await response.json());
|
|
105
|
+
const expiry =
|
|
106
|
+
parsed.expires_in !== undefined
|
|
107
|
+
? new Date(Date.now() + parsed.expires_in * 1000)
|
|
108
|
+
: undefined;
|
|
109
|
+
return {
|
|
110
|
+
value: parsed.access_token,
|
|
111
|
+
...(parsed.token_type !== undefined && {type: parsed.token_type}),
|
|
112
|
+
...(expiry !== undefined && {expiry}),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function determineAudience(
|
|
117
|
+
config: DatabricksOidcTokenProviderConfig,
|
|
118
|
+
endpoints: OAuthAuthorizationServer
|
|
119
|
+
): string {
|
|
120
|
+
if (config.audience !== undefined && config.audience !== '') {
|
|
121
|
+
return config.audience;
|
|
122
|
+
}
|
|
123
|
+
if (config.accountId !== undefined && config.accountId !== '') {
|
|
124
|
+
return config.accountId;
|
|
125
|
+
}
|
|
126
|
+
return endpoints.tokenEndpoint;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const tokenResponseSchema = z.object({
|
|
130
|
+
access_token: z.string(),
|
|
131
|
+
token_type: z.string().optional(),
|
|
132
|
+
expires_in: z.number().optional(),
|
|
133
|
+
});
|
package/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
module.exports = {};
|