vscode icon indicating copy to clipboard operation
vscode copied to clipboard

Getting wrong account

Open chrmarti opened this issue 1 year ago • 2 comments

Testing #226540

Asking for 'scope2' and account 2, I got 'scope1' and account 1:

Image

The extension had access to account 1, but not account 2. I expected undefined to be returned and the UI to allow me to grant the extension access to account 2.

Code:

// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
import * as crypto from 'crypto';

// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {

	// Use the console to output diagnostic information (console.log) and errors (console.error)
	// This line of code will only be executed once when your extension is activated
	console.log('Congratulations, your extension "multi-account-test" is now active!');

	// The command has been defined in the package.json file
	// Now provide the implementation of the command with registerCommand
	// The commandId parameter must match the command field in package.json
	const disposable = vscode.commands.registerCommand('multi-account-test.helloWorld', async () => {
		// The code you place here will be executed every time your command is executed
		// Display a message box to the user
		vscode.window.showInformationMessage('Hello World from multi-account-test!');
		
		const accounts = await vscode.authentication.getAccounts('multi-account-test');
		console.log('accounts', accounts);
		const session = await vscode.authentication.getSession('multi-account-test', ['scope2'], { account: accounts[1] });
		console.log('session', session);
	});

	context.subscriptions.push(disposable);

	const account1 = { id: 'test-account-id-1', label: 'Test Account 1' };
	const account2 = { id: 'test-account-id-2', label: 'Test Account 2' };
	let sessions: vscode.AuthenticationSession[] = [
		{
			id: 'test-id-' + crypto.randomUUID(),
			accessToken: 'test-token-' + crypto.randomUUID(),
			account: account1,
			scopes: ['scope1'],
		},
		{
			id: 'test-id-' + crypto.randomUUID(),
			accessToken: 'test-token-' + crypto.randomUUID(),
			account: account1,
			scopes: ['scope2'],
		},
		{
			id: 'test-id-' + crypto.randomUUID(),
			accessToken: 'test-token-' + crypto.randomUUID(),
			account: account2,
			scopes: ['scope1'],
		},
		{
			id: 'test-id-' + crypto.randomUUID(),
			accessToken: 'test-token-' + crypto.randomUUID(),
			account: account2,
			scopes: ['scope2'],
		},
		{
			id: 'test-id-' + crypto.randomUUID(),
			accessToken: 'test-token-' + crypto.randomUUID(),
			account: account2,
			scopes: ['scope2'],
		},
	];

	const emitter = new vscode.EventEmitter<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent>();
	vscode.authentication.registerAuthenticationProvider('multi-account-test', 'Multi Account Test', {
		onDidChangeSessions: emitter.event,
		getSessions: async (scopes: readonly string[] | undefined, options: vscode.AuthenticationProviderSessionOptions): Promise<vscode.AuthenticationSession[]> => {
			return sessions.slice();
		},
		createSession: async (scopes: readonly string[], options: vscode.AuthenticationProviderSessionOptions): Promise<vscode.AuthenticationSession> => {
			return {
				id: 'test-id-' + crypto.randomUUID(),
				accessToken: 'test-token-' + crypto.randomUUID(),
				account: options.account || { id: 'test-account-id', label: 'Test Account' },
				scopes: scopes,
			};
		},
		removeSession: async (sessionId: string): Promise<void> => {
			const removed = sessions.filter(session => session.id === sessionId);
			sessions = sessions.filter(session => session.id !== sessionId);
			emitter.fire({ added: [], removed, changed: [] });
		},
	}, { supportsMultipleAccounts: true });
}

// This method is called when your extension is deactivated
export function deactivate() {}

chrmarti avatar Aug 27 '24 16:08 chrmarti

  1. Adding createIfNone: true showed the picker with multiple entries of the same account (#226781). The accounts were not filtered to scope2 (as passed to getSession) which seems like bug.
  2. When I reran with createIfNone: true it again asked me if want to allow the extension to sign in, but then did not show the picker, but just returned my previous choice. It should probably not ask me if I want to allow the extension to sign in this second time.

chrmarti avatar Aug 28 '24 07:08 chrmarti

Realized I hadn't fully implemented getSessions. With that it works much more like I imagined. Should clearSessionPreference: true show the session picker? I don't see that.

// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
import * as crypto from 'crypto';

// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {

	// Use the console to output diagnostic information (console.log) and errors (console.error)
	// This line of code will only be executed once when your extension is activated
	console.log('Congratulations, your extension "multi-account-test" is now active!');

	// The command has been defined in the package.json file
	// Now provide the implementation of the command with registerCommand
	// The commandId parameter must match the command field in package.json
	const disposable = vscode.commands.registerCommand('multi-account-test.helloWorld', async () => {
		// The code you place here will be executed every time your command is executed
		// Display a message box to the user
		vscode.window.showInformationMessage('Hello World from multi-account-test!');
		
		const accounts = await vscode.authentication.getAccounts('multi-account-test');
		console.log('accounts', accounts);
		const session = await vscode.authentication.getSession('multi-account-test', ['scope2'], { /* account: accounts[1], */ /* createIfNone: true, */ clearSessionPreference: true, /* forceNewSession: true  */});
		console.log('session', session);
	});

	context.subscriptions.push(disposable);

	const account1 = { id: 'test-account-id-1', label: 'Test Account 1' };
	const account2 = { id: 'test-account-id-2', label: 'Test Account 2' };
	let sessions: vscode.AuthenticationSession[] = [
		{
			id: 'test-id-' + crypto.randomUUID(),
			accessToken: 'test-token-' + crypto.randomUUID(),
			account: account1,
			scopes: ['scope1'],
		},
		{
			id: 'test-id-' + crypto.randomUUID(),
			accessToken: 'test-token-' + crypto.randomUUID(),
			account: account1,
			scopes: ['scope2'],
		},
		{
			id: 'test-id-' + crypto.randomUUID(),
			accessToken: 'test-token-' + crypto.randomUUID(),
			account: account2,
			scopes: ['scope1'],
		},
		{
			id: 'test-id-' + crypto.randomUUID(),
			accessToken: 'test-token-' + crypto.randomUUID(),
			account: account2,
			scopes: ['scope2'],
		},
		{
			id: 'test-id-' + crypto.randomUUID(),
			accessToken: 'test-token-' + crypto.randomUUID(),
			account: account2,
			scopes: ['scope2', 'scope3'],
		},
	];

	const emitter = new vscode.EventEmitter<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent>();
	vscode.authentication.registerAuthenticationProvider('multi-account-test', 'Multi Account Test', {
		onDidChangeSessions: emitter.event,
		getSessions: async (scopes: readonly string[] | undefined, options: vscode.AuthenticationProviderSessionOptions): Promise<vscode.AuthenticationSession[]> => {
			const sessionsForAccount = options.account ? sessions.filter(s => s.account.id === options.account!.id) : sessions.slice();
			if (!scopes) {
				return sessionsForAccount;
			}
			const res = sessionsForAccount.filter(session => scopes.every(scope => session.scopes.includes(scope)));
			return res;
		},
		createSession: async (scopes: readonly string[], options: vscode.AuthenticationProviderSessionOptions): Promise<vscode.AuthenticationSession> => {
			await vscode.window.showInformationMessage(`Creating session for '${options.account?.label}'`, { modal: true });
			const session = {
				id: 'test-id-' + crypto.randomUUID(),
				accessToken: 'test-token-' + crypto.randomUUID(),
				account: options.account || { id: 'test-account-id-3', label: 'Test Account 3' },
				scopes: scopes,
			};
			sessions.push(session);
			emitter.fire({ added: [session], removed: [], changed: [] });
			return session;
		},
		removeSession: async (sessionId: string): Promise<void> => {
			const removed = sessions.filter(session => session.id === sessionId);
			sessions = sessions.filter(session => session.id !== sessionId);
			emitter.fire({ added: [], removed, changed: [] });
		},
	}, { supportsMultipleAccounts: true });
}

// This method is called when your extension is deactivated
export function deactivate() {}

chrmarti avatar Aug 28 '24 07:08 chrmarti

@chrmarti you have to include createIfNone: true to get the session picker. Otherwise it just silently clears the preference... that's how it works today, not saying it should work that way.

Can you quickly recap what the issue is in this issue because from your last comment it sounds like there's no issue related to getting the wrong account?

TylerLeonhardt avatar Sep 05 '24 03:09 TylerLeonhardt

Sounds good. This was mainly me slowly digging into the API then. Since what I describe is expected behavior, I think we can close this issue.

chrmarti avatar Sep 05 '24 07:09 chrmarti