Skip to content

Commit b95a64e

Browse files
authored
🐛 handle creds better in solution server (konveyor#799)
Fixes konveyor#798 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a dedicated sign-in step (authenticate) to store credentials before connecting. * Connections can use an existing bearer token; tokens are exchanged and refreshed automatically. * VS Code: entering credentials triggers a solution server restart to apply them. * **Bug Fixes** * More reliable reconnect/refresh handling that stops after failed attempts. * Clear error when connecting without prior sign-in. * Authentication-disabled mode now clears auth state and behaves correctly. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: David Zager <david.j.zager@gmail.com>
1 parent b1ffcb2 commit b95a64e

File tree

3 files changed

+41
-30
lines changed

3 files changed

+41
-30
lines changed

agentic/src/clients/solutionServerClient.ts

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,27 @@ export class SolutionServerClient {
106106
this.logger.info("Solution server configuration updated");
107107
}
108108

109-
public async connect(username?: string, password?: string): Promise<void> {
109+
public async authenticate(username: string, password: string): Promise<void> {
110+
if (!this.enabled) {
111+
this.logger.info("Solution server is disabled, skipping authentication");
112+
return;
113+
}
114+
115+
if (!this.authEnabled) {
116+
this.logger.info("Authentication is disabled");
117+
return;
118+
}
119+
120+
if (!username || !password) {
121+
throw new SolutionServerClientError("No username or password provided");
122+
}
123+
124+
this.username = username;
125+
this.password = password;
126+
this.logger.info("Credentials stored for authentication");
127+
}
128+
129+
public async connect(): Promise<void> {
110130
if (!this.enabled) {
111131
this.logger.info("Solution server is disabled, skipping connection");
112132
return;
@@ -117,24 +137,24 @@ export class SolutionServerClient {
117137
this.sslBypassCleanup = this.applySSLBypass();
118138
}
119139

140+
// Handle authentication if required
120141
if (this.authEnabled) {
121-
if (!username || !password) {
122-
throw new SolutionServerClientError("No username or password provided");
142+
// Only require credentials if we don't yet have a token
143+
if (!this.bearerToken && (!this.username || !this.password)) {
144+
throw new SolutionServerClientError("No credentials available. Call authenticate() first.");
123145
}
124-
this.username = username;
125-
this.password = password;
126-
127-
// Always get fresh tokens on startup/connect
128-
try {
129-
if (!this.bearerToken) {
146+
// Exchange for tokens if we don't have one
147+
if (!this.bearerToken) {
148+
try {
130149
await this.exchangeForTokens();
150+
} catch (error) {
151+
this.logger.error("Failed to exchange for tokens", error);
152+
throw error;
131153
}
132-
this.startTokenRefreshTimer();
133-
} catch (error) {
134-
this.logger.error("Failed to exchange for tokens", error);
135-
await this.disconnect();
136-
return;
137154
}
155+
156+
// Ensure refresh timer is running (also after manual restarts)
157+
this.startTokenRefreshTimer();
138158
}
139159

140160
this.mcpClient = new Client(
@@ -803,11 +823,6 @@ export class SolutionServerClient {
803823
return;
804824
}
805825

806-
if (!this.username || !this.password) {
807-
this.logger.warn("No auth config available for token refresh");
808-
return;
809-
}
810-
811826
const url = new URL(this.serverUrl);
812827
const keycloakUrl = `${url.protocol}//${url.host}/auth`;
813828
const tokenUrl = `${keycloakUrl}/realms/${this.realm}/protocol/openid-connect/token`;
@@ -856,8 +871,7 @@ export class SolutionServerClient {
856871
this.logger.error("Error reconnecting to MCP solution server", error);
857872
}
858873
}
859-
860-
// Restart the refresh timer
874+
// Always restart the refresh timer
861875
this.startTokenRefreshTimer();
862876
} catch (error) {
863877
this.logger.error("Token refresh failed", error);

vscode/src/commands.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ import {
3636
getTraceDir,
3737
getConfigSolutionServerAuth,
3838
fileUriToPath,
39-
updateSolutionServerConfig,
40-
getConfigSolutionServer,
4139
} from "./utilities/configuration";
4240
import { EXTENSION_NAME } from "./utilities/constants";
4341
import { promptForCredentials } from "./utilities/auth";
@@ -792,13 +790,11 @@ const commandsMap: (
792790
return;
793791
}
794792

795-
await updateSolutionServerConfig({
796-
auth: {
797-
...getConfigSolutionServer().auth,
798-
...credentials,
799-
},
800-
});
793+
// Update the solution server client with new credentials
794+
// (credentials are already stored by promptForCredentials)
795+
await state.solutionServerClient.authenticate(credentials.username, credentials.password);
801796

797+
// Restart the connection with new credentials
802798
await executeExtensionCommand("restartSolutionServer");
803799
logger.info("Solution server credentials updated successfully.");
804800
},

vscode/src/extension.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,8 @@ class VsCodeExtension {
613613
}
614614

615615
this.state.solutionServerClient
616-
.connect(username, password)
616+
.authenticate(username, password)
617+
.then(() => this.state.solutionServerClient.connect())
617618
.then(() => {
618619
// Update state to reflect successful connection
619620
this.state.mutateData((draft) => {

0 commit comments

Comments
 (0)