Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion KERNEL_REV
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0d46716c466897148dfc1d2976ff03bdf097998c
eff8950428f4e6cc9975c663ec919f334962f7d0
4 changes: 4 additions & 0 deletions lib/contracts/IDBSQLClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ type AuthOptions =
| {
authType?: 'access-token';
token: string;
/** Kernel backend: selects mandatory SP-wide Workload Identity Federation. */
identityFederationClientId?: string;
}
| {
authType: 'databricks-oauth';
Expand All @@ -26,6 +28,8 @@ type AuthOptions =
// U2M flow to `['sql', 'offline_access']` (parity with the Thrift driver's
// `defaultOAuthScopes`), overriding the kernel's bare `all-apis offline_access`.
oauthScopes?: Array<string>;
/** Kernel backend: selects mandatory SP-wide Workload Identity Federation. */
identityFederationClientId?: string;
}
| {
authType: 'custom';
Expand Down
20 changes: 19 additions & 1 deletion lib/kernel/KernelAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ const DEFAULT_OAUTH_CLIENT_ID = 'databricks-sql-connector';
* everything else (client_id, scopes, callback timeout,
* token_url_override) uses kernel defaults.
*
* A non-empty `identityFederationClientId` selects mandatory SP-wide
* workload-identity token exchange for every auth mode.
*
* The `authMode` string literals MUST match the napi-emitted `AuthMode`
* variant names verbatim (`'Pat'`, `'OAuthM2m'`, `'OAuthU2m'` — napi-rs's
* `#[napi(string_enum)]` without an explicit case option emits the
Expand Down Expand Up @@ -212,10 +215,19 @@ export interface KernelProxyOptions {
};
}

export interface KernelFederationOptions {
/**
* SP-wide Workload Identity Federation client id. Omitted selects BYOT /
* account-wide WIF.
*/
identityFederationClientId?: string;
}

export type KernelNativeConnectionOptions = KernelSessionDefaults &
KernelTlsOptions &
KernelHttpOptions &
KernelProxyOptions &
KernelFederationOptions &
(
| {
hostName: string;
Expand Down Expand Up @@ -559,7 +571,8 @@ export function buildKernelConnectionOptions(options: ConnectionOptions): Kernel
maxConnections?: number;
} & KernelTlsOptions &
KernelHttpOptions &
KernelProxyOptions = {
KernelProxyOptions &
KernelFederationOptions = {
hostName: options.host,
httpPath: prependSlash(options.path),
// Match the NodeJS Thrift driver, which surfaces INTERVAL columns as
Expand All @@ -579,6 +592,11 @@ export function buildKernelConnectionOptions(options: ConnectionOptions): Kernel
...buildKernelProxyOptions(options),
};

const { identityFederationClientId } = options as { identityFederationClientId?: string };
if (identityFederationClientId) {
base.identityFederationClientId = identityFederationClientId;
}

// kernel-only pool sizing; read via cast to match how this function reads the
// other kernel-specific options (TLS) — they live on the internal options
// surface, not the published public `ConnectionOptions` `.d.ts`.
Expand Down
11 changes: 8 additions & 3 deletions native/kernel/index.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions tests/unit/kernel/auth-m2m.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ describe('KernelAuth + KernelBackend — OAuth M2M auth flow', () => {
});
});

it('forwards a federation client id on M2M auth', () => {
const native = buildKernelConnectionOptions({
host: 'example.cloud.databricks.com',
path: '/sql/1.0/warehouses/abc',
authType: 'databricks-oauth',
oauthClientId: 'client-uuid',
oauthClientSecret: 'dose-fake-secret',
identityFederationClientId: 'federation-client',
});

expect(native.identityFederationClientId).to.equal('federation-client');
});

it('defaults M2M oauthScopes to all-apis (Thrift + kernel parity)', () => {
const native = buildKernelConnectionOptions({
host: 'example.cloud.databricks.com',
Expand Down Expand Up @@ -190,6 +203,7 @@ describe('KernelAuth + KernelBackend — OAuth M2M auth flow', () => {
authType: 'databricks-oauth',
oauthClientId: 'client-uuid',
oauthClientSecret: 'dose-fake-secret',
identityFederationClientId: 'federation-client',
});

const session = await backend.openSession({});
Expand All @@ -207,6 +221,7 @@ describe('KernelAuth + KernelBackend — OAuth M2M auth flow', () => {
oauthClientId: 'client-uuid',
oauthClientSecret: 'dose-fake-secret',
oauthScopes: ['all-apis'],
identityFederationClientId: 'federation-client',
});

await session.close();
Expand Down
22 changes: 22 additions & 0 deletions tests/unit/kernel/auth-pat.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,28 @@ describe('KernelAuth — PAT auth options builder', () => {
}
});

it('forwards a federation client id on PAT auth', () => {
const native = buildKernelConnectionOptions({
host: 'example.cloud.databricks.com',
path: '/sql/1.0/warehouses/abc',
token: 'dapi-fake-pat',
identityFederationClientId: 'federation-client',
});

expect(native.identityFederationClientId).to.equal('federation-client');
});

it('omits an empty federation client id', () => {
const native = buildKernelConnectionOptions({
host: 'example.cloud.databricks.com',
path: '/sql/1.0/warehouses/abc',
token: 'dapi-fake-pat',
identityFederationClientId: '',
});

expect(native).not.to.have.property('identityFederationClientId');
});

it('prepends `/` to a path missing the leading slash', () => {
const opts: ConnectionOptions = {
host: 'example.cloud.databricks.com',
Expand Down
11 changes: 11 additions & 0 deletions tests/unit/kernel/auth-u2m.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,17 @@ describe('KernelAuth + KernelBackend — OAuth U2M auth flow', () => {
});
});

it('forwards a federation client id on U2M auth', () => {
const native = buildKernelConnectionOptions({
host: 'example.cloud.databricks.com',
path: '/sql/1.0/warehouses/abc',
authType: 'databricks-oauth',
identityFederationClientId: 'federation-client',
});

expect(native.identityFederationClientId).to.equal('federation-client');
});

it('defaults U2M oauthScopes to Thrift parity (sql offline_access)', () => {
const native = buildKernelConnectionOptions({
host: 'example.cloud.databricks.com',
Expand Down
Loading