Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 2 additions & 2 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export class Commands {
if (this.deploymentManager.isAuthenticated()) {
return;
}
this.logger.info("Logging in");
this.logger.debug("Logging in");

const currentDeployment = await this.secretsManager.getCurrentDeployment();
const url = await maybeAskUrl(
Expand Down Expand Up @@ -205,7 +205,7 @@ export class Commands {
return;
}

this.logger.info("Logging out");
this.logger.debug("Logging out");

await this.deploymentManager.clearDeployment();

Expand Down
17 changes: 14 additions & 3 deletions src/deployment/deploymentManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import { type Logger } from "../logging/logger";
import { type OAuthSessionManager } from "../oauth/sessionManager";
import { type WorkspaceProvider } from "../workspace/workspacesProvider";

import { type Deployment, type DeploymentWithAuth } from "./types";
import {
DeploymentSchema,
type Deployment,
type DeploymentWithAuth,
} from "./types";

import type { User } from "coder/site/src/api/typesGenerated";
import type * as vscode from "vscode";
Expand Down Expand Up @@ -115,6 +119,10 @@ export class DeploymentManager implements vscode.Disposable {
public async setDeployment(
deployment: DeploymentWithAuth & { user: User },
): Promise<void> {
this.logger.debug("Setting deployment", {
hostname: deployment.safeHostname,
user: deployment.user.username,
});
this.#deployment = { ...deployment };

// Updates client credentials
Expand All @@ -131,14 +139,17 @@ export class DeploymentManager implements vscode.Disposable {
this.updateAuthContexts(deployment.user);
this.refreshWorkspaces();

await this.oauthSessionManager.setDeployment(deployment);
await this.persistDeployment(deployment);
const deploymentWithoutAuth: Deployment =
DeploymentSchema.parse(deployment);
await this.oauthSessionManager.setDeployment(deploymentWithoutAuth);
await this.persistDeployment(deploymentWithoutAuth);
}

/**
* Clears the current deployment.
*/
public async clearDeployment(): Promise<void> {
this.logger.debug("Clearing deployment", this.#deployment?.safeHostname);
this.suspendSession();
this.#authListenerDisposable?.dispose();
this.#authListenerDisposable = undefined;
Expand Down
2 changes: 1 addition & 1 deletion src/login/loginCoordinator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ export class LoginCoordinator implements vscode.Disposable {
*/
private async loginWithOAuth(deployment: Deployment): Promise<LoginResult> {
try {
this.logger.info("Starting OAuth authentication");
this.logger.debug("Starting OAuth authentication");

const { tokenResponse, user } = await vscode.window.withProgress(
{
Expand Down
4 changes: 2 additions & 2 deletions src/oauth/authorizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export class OAuthAuthorizer implements vscode.Disposable {
reportProgress("fetching user...", 20);
const user = await client.getAuthenticatedUser();

this.logger.info("OAuth login flow completed successfully");
this.logger.debug("OAuth login flow completed successfully");

return {
tokenResponse,
Expand Down Expand Up @@ -157,7 +157,7 @@ export class OAuthAuthorizer implements vscode.Disposable {
deployment.safeHostname,
response.data,
);
this.logger.info(
this.logger.debug(
"Saved OAuth client registration:",
response.data.client_id,
);
Expand Down
19 changes: 11 additions & 8 deletions src/oauth/sessionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,12 +311,15 @@ export class OAuthSessionManager implements vscode.Disposable {
) {
return;
}
this.logger.debug("Switching OAuth deployment", deployment);
this.deployment = deployment;
this.clearRefreshState();

// Block on refresh if token is expired to ensure valid state for callers
const storedTokens = await this.getStoredTokens();
if (storedTokens) {
this.logger.debug("Switching OAuth deployment", deployment);
}

// Block on refresh if token is expired to ensure valid state for callers
if (storedTokens && Date.now() >= storedTokens.expiry_timestamp) {
try {
await this.refreshToken();
Expand All @@ -331,7 +334,6 @@ export class OAuthSessionManager implements vscode.Disposable {
}

public clearDeployment(): void {
this.logger.debug("Clearing OAuth deployment state");
this.deployment = null;
this.clearRefreshState();
}
Expand Down Expand Up @@ -422,7 +424,7 @@ export class OAuthSessionManager implements vscode.Disposable {
public async revokeRefreshToken(): Promise<void> {
const storedTokens = await this.getStoredTokens();
if (!storedTokens?.refresh_token) {
this.logger.info("No refresh token to revoke");
this.logger.debug("No refresh token to revoke");
return;
}

Expand All @@ -449,11 +451,13 @@ export class OAuthSessionManager implements vscode.Disposable {
await this.prepareOAuthOperation(authToken);

if (!metadata.revocation_endpoint) {
this.logger.info("No revocation endpoint available, skipping revocation");
this.logger.debug(
"No revocation endpoint available, skipping revocation",
);
return;
}

this.logger.info("Revoking refresh token");
this.logger.debug("Revoking refresh token");

const params: OAuth2TokenRevocationRequest = {
token: tokenToRevoke,
Expand All @@ -475,7 +479,7 @@ export class OAuthSessionManager implements vscode.Disposable {
},
);

this.logger.info("Token revocation successful");
this.logger.debug("Token revocation successful");
} catch (error) {
this.logger.error("Token revocation failed:", error);
throw error;
Expand Down Expand Up @@ -503,6 +507,5 @@ export class OAuthSessionManager implements vscode.Disposable {
public dispose(): void {
this.disposed = true;
this.clearDeployment();
this.logger.debug("OAuth session manager disposed");
}
}
21 changes: 21 additions & 0 deletions src/remote/remote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ export class Remote {
return;
}

this.logger.debug("Setting up remote connection", {
hostname: parts.safeHostname,
workspace: `${parts.username}/${parts.workspace}`,
agent: parts.agent || "(default)",
});

const workspaceName = `${parts.username}/${parts.workspace}`;

// Migrate existing legacy file-based auth to secrets storage.
Expand All @@ -110,6 +116,11 @@ export class Remote {
const auth = await this.secretsManager.getSessionAuth(parts.safeHostname);
const baseUrlRaw = auth?.url ?? "";
const token = auth?.token;
this.logger.debug("Retrieved auth for hostname", {
hostname: parts.safeHostname,
hasUrl: Boolean(baseUrlRaw),
hasToken: token !== undefined,
});
// Empty token is valid for mTLS
if (baseUrlRaw && token !== undefined) {
await this.cliManager.configure(parts.safeHostname, baseUrlRaw, token);
Expand Down Expand Up @@ -343,6 +354,10 @@ export class Remote {
);

// Wait for workspace to be running and agent to be ready
this.logger.debug("Starting workspace state machine", {
workspace: workspaceName,
initialStatus: workspace.latest_build.status,
});
const stateMachine = new WorkspaceStateMachine(
parts,
workspaceClient,
Expand Down Expand Up @@ -423,6 +438,12 @@ export class Remote {
throw new Error("Failed to get workspace or agent from state machine");
}

this.logger.info("Workspace ready", {
workspace: workspaceName,
agent: agent.name,
status: workspace.latest_build.status,
});

this.commands.workspace = workspace;

// Watch coder inbox for messages
Expand Down