@dbx-tools/auth 0.9.31 → 0.9.36
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/README.md +49 -97
- package/index.ts +8 -26
- package/lib/index.d.ts +8 -26
- package/lib/index.js +6 -22
- package/lib/src/{databricks-cli.d.ts → _databricks-cli.d.ts} +3 -7
- package/lib/src/_databricks-cli.js +172 -0
- package/lib/src/_errors.d.ts +5 -0
- package/lib/src/_errors.js +10 -0
- package/lib/src/{lifecycle.d.ts → _lifecycle.d.ts} +5 -5
- package/lib/src/_lifecycle.js +214 -0
- package/lib/src/_logging.d.ts +1 -1
- package/lib/src/_logging.js +2 -2
- package/lib/src/{personal-access-token.d.ts → _personal-access-token.d.ts} +1 -1
- package/lib/src/_personal-access-token.js +30 -0
- package/lib/src/_profile-config.d.ts +43 -0
- package/lib/src/_profile-config.js +443 -0
- package/lib/src/{service-principal.d.ts → _service-principal.d.ts} +1 -1
- package/lib/src/_service-principal.js +69 -0
- package/lib/src/{storage.d.ts → _storage.d.ts} +4 -6
- package/lib/src/_storage.js +94 -0
- package/lib/src/_types.d.ts +40 -0
- package/lib/src/_types.js +2 -0
- package/lib/src/client.d.ts +30 -0
- package/lib/src/client.js +223 -0
- package/lib/src/config.d.ts +36 -0
- package/lib/src/config.js +15 -0
- package/lib/src/profile.d.ts +11 -0
- package/lib/src/profile.js +21 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +34 -10
- package/src/{databricks-cli.ts → _databricks-cli.ts} +20 -109
- package/src/_errors.ts +11 -0
- package/src/{lifecycle.ts → _lifecycle.ts} +24 -48
- package/src/_logging.ts +2 -2
- package/src/{personal-access-token.ts → _personal-access-token.ts} +1 -1
- package/src/_profile-config.ts +545 -0
- package/src/{service-principal.ts → _service-principal.ts} +2 -2
- package/src/_storage.ts +109 -0
- package/src/_types.ts +45 -0
- package/src/client.ts +291 -0
- package/src/config.ts +44 -0
- package/src/profile.ts +33 -0
- package/lib/exports.d.ts +0 -2
- package/lib/exports.js +0 -3
- package/lib/src/_profile-core.d.ts +0 -28
- package/lib/src/_profile-core.js +0 -216
- package/lib/src/_profile.d.ts +0 -15
- package/lib/src/_profile.js +0 -236
- package/lib/src/databricks-auth.d.ts +0 -11
- package/lib/src/databricks-auth.js +0 -314
- package/lib/src/databricks-cli.js +0 -239
- package/lib/src/errors.d.ts +0 -7
- package/lib/src/errors.js +0 -14
- package/lib/src/generated/databricks-cli-assets.json +0 -33
- package/lib/src/http-client.d.ts +0 -23
- package/lib/src/http-client.js +0 -116
- package/lib/src/lifecycle.js +0 -229
- package/lib/src/node-storage.d.ts +0 -20
- package/lib/src/node-storage.js +0 -207
- package/lib/src/personal-access-token.js +0 -30
- package/lib/src/service-principal.js +0 -69
- package/lib/src/storage.js +0 -98
- package/lib/src/types.d.ts +0 -157
- package/lib/src/types.js +0 -67
- package/src/_profile-core.ts +0 -258
- package/src/_profile.ts +0 -319
- package/src/databricks-auth.ts +0 -416
- package/src/errors.ts +0 -16
- package/src/generated/databricks-cli-assets.json +0 -33
- package/src/http-client.ts +0 -147
- package/src/node-storage.ts +0 -230
- package/src/storage.ts +0 -104
- package/src/types.ts +0 -188
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dbx-tools/auth",
|
|
3
|
-
"description": "
|
|
3
|
+
"description": "Databricks profile resolution and token or authentication-header production for Node.js and Bun",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/reggie-db/dbx-tools.git",
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
"projen": "projen"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
|
-
"@types/bun": "1.3.14",
|
|
23
22
|
"@types/ini": "^4.1.1",
|
|
24
23
|
"@types/node": "^24.6.0",
|
|
25
24
|
"typescript": "^5.9.3"
|
|
26
25
|
},
|
|
27
26
|
"dependencies": {
|
|
28
|
-
"@dbx-tools/core": "^0.9.
|
|
29
|
-
"@dbx-tools/shared-
|
|
27
|
+
"@dbx-tools/core": "^0.9.36",
|
|
28
|
+
"@dbx-tools/shared-auth": "^0.9.36",
|
|
29
|
+
"@dbx-tools/shared-core": "^0.9.36",
|
|
30
30
|
"ini": "^6.0.0",
|
|
31
31
|
"oauth4webapi": "^3.8.8"
|
|
32
32
|
},
|
|
@@ -41,17 +41,46 @@
|
|
|
41
41
|
"types": "./lib/index.d.ts",
|
|
42
42
|
"default": "./lib/index.js"
|
|
43
43
|
},
|
|
44
|
+
"./client": {
|
|
45
|
+
"types": "./lib/src/client.d.ts",
|
|
46
|
+
"default": "./lib/src/client.js"
|
|
47
|
+
},
|
|
48
|
+
"./config": {
|
|
49
|
+
"types": "./lib/src/config.d.ts",
|
|
50
|
+
"default": "./lib/src/config.js"
|
|
51
|
+
},
|
|
52
|
+
"./profile": {
|
|
53
|
+
"types": "./lib/src/profile.d.ts",
|
|
54
|
+
"default": "./lib/src/profile.js"
|
|
55
|
+
},
|
|
44
56
|
"./package.json": "./package.json"
|
|
45
57
|
}
|
|
46
58
|
},
|
|
47
|
-
"version": "0.9.
|
|
59
|
+
"version": "0.9.36",
|
|
48
60
|
"types": "./lib/index.d.ts",
|
|
61
|
+
"dbxToolsConfig": {
|
|
62
|
+
"tags": [
|
|
63
|
+
"node"
|
|
64
|
+
]
|
|
65
|
+
},
|
|
49
66
|
"type": "module",
|
|
50
67
|
"exports": {
|
|
51
68
|
".": {
|
|
52
69
|
"types": "./lib/index.d.ts",
|
|
53
70
|
"default": "./lib/index.js"
|
|
54
71
|
},
|
|
72
|
+
"./client": {
|
|
73
|
+
"types": "./lib/src/client.d.ts",
|
|
74
|
+
"default": "./lib/src/client.js"
|
|
75
|
+
},
|
|
76
|
+
"./config": {
|
|
77
|
+
"types": "./lib/src/config.d.ts",
|
|
78
|
+
"default": "./lib/src/config.js"
|
|
79
|
+
},
|
|
80
|
+
"./profile": {
|
|
81
|
+
"types": "./lib/src/profile.d.ts",
|
|
82
|
+
"default": "./lib/src/profile.js"
|
|
83
|
+
},
|
|
55
84
|
"./package.json": "./package.json"
|
|
56
85
|
},
|
|
57
86
|
"files": [
|
|
@@ -59,10 +88,5 @@
|
|
|
59
88
|
"src",
|
|
60
89
|
"lib"
|
|
61
90
|
],
|
|
62
|
-
"dbxToolsConfig": {
|
|
63
|
-
"tags": [
|
|
64
|
-
"node"
|
|
65
|
-
]
|
|
66
|
-
},
|
|
67
91
|
"//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"bunx projen\"."
|
|
68
92
|
}
|
|
@@ -1,60 +1,34 @@
|
|
|
1
|
-
import { homedir } from "node:os";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
|
|
4
|
-
import * as bin from "@dbx-tools/core/bin";
|
|
5
1
|
import * as exec from "@dbx-tools/core/exec";
|
|
6
2
|
import * as stringUtils from "@dbx-tools/shared-core/string-utils";
|
|
7
3
|
|
|
8
|
-
import
|
|
4
|
+
import { AuthError } from "./_errors.ts";
|
|
9
5
|
import { authLogger, failureMetadata, tokenMetadata } from "./_logging.ts";
|
|
10
|
-
import {
|
|
11
|
-
import type { Token, TokenProvider } from "./types.ts";
|
|
6
|
+
import type { Token, TokenProvider } from "./_types.ts";
|
|
12
7
|
|
|
13
8
|
const resolutionCache = new Map<string, Promise<string | undefined>>();
|
|
14
9
|
const logger = authLogger("cli");
|
|
15
10
|
|
|
16
|
-
interface DatabricksCliAsset {
|
|
17
|
-
readonly url: string;
|
|
18
|
-
readonly sha256: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
11
|
/** Deferred Databricks CLI lookup used by U2M token acquisition. */
|
|
22
12
|
export type DatabricksCliResolver = () => Promise<string | undefined>;
|
|
23
13
|
|
|
24
|
-
/**
|
|
25
|
-
export interface DatabricksCliResolutionOptions {
|
|
26
|
-
install?: boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Resolve a compatible Databricks CLI, installing the pinned build asset when needed. */
|
|
14
|
+
/** Resolve an available Databricks CLI without installing executables. */
|
|
30
15
|
export function resolveDatabricksCli(
|
|
31
16
|
environment: Readonly<Record<string, string | undefined>> = process.env,
|
|
32
|
-
options: DatabricksCliResolutionOptions = {},
|
|
33
17
|
): Promise<string | undefined> {
|
|
34
18
|
const candidate = stringValue(environment.DATABRICKS_CLI_PATH) ?? "databricks";
|
|
35
|
-
const
|
|
36
|
-
const cached = resolutionCache.get(key);
|
|
19
|
+
const cached = resolutionCache.get(candidate);
|
|
37
20
|
if (cached) {
|
|
38
|
-
logger.debug("reused Databricks CLI resolution", {
|
|
39
|
-
candidate,
|
|
40
|
-
install: options.install !== false,
|
|
41
|
-
});
|
|
21
|
+
logger.debug("reused Databricks CLI resolution", { candidate });
|
|
42
22
|
return cached;
|
|
43
23
|
}
|
|
44
|
-
logger.debug("resolving Databricks CLI", {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
minimumVersion: cliAssets.minimumVersion,
|
|
50
|
-
});
|
|
51
|
-
const resolved = resolveDatabricksCliUncached(candidate, options.install !== false).catch(
|
|
52
|
-
(error) => {
|
|
53
|
-
resolutionCache.delete(key);
|
|
24
|
+
logger.debug("resolving Databricks CLI", { candidate });
|
|
25
|
+
const resolved = compatibleDatabricksCli(candidate)
|
|
26
|
+
.then((compatible) => (compatible ? candidate : undefined))
|
|
27
|
+
.catch((error) => {
|
|
28
|
+
resolutionCache.delete(candidate);
|
|
54
29
|
throw error;
|
|
55
|
-
}
|
|
56
|
-
);
|
|
57
|
-
resolutionCache.set(key, resolved);
|
|
30
|
+
});
|
|
31
|
+
resolutionCache.set(candidate, resolved);
|
|
58
32
|
return resolved;
|
|
59
33
|
}
|
|
60
34
|
|
|
@@ -64,59 +38,6 @@ export function resetDatabricksCliResolution(): void {
|
|
|
64
38
|
logger.debug("cleared Databricks CLI resolution cache");
|
|
65
39
|
}
|
|
66
40
|
|
|
67
|
-
async function resolveDatabricksCliUncached(
|
|
68
|
-
candidate: string,
|
|
69
|
-
install: boolean,
|
|
70
|
-
): Promise<string | undefined> {
|
|
71
|
-
if (await compatibleDatabricksCli(candidate)) {
|
|
72
|
-
logger.debug("selected installed Databricks CLI", { executable: candidate });
|
|
73
|
-
return candidate;
|
|
74
|
-
}
|
|
75
|
-
const managed = managedExecutable();
|
|
76
|
-
if (await compatibleDatabricksCli(managed)) {
|
|
77
|
-
logger.debug("selected managed Databricks CLI", { executable: managed });
|
|
78
|
-
return managed;
|
|
79
|
-
}
|
|
80
|
-
if (!install) {
|
|
81
|
-
logger.debug("compatible Databricks CLI unavailable and installation disabled");
|
|
82
|
-
return undefined;
|
|
83
|
-
}
|
|
84
|
-
const asset = platformAsset();
|
|
85
|
-
if (!asset) {
|
|
86
|
-
logger.debug("no managed Databricks CLI asset for platform", {
|
|
87
|
-
platform: process.platform,
|
|
88
|
-
arch: process.arch,
|
|
89
|
-
});
|
|
90
|
-
return undefined;
|
|
91
|
-
}
|
|
92
|
-
const root = join(homedir(), ".databricks");
|
|
93
|
-
const binDir = join(root, "bin");
|
|
94
|
-
const executable = managedExecutableName();
|
|
95
|
-
const installed = await bin.ensure("databricks", asset, {
|
|
96
|
-
autoUnpackage: true,
|
|
97
|
-
destination: { root, binDir, path: join(binDir, executable) },
|
|
98
|
-
minVersion: cliAssets.minimumVersion,
|
|
99
|
-
selector: ({ source }) => join(source, executable),
|
|
100
|
-
versionParser: (output) => {
|
|
101
|
-
const version = bin.parseVersion(output);
|
|
102
|
-
return version === cliAssets.version ? version : undefined;
|
|
103
|
-
},
|
|
104
|
-
});
|
|
105
|
-
logger.debug("ensured managed Databricks CLI", {
|
|
106
|
-
executable: installed.path,
|
|
107
|
-
version: cliAssets.version,
|
|
108
|
-
});
|
|
109
|
-
return installed.path;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function managedExecutable(): string {
|
|
113
|
-
return join(homedir(), ".databricks", "bin", managedExecutableName());
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function managedExecutableName(): string {
|
|
117
|
-
return process.platform === "win32" ? "databricks.exe" : "databricks";
|
|
118
|
-
}
|
|
119
|
-
|
|
120
41
|
async function compatibleDatabricksCli(executable: string): Promise<boolean> {
|
|
121
42
|
try {
|
|
122
43
|
const result = await runProcess(executable, ["--version"], {}, 10_000);
|
|
@@ -127,16 +48,8 @@ async function compatibleDatabricksCli(executable: string): Promise<boolean> {
|
|
|
127
48
|
});
|
|
128
49
|
return false;
|
|
129
50
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
version !== undefined && bin.isVersionAtLeast(version, cliAssets.minimumVersion);
|
|
133
|
-
logger.debug("probed Databricks CLI version", {
|
|
134
|
-
executable,
|
|
135
|
-
version: version ?? "unknown",
|
|
136
|
-
minimumVersion: cliAssets.minimumVersion,
|
|
137
|
-
compatible,
|
|
138
|
-
});
|
|
139
|
-
return compatible;
|
|
51
|
+
logger.debug("selected installed Databricks CLI", { executable });
|
|
52
|
+
return true;
|
|
140
53
|
} catch (cause) {
|
|
141
54
|
logger.debug("Databricks CLI version probe unavailable", {
|
|
142
55
|
executable,
|
|
@@ -146,11 +59,6 @@ async function compatibleDatabricksCli(executable: string): Promise<boolean> {
|
|
|
146
59
|
}
|
|
147
60
|
}
|
|
148
61
|
|
|
149
|
-
function platformAsset(): DatabricksCliAsset | undefined {
|
|
150
|
-
const key = `${process.platform}-${process.arch}` as keyof typeof cliAssets.assets;
|
|
151
|
-
return cliAssets.assets[key];
|
|
152
|
-
}
|
|
153
|
-
|
|
154
62
|
/** Run interactive Databricks CLI login for one profile. */
|
|
155
63
|
export async function databricksCliLogin(
|
|
156
64
|
profile: string,
|
|
@@ -164,8 +72,9 @@ export async function databricksCliLogin(
|
|
|
164
72
|
["auth", "login", "--profile", profile, "--timeout", `${Math.ceil(timeoutMs / 1000)}s`],
|
|
165
73
|
environment,
|
|
166
74
|
);
|
|
167
|
-
if (result.exitCode !== 0)
|
|
75
|
+
if (result.exitCode !== 0) {
|
|
168
76
|
throw new AuthError("cli", result.stderr || `databricks auth login exited ${result.exitCode}`);
|
|
77
|
+
}
|
|
169
78
|
logger.debug("Databricks CLI login completed", { profile, executable });
|
|
170
79
|
}
|
|
171
80
|
|
|
@@ -180,8 +89,9 @@ export async function databricksCliToken(
|
|
|
180
89
|
const args = ["auth", "token", "--profile", profile, "--output", "json"];
|
|
181
90
|
if (forceRefresh) args.push("--force-refresh");
|
|
182
91
|
const result = await runProcess(executable, args, environment);
|
|
183
|
-
if (result.exitCode !== 0)
|
|
92
|
+
if (result.exitCode !== 0) {
|
|
184
93
|
throw new AuthError("cli", result.stderr || `databricks auth token exited ${result.exitCode}`);
|
|
94
|
+
}
|
|
185
95
|
let value: Record<string, unknown>;
|
|
186
96
|
try {
|
|
187
97
|
value = JSON.parse(result.stdout ?? "") as Record<string, unknown>;
|
|
@@ -281,8 +191,9 @@ export class DatabricksCliProvider implements TokenProvider {
|
|
|
281
191
|
logger.debug("resolving provider CLI lazily", { profile: this.profile });
|
|
282
192
|
this.resolution = this.executableOrResolver()
|
|
283
193
|
.then((executable) => {
|
|
284
|
-
if (!executable)
|
|
194
|
+
if (!executable) {
|
|
285
195
|
throw new AuthError("cli", "Databricks CLI is unavailable on this platform");
|
|
196
|
+
}
|
|
286
197
|
return executable;
|
|
287
198
|
})
|
|
288
199
|
.catch((error) => {
|
package/src/_errors.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Authentication failure with a stable category for callers and language shims. */
|
|
2
|
+
export class AuthError extends Error {
|
|
3
|
+
constructor(
|
|
4
|
+
readonly kind: "config" | "oauth" | "cli",
|
|
5
|
+
message: string,
|
|
6
|
+
options?: ErrorOptions,
|
|
7
|
+
) {
|
|
8
|
+
super(message, options);
|
|
9
|
+
this.name = "AuthError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { AuthError } from "./
|
|
1
|
+
import { AuthError } from "./_errors.ts";
|
|
2
2
|
import { authLogger, credentialId, failureMetadata, tokenMetadata } from "./_logging.ts";
|
|
3
|
-
import type {
|
|
3
|
+
import type { CredentialStore, Token, TokenProvider } from "./_types.ts";
|
|
4
|
+
import type { AccessToken } from "./client.ts";
|
|
5
|
+
import type { AuthOptions } from "./config.ts";
|
|
4
6
|
|
|
5
7
|
const logger = authLogger("lifecycle");
|
|
6
8
|
|
|
@@ -10,30 +12,22 @@ export class TokenLifecycle {
|
|
|
10
12
|
private readonly key: string,
|
|
11
13
|
private readonly provider: TokenProvider,
|
|
12
14
|
private readonly store: CredentialStore,
|
|
13
|
-
private readonly options: AuthOptions
|
|
15
|
+
private readonly options: Required<AuthOptions>,
|
|
14
16
|
private readonly now: () => Date = () => new Date(),
|
|
15
17
|
) {
|
|
16
18
|
logger.debug("created authentication lifecycle", {
|
|
17
19
|
credential: credentialId(key),
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
loginTimeoutSeconds: options.loginTimeoutSeconds,
|
|
20
|
+
refreshBufferMs: options.refreshBufferMs,
|
|
21
|
+
lockTimeoutMs: options.lockTimeoutMs,
|
|
22
|
+
loginTimeoutMs: options.loginTimeoutMs,
|
|
22
23
|
silentProvider: provider.canAuthenticateSilently(),
|
|
23
24
|
});
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
storeName(): string {
|
|
27
|
-
return this.store.name();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
27
|
async login(): Promise<AccessToken> {
|
|
31
28
|
logger.debug("interactive login requested", this.context());
|
|
32
29
|
return this.withLock(async () => {
|
|
33
|
-
await this.
|
|
34
|
-
const token = validateToken(
|
|
35
|
-
await this.provider.login(this.options.loginTimeoutSeconds * 1000),
|
|
36
|
-
);
|
|
30
|
+
const token = validateToken(await this.provider.login(this.options.loginTimeoutMs));
|
|
37
31
|
await this.store.save(this.key, token);
|
|
38
32
|
logger.debug("interactive login stored credential", {
|
|
39
33
|
...this.context(),
|
|
@@ -60,12 +54,7 @@ export class TokenLifecycle {
|
|
|
60
54
|
|
|
61
55
|
forceRefresh(login = true): Promise<AccessToken> {
|
|
62
56
|
logger.debug("forced refresh requested", { ...this.context(), login });
|
|
63
|
-
return this.
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
refreshRejectedToken(staleAccessToken: string, login = true): Promise<AccessToken> {
|
|
67
|
-
logger.debug("rejected token refresh requested", { ...this.context(), login });
|
|
68
|
-
return this.refreshRejected(staleAccessToken, login);
|
|
57
|
+
return this.refresh(login);
|
|
69
58
|
}
|
|
70
59
|
|
|
71
60
|
async logout(): Promise<void> {
|
|
@@ -97,24 +86,9 @@ export class TokenLifecycle {
|
|
|
97
86
|
});
|
|
98
87
|
}
|
|
99
88
|
|
|
100
|
-
private async
|
|
101
|
-
staleAccessToken: string | undefined,
|
|
102
|
-
login: boolean,
|
|
103
|
-
): Promise<AccessToken> {
|
|
89
|
+
private async refresh(login: boolean): Promise<AccessToken> {
|
|
104
90
|
return this.withLock(async () => {
|
|
105
91
|
const current = await this.store.load(this.key);
|
|
106
|
-
if (
|
|
107
|
-
staleAccessToken &&
|
|
108
|
-
current?.accessToken &&
|
|
109
|
-
current.accessToken !== staleAccessToken &&
|
|
110
|
-
isValid(current, this.now())
|
|
111
|
-
) {
|
|
112
|
-
logger.debug("reused replacement for rejected credential", {
|
|
113
|
-
...this.context(),
|
|
114
|
-
token: tokenMetadata(current, this.now()),
|
|
115
|
-
});
|
|
116
|
-
return publicToken(current);
|
|
117
|
-
}
|
|
118
92
|
return this.renew(current, login);
|
|
119
93
|
});
|
|
120
94
|
}
|
|
@@ -137,7 +111,7 @@ export class TokenLifecycle {
|
|
|
137
111
|
});
|
|
138
112
|
if (!login) throw error;
|
|
139
113
|
logger.debug("falling back to interactive login", this.context());
|
|
140
|
-
token = await this.provider.login(this.options.
|
|
114
|
+
token = await this.provider.login(this.options.loginTimeoutMs);
|
|
141
115
|
}
|
|
142
116
|
} else if (this.provider.canAuthenticateSilently()) {
|
|
143
117
|
logger.debug("attempting silent credential acquisition", {
|
|
@@ -145,7 +119,7 @@ export class TokenLifecycle {
|
|
|
145
119
|
loginFallback: login,
|
|
146
120
|
});
|
|
147
121
|
try {
|
|
148
|
-
token = await this.provider.authenticate(this.options.
|
|
122
|
+
token = await this.provider.authenticate(this.options.loginTimeoutMs);
|
|
149
123
|
} catch (error) {
|
|
150
124
|
logger.debug("silent credential acquisition failed", {
|
|
151
125
|
...this.context(),
|
|
@@ -154,11 +128,11 @@ export class TokenLifecycle {
|
|
|
154
128
|
});
|
|
155
129
|
if (!login) throw error;
|
|
156
130
|
logger.debug("falling back to interactive login", this.context());
|
|
157
|
-
token = await this.provider.login(this.options.
|
|
131
|
+
token = await this.provider.login(this.options.loginTimeoutMs);
|
|
158
132
|
}
|
|
159
133
|
} else if (login) {
|
|
160
134
|
logger.debug("provider requires interactive login", this.context());
|
|
161
|
-
token = await this.provider.login(this.options.
|
|
135
|
+
token = await this.provider.login(this.options.loginTimeoutMs);
|
|
162
136
|
} else {
|
|
163
137
|
throw new AuthError(
|
|
164
138
|
"oauth",
|
|
@@ -170,7 +144,6 @@ export class TokenLifecycle {
|
|
|
170
144
|
...this.context(),
|
|
171
145
|
token: tokenMetadata(token, this.now()),
|
|
172
146
|
});
|
|
173
|
-
await this.store.prepareWrite();
|
|
174
147
|
await this.store.save(this.key, token);
|
|
175
148
|
logger.debug("credential saved", this.context());
|
|
176
149
|
return publicToken(token);
|
|
@@ -179,14 +152,16 @@ export class TokenLifecycle {
|
|
|
179
152
|
private canReuse(token: Token): boolean {
|
|
180
153
|
if (!isValid(token, this.now())) return false;
|
|
181
154
|
if (!token.expiry) return true;
|
|
182
|
-
return (
|
|
183
|
-
Date.parse(token.expiry) - this.now().getTime() > this.options.refreshBufferSeconds * 1000
|
|
184
|
-
);
|
|
155
|
+
return Date.parse(token.expiry) - this.now().getTime() > this.options.refreshBufferMs;
|
|
185
156
|
}
|
|
186
157
|
|
|
187
158
|
private async withLock<T>(action: () => Promise<T>): Promise<T> {
|
|
188
159
|
logger.debug("waiting for credential lock", this.context());
|
|
189
|
-
|
|
160
|
+
// Omit the timeout (wait indefinitely) unless a positive cap is configured.
|
|
161
|
+
// The holder may run for minutes; the caller owns the overall request budget
|
|
162
|
+
// and the lock is always released when `action` settles (success or error).
|
|
163
|
+
const lockTimeoutMs = this.options.lockTimeoutMs > 0 ? this.options.lockTimeoutMs : undefined;
|
|
164
|
+
const lease = await this.store.acquireLock(this.key, lockTimeoutMs);
|
|
190
165
|
logger.debug("credential lock acquired", this.context());
|
|
191
166
|
let failure: unknown;
|
|
192
167
|
try {
|
|
@@ -209,7 +184,7 @@ export class TokenLifecycle {
|
|
|
209
184
|
}
|
|
210
185
|
|
|
211
186
|
private context(): Record<string, unknown> {
|
|
212
|
-
return { credential: credentialId(this.key)
|
|
187
|
+
return { credential: credentialId(this.key) };
|
|
213
188
|
}
|
|
214
189
|
}
|
|
215
190
|
|
|
@@ -223,8 +198,9 @@ export function publicToken(token: Token): AccessToken {
|
|
|
223
198
|
}
|
|
224
199
|
|
|
225
200
|
export function validateToken(token: Token, previous?: Token): Token {
|
|
226
|
-
if (!token.accessToken.trim())
|
|
201
|
+
if (!token.accessToken.trim()) {
|
|
227
202
|
throw new AuthError("oauth", "Token response did not contain an access token");
|
|
203
|
+
}
|
|
228
204
|
return {
|
|
229
205
|
accessToken: token.accessToken,
|
|
230
206
|
tokenType: token.tokenType || "Bearer",
|
package/src/_logging.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as hash from "@dbx-tools/shared-core/hash";
|
|
2
2
|
import * as log from "@dbx-tools/shared-core/log";
|
|
3
3
|
|
|
4
|
-
import { AuthError } from "./
|
|
5
|
-
import type { Token } from "./
|
|
4
|
+
import { AuthError } from "./_errors.ts";
|
|
5
|
+
import type { Token } from "./_types.ts";
|
|
6
6
|
|
|
7
7
|
/** Auth logger with a stable capability-specific tag. */
|
|
8
8
|
export function authLogger(scope: string): log.Logger {
|