Skip to content

Commit c3b2309

Browse files
committed
Improve consistency in client-side login/logout experience
1 parent dc4d6d4 commit c3b2309

File tree

4 files changed

+66
-44
lines changed

4 files changed

+66
-44
lines changed

‎src/commands.ts‎

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -159,31 +159,32 @@ export class Commands{
159159
}
160160

161161
/**
162-
* Log into the provided deployment. If the deployment URL is not specified,
162+
* Log into the provided deployment. If the deployment URL is not specified,
163163
* ask for it first with a menu showing recent URLs along with the default URL
164164
* and CODER_URL, if those are set.
165165
*/
166-
publicasynclogin(...args: string[]): Promise<void>{
167-
// Destructure would be nice but VS Code can pass undefined which errors.
168-
constinputUrl=args[0];
169-
constinputToken=args[1];
170-
constinputLabel=args[2];
171-
constisAutologin=
172-
typeofargs[3]==="undefined" ? false : Boolean(args[3]);
173-
174-
consturl=awaitthis.maybeAskUrl(inputUrl);
166+
publicasynclogin(args?: {
167+
url?: string;
168+
token?: string;
169+
label?: string;
170+
autoLogin?: boolean;
171+
}): Promise<void>{
172+
consturl=awaitthis.maybeAskUrl(args?.url);
175173
if(!url){
176174
return;// The user aborted.
177175
}
178176

179177
// It is possible that we are trying to log into an old-style host, in which
180178
// case we want to write with the provided blank label instead of generating
181179
// a host label.
182-
constlabel=
183-
typeofinputLabel==="undefined" ? toSafeHost(url) : inputLabel;
180+
constlabel=args?.label===undefined ? toSafeHost(url) : args?.label;
184181

185182
// Try to get a token from the user, if we need one, and their user.
186-
constres=awaitthis.maybeAskToken(url,inputToken,isAutologin);
183+
constres=awaitthis.maybeAskToken(
184+
url,
185+
args?.token,
186+
args?.autoLogin===true,
187+
);
187188
if(!res){
188189
return;// The user aborted, or unable to auth.
189190
}
@@ -237,21 +238,23 @@ export class Commands{
237238
*/
238239
privateasyncmaybeAskToken(
239240
url: string,
240-
token: string,
241-
isAutologin: boolean,
241+
token: string|undefined,
242+
isAutoLogin: boolean,
242243
): Promise<{user: User;token: string}|null>{
243244
constclient=CoderApi.create(url,token,this.storage.output,()=>
244245
vscode.workspace.getConfiguration(),
245246
);
246-
if(!needToken(vscode.workspace.getConfiguration())){
247-
try{
248-
constuser=awaitclient.getAuthenticatedUser();
249-
// For non-token auth, we write a blank token since the `vscodessh`
250-
// command currently always requires a token file.
251-
return{token: "", user };
252-
}catch(err){
247+
constneedsToken=needToken(vscode.workspace.getConfiguration());
248+
try{
249+
constuser=awaitclient.getAuthenticatedUser();
250+
// For non-token auth, we write a blank token since the `vscodessh`
251+
// command currently always requires a token file.
252+
// For token auth, we have valid access so we can just return the user here
253+
return{token: needsToken&&token ? token : "", user };
254+
}catch(err){
255+
if(!needToken(vscode.workspace.getConfiguration())){
253256
constmessage=getErrorMessage(err,"no response from the server");
254-
if(isAutologin){
257+
if(isAutoLogin){
255258
this.storage.output.warn(
256259
"Failed to log in to Coder server:",
257260
message,
@@ -286,6 +289,9 @@ export class Commands{
286289
value: token||(awaitthis.storage.getSessionToken()),
287290
ignoreFocusOut: true,
288291
validateInput: async(value)=>{
292+
if(!value){
293+
returnnull;
294+
}
289295
client.setSessionToken(value);
290296
try{
291297
user=awaitclient.getAuthenticatedUser();
@@ -354,7 +360,10 @@ export class Commands{
354360
// Sanity check; command should not be available if no url.
355361
thrownewError("You are not logged in");
356362
}
363+
awaitthis.forceLogout();
364+
}
357365

366+
publicasyncforceLogout(): Promise<void>{
358367
// Clear from the REST client. An empty url will indicate to other parts of
359368
// the code that we are logged out.
360369
this.restClient.setHost("");
@@ -373,7 +382,7 @@ export class Commands{
373382
.showInformationMessage("You've been logged out of Coder!","Login")
374383
.then((action)=>{
375384
if(action==="Login"){
376-
vscode.commands.executeCommand("coder.login");
385+
this.login();
377386
}
378387
});
379388

‎src/extension.ts‎

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,21 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void>{
297297
commands.viewLogs.bind(commands),
298298
);
299299

300+
ctx.subscriptions.push(
301+
storage.onDidChangeSessionToken(async()=>{
302+
consttoken=awaitstorage.getSessionToken();
303+
consturl=storage.getUrl();
304+
if(!token){
305+
output.info("Logging out");
306+
awaitcommands.forceLogout();
307+
}elseif(url){
308+
output.info("Logging in");
309+
// Should login the user directly if the URL+Token are valid
310+
awaitcommands.login({ url, token });
311+
}
312+
}),
313+
);
314+
300315
// Since the "onResolveRemoteAuthority:ssh-remote" activation event exists
301316
// in package.json we're able to perform actions before the authority is
302317
// resolved by the remote SSH extension.
@@ -409,15 +424,10 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void>{
409424
// Handle autologin, if not already logged in.
410425
constcfg=vscode.workspace.getConfiguration();
411426
if(cfg.get("coder.autologin")===true){
412-
constdefaultUrl=cfg.get("coder.defaultUrl")||process.env.CODER_URL;
427+
constdefaultUrl=
428+
cfg.get<string>("coder.defaultUrl")||process.env.CODER_URL;
413429
if(defaultUrl){
414-
vscode.commands.executeCommand(
415-
"coder.login",
416-
defaultUrl,
417-
undefined,
418-
undefined,
419-
"true",
420-
);
430+
commands.login({url: defaultUrl,autoLogin: true});
421431
}
422432
}
423433
}

‎src/remote.ts‎

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -239,12 +239,7 @@ export class Remote{
239239
awaitthis.closeRemote();
240240
}else{
241241
// Log in then try again.
242-
awaitvscode.commands.executeCommand(
243-
"coder.login",
244-
baseUrlRaw,
245-
undefined,
246-
parts.label,
247-
);
242+
awaitthis.commands.login({url: baseUrlRaw,label: parts.label});
248243
awaitthis.setup(remoteAuthority,firstConnect);
249244
}
250245
return;
@@ -361,12 +356,7 @@ export class Remote{
361356
if(!result){
362357
awaitthis.closeRemote();
363358
}else{
364-
awaitvscode.commands.executeCommand(
365-
"coder.login",
366-
baseUrlRaw,
367-
undefined,
368-
parts.label,
369-
);
359+
awaitthis.commands.login({url: baseUrlRaw,label: parts.label});
370360
awaitthis.setup(remoteAuthority,firstConnect);
371361
}
372362
return;

‎src/storage.ts‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,19 @@ export class Storage{
115115
}
116116
}
117117

118+
/**
119+
* Subscribe to changes to the session token which can be used to indicate user login status.
120+
*/
121+
publiconDidChangeSessionToken(
122+
listener: ()=>Promise<void>,
123+
): vscode.Disposable{
124+
returnthis.secrets.onDidChange((e)=>{
125+
if(e.key==="sessionToken"){
126+
listener();
127+
}
128+
});
129+
}
130+
118131
/**
119132
* Returns the log path for the "Remote - SSH" output panel. There is no VS
120133
* Code API to get the contents of an output panel. We use this to get the

0 commit comments

Comments
(0)