Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
16 changes: 14 additions & 2 deletions src/github/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,20 @@ export async function findExistingSession(
authProviderId: AuthProvider,
getSession: AuthenticationSessionGetter = (providerId, scopes, options) => vscode.authentication.getSession(providerId, scopes, options),
): Promise<ExistingSession | undefined> {
// Establish the preferred account with the normal scopes before looking for broader sessions.
// Otherwise, a single broader session from another account can override the workspace preference.
// Establish the preferred account across all scopes before looking for its best session.
// A scope-specific lookup can otherwise fall back to the only account with those scopes.
const preferredSession = await getSession(authProviderId, [], { silent: true });
if (preferredSession) {
const scopesInPreferenceOrder = [SCOPES_WITH_ADDITIONAL, SCOPES_OLD, SCOPES_OLDEST];
for (const scopes of scopesInPreferenceOrder) {
const session = await getSession(authProviderId, scopes, { silent: true, account: preferredSession.account });
if (session) {
return { session, scopes };
}
}
return { session: preferredSession, scopes: [...preferredSession.scopes] };
}

const scopePreferences = [
{ scopes: SCOPES_OLD, broaderScopes: [SCOPES_WITH_ADDITIONAL] },
{ scopes: SCOPES_OLDEST, broaderScopes: [SCOPES_WITH_ADDITIONAL, SCOPES_OLD] },
Expand Down
44 changes: 39 additions & 5 deletions src/test/github/credentials.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,16 @@ describe('CredentialStore', function () {

const result = await findExistingSession(AuthProvider.github, async (_providerId, scopes, options) => {
requests.push({ scopes, accountId: options.account?.id });
if (options.account?.id === 'second') {
return undefined;
if (!scopes.length) {
return secondAccountDefault;
}
if (options.account?.id === 'second' && scopesEqual(scopes, defaultScopes)) {
return secondAccountDefault;
}
if (scopesEqual(scopes, defaultScopes)) {
return secondAccountDefault;
}
if (scopesEqual(scopes, additionalScopes)) {
if (!options.account && scopesEqual(scopes, additionalScopes)) {
return firstAccountAdditional;
}
return undefined;
Expand All @@ -67,7 +70,35 @@ describe('CredentialStore', function () {
strictEqual(result?.session, secondAccountDefault);
deepStrictEqual(result?.scopes, defaultScopes);
deepStrictEqual(requests, [
{ scopes: defaultScopes, accountId: undefined },
{ scopes: [], accountId: undefined },
{ scopes: additionalScopes, accountId: 'second' },
{ scopes: defaultScopes, accountId: 'second' },
]);
});

it('uses the preferred account when accounts have different scope sets', async function () {
const firstAccountDefault = createSession('first-default', 'first', defaultScopes);
const secondAccountAdditional = createSession('second-additional', 'second', additionalScopes);
const requests: { scopes: readonly string[], accountId?: string }[] = [];

const result = await findExistingSession(AuthProvider.github, async (_providerId, scopes, options) => {
requests.push({ scopes, accountId: options.account?.id });
if (!scopes.length) {
return secondAccountAdditional;
}
if (!options.account && scopesEqual(scopes, defaultScopes)) {
return firstAccountDefault;
}
if (options.account?.id === 'second' && scopesEqual(scopes, additionalScopes)) {
return secondAccountAdditional;
}
return undefined;
});

strictEqual(result?.session, secondAccountAdditional);
deepStrictEqual(result?.scopes, additionalScopes);
deepStrictEqual(requests, [
{ scopes: [], accountId: undefined },
{ scopes: additionalScopes, accountId: 'second' },
]);
});
Expand All @@ -77,6 +108,9 @@ describe('CredentialStore', function () {
const preferredAdditional = createSession('preferred-additional', 'preferred', additionalScopes);

const result = await findExistingSession(AuthProvider.github, async (_providerId, scopes, options) => {
if (!scopes.length) {
return preferredDefault;
}
if (options.account?.id === 'preferred' && scopesEqual(scopes, additionalScopes)) {
return preferredAdditional;
}
Expand Down Expand Up @@ -104,7 +138,7 @@ describe('CredentialStore', function () {

const additionalSession = createSession('additional', 'additional', additionalScopes);
const additionalResult = await findExistingSession(AuthProvider.github, async (_providerId, scopes, options) => {
if (!options.account && scopesEqual(scopes, additionalScopes)) {
if (!scopes.length || (options.account?.id === 'additional' && scopesEqual(scopes, additionalScopes))) {
return additionalSession;
}
return undefined;
Expand Down