Skip to content

Commit 9a72ccf

Browse files
committed
refactor: reorganize util modules and return token source
Split src/util.ts into focused modules: src/util/uri.ts (toSafeHost, removeTrailingSlashes, resolveUiUrl, openInBrowser) and src/util/authority.ts (Remote SSH authority helpers), replacing src/uri/utils.ts. Migrate all importers and mirror the unit tests. Make CliCredentialManager.readToken return the token together with its source ("keyring" | "files") so LoginCoordinator labels the login method from the actual source instead of re-deriving it from whether keyring is enabled.
1 parent 7323af4 commit 9a72ccf

22 files changed

Lines changed: 473 additions & 433 deletions

src/api/authInterceptor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { type AxiosError, isAxiosError } from "axios";
22

33
import { AuthTelemetry } from "../instrumentation/auth";
44
import { OAuthError } from "../oauth/errors";
5-
import { toSafeHost } from "../util";
5+
import { toSafeHost } from "../util/uri";
66

77
import type * as vscode from "vscode";
88

src/commands.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ import {
4646
import { resolveCliAuth } from "./settings/cli";
4747
import { appendVsCodeLogs } from "./supportBundle/appendVsCodeLogs";
4848
import { runExportTelemetryCommand } from "./telemetry/export/command";
49-
import { openInBrowser, toRemoteAuthority, toSafeHost } from "./util";
49+
import { toRemoteAuthority } from "./util/authority";
50+
import { openInBrowser, toSafeHost } from "./util/uri";
5051
import { vscodeProposed } from "./vscodeProposed";
5152
import { parseSpeedtestResult } from "./webviews/speedtest/types";
5253
import {

src/core/cliCredentialManager.ts

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import {
1515
import { isKeyringEnabled } from "../settings/cli";
1616
import { getHeaderArgs } from "../settings/headers";
1717
import { type TelemetryReporter } from "../telemetry/reporter";
18-
import { removeTrailingSlashes, toSafeHost } from "../uri/utils";
1918
import { writeAtomically } from "../util/fs";
19+
import { removeTrailingSlashes, toSafeHost } from "../util/uri";
2020

2121
import { version } from "./cliExec";
2222

@@ -35,6 +35,11 @@ type TokenReadSource =
3535
| { mode: "keyring"; binPath: string }
3636
| { mode: "none" };
3737

38+
export interface CliCredential {
39+
token: string;
40+
source: "keyring" | "files";
41+
}
42+
3843
const EXEC_TIMEOUT_MS = 60_000;
3944
const EXEC_LOG_INTERVAL_MS = 5_000;
4045

@@ -128,22 +133,30 @@ export class CliCredentialManager {
128133
/**
129134
* Read a token from CLI-managed credentials. Uses `coder login token --url`
130135
* when keyring auth is active, otherwise reads the file credentials under
131-
* --global-config. Returns undefined on any failure (resolver, CLI, empty
132-
* output). Throws AbortError when the signal is aborted.
136+
* --global-config. Returns the token and the source it came from, or
137+
* undefined on any failure (resolver, CLI, empty output). Throws AbortError
138+
* when the signal is aborted.
133139
*/
134140
public async readToken(
135141
url: string,
136142
configs: Pick<WorkspaceConfiguration, "get">,
137143
options?: { signal?: AbortSignal },
138-
): Promise<string | undefined> {
144+
): Promise<CliCredential | undefined> {
139145
const source = await this.resolveTokenReadSource(url, configs);
140146
if (source.mode === "files") {
141-
return this.readCredentialFiles(url);
147+
const token = await this.readCredentialFiles(url);
148+
return token ? { token, source: "files" } : undefined;
142149
}
143150
if (source.mode === "none") {
144151
return undefined;
145152
}
146-
return this.readKeyringToken(source.binPath, url, configs, options);
153+
const token = await this.readKeyringToken(
154+
source.binPath,
155+
url,
156+
configs,
157+
options,
158+
);
159+
return token ? { token, source: "keyring" } : undefined;
147160
}
148161

149162
private async readKeyringToken(
@@ -284,7 +297,7 @@ export class CliCredentialManager {
284297
private async readCredentialFiles(url: string): Promise<string | undefined> {
285298
try {
286299
const files = await this.readCredentialFilePair(url);
287-
return sameCredentialUrl(files.url, url)
300+
return sameNormalizedUrl(files.url, url)
288301
? nonEmpty(files.token)
289302
: undefined;
290303
} catch (error) {
@@ -376,11 +389,11 @@ export class CliCredentialManager {
376389
}
377390
}
378391

379-
function sameCredentialUrl(storedUrl: string, expectedUrl: string): boolean {
380-
return cleanCredentialUrl(storedUrl) === cleanCredentialUrl(expectedUrl);
392+
function sameNormalizedUrl(storedUrl: string, expectedUrl: string): boolean {
393+
return normalizeUrl(storedUrl) === normalizeUrl(expectedUrl);
381394
}
382395

383-
function cleanCredentialUrl(url: string): string {
396+
function normalizeUrl(url: string): string {
384397
return removeTrailingSlashes(url.trim());
385398
}
386399

src/core/cliManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ import {
2323
import * as pgp from "../pgp";
2424
import { withCancellableProgress, withOptionalProgress } from "../progress";
2525
import { isKeyringEnabled } from "../settings/cli";
26-
import { toSafeHost } from "../util";
2726
import { tempFilePath } from "../util/fs";
27+
import { toSafeHost } from "../util/uri";
2828
import { vscodeProposed } from "../vscodeProposed";
2929

3030
import { BinaryLock } from "./binaryLock";

src/core/secretsManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { z } from "zod";
22

33
import { DeploymentSchema, type Deployment } from "../deployment/types";
4-
import { toSafeHost } from "../util";
4+
import { toSafeHost } from "../util/uri";
55

66
import type { OAuth2ClientRegistrationResponse } from "coder/site/src/api/typesGenerated";
77
import type { Memento, SecretStorage, Disposable } from "vscode";

src/login/loginCoordinator.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { buildOAuthTokenData } from "../oauth/utils";
1010
import { withOptionalProgress } from "../progress";
1111
import { maybeAskAuthMethod, maybeAskUrl } from "../promptUtils";
1212
import { isKeyringEnabled } from "../settings/cli";
13-
import { openInBrowser } from "../util";
13+
import { openInBrowser } from "../util/uri";
1414
import { vscodeProposed } from "../vscodeProposed";
1515

1616
import type { User } from "coder/site/src/api/typesGenerated";
@@ -330,23 +330,23 @@ export class LoginCoordinator implements vscode.Disposable {
330330
cancellable: true,
331331
},
332332
);
333-
const cliCredentialToken = cliCredentialResult.ok
333+
const cliCredential = cliCredentialResult.ok
334334
? cliCredentialResult.value
335335
: undefined;
336336
if (
337-
cliCredentialToken &&
338-
cliCredentialToken !== providedToken &&
339-
cliCredentialToken !== auth?.token
337+
cliCredential &&
338+
cliCredential.token !== providedToken &&
339+
cliCredential.token !== auth?.token
340340
) {
341341
this.logger.debug("Trying token from CLI credentials");
342342
const result = await this.tryTokenAuth(
343343
client,
344-
cliCredentialToken,
344+
cliCredential.token,
345345
isAutoLogin,
346346
);
347347
if (result !== "unauthorized") {
348348
return withLoginMethod(
349-
keyringEnabled ? "keyring_token" : "cli_token",
349+
cliCredential.source === "keyring" ? "keyring_token" : "cli_token",
350350
result,
351351
);
352352
}

src/oauth/authorizer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as vscode from "vscode";
22

33
import { CoderApi } from "../api/coderApi";
4-
import { resolveUiUrl } from "../util";
4+
import { resolveUiUrl } from "../util/uri";
55

66
import {
77
AUTH_GRANT_TYPE,

src/remote/remote.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,12 @@ import {
5151
resolveCliAuth,
5252
} from "../settings/cli";
5353
import { getHeaderCommand } from "../settings/headers";
54+
import { escapeCommandArg, expandPath } from "../util";
5455
import {
5556
AuthorityPrefix,
5657
type AuthorityParts,
57-
escapeCommandArg,
58-
expandPath,
5958
parseRemoteAuthority,
60-
} from "../util";
59+
} from "../util/authority";
6160
import { vscodeProposed } from "../vscodeProposed";
6261
import { WorkspaceMonitor } from "../workspace/workspaceMonitor";
6362

src/remote/workspaceStateMachine.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import type { StartupMode } from "../core/mementoManager";
3737
import type { FeatureSet } from "../featureSet";
3838
import type { Logger } from "../logging/logger";
3939
import type { CliAuth } from "../settings/cli";
40-
import type { AuthorityParts } from "../util";
40+
import type { AuthorityParts } from "../util/authority";
4141

4242
/**
4343
* Manages workspace and agent state transitions until ready for SSH connection.

src/uri/uriHandler.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@ import { errToStr } from "../api/api-helper";
44
import { AuthTelemetry } from "../instrumentation/auth";
55
import { CALLBACK_PATH } from "../oauth/utils";
66
import { maybeAskUrl } from "../promptUtils";
7+
import { toSafeHost } from "../util/uri";
78
import { vscodeProposed } from "../vscodeProposed";
89

9-
import { toSafeHost } from "./utils";
10-
1110
import type { Commands } from "../commands";
1211
import type { ServiceContainer } from "../core/container";
1312
import type { DeploymentManager } from "../deployment/deploymentManager";

0 commit comments

Comments
 (0)