Introduce Issuer handling in the Authentication stack (#248948)

Mostly plumbing... this enables:
```
vscode.authentication.getSession('microsoft', scopes, { issuer: "https://login.microsoftonline.com/common/v2.0" });
```
And the respective API for an auth providers to handle it being passed in.

This props up work in MCP land which needs a way to map an issuer to an auth provider... but I certainly see utility outside of that space.

Fixes https://github.com/microsoft/vscode/issues/248775#issuecomment-2876711396
This commit is contained in:
Tyler James Leonhardt
2025-05-14 14:02:15 -07:00
committed by GitHub
parent 1e03c9074c
commit 86efdcd2c1
22 changed files with 365 additions and 69 deletions

View File

@@ -3,6 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Uri } from 'vscode';
const DEFAULT_CLIENT_ID = 'aebc6443-996d-45c2-90f0-388ff96faa56';
const DEFAULT_TENANT = 'organizations';
@@ -43,14 +45,14 @@ export class ScopeData {
*/
readonly tenantId: string | undefined;
constructor(readonly originalScopes: readonly string[] = []) {
constructor(readonly originalScopes: readonly string[] = [], issuer?: Uri) {
const modifiedScopes = [...originalScopes];
modifiedScopes.sort();
this.allScopes = modifiedScopes;
this.scopeStr = modifiedScopes.join(' ');
this.scopesToSend = this.getScopesToSend(modifiedScopes);
this.clientId = this.getClientId(this.allScopes);
this.tenant = this.getTenant(this.allScopes);
this.tenant = this.getTenant(this.allScopes, issuer);
this.tenantId = this.getTenantId(this.tenant);
}
@@ -63,7 +65,14 @@ export class ScopeData {
}, undefined) ?? DEFAULT_CLIENT_ID;
}
private getTenant(scopes: string[]): string {
private getTenant(scopes: string[], issuer?: Uri): string {
if (issuer?.path) {
// Get tenant portion of URL
const tenant = issuer.path.split('/')[1];
if (tenant) {
return tenant;
}
}
return scopes.reduce<string | undefined>((prev, current) => {
if (current.startsWith('VSCODE_TENANT:')) {
return current.split('VSCODE_TENANT:')[1];

View File

@@ -5,6 +5,7 @@
import * as assert from 'assert';
import { ScopeData } from '../scopeData';
import { Uri } from 'vscode';
suite('ScopeData', () => {
test('should include default scopes if not present', () => {
@@ -73,4 +74,22 @@ suite('ScopeData', () => {
const scopeData = new ScopeData(['custom_scope', 'VSCODE_TENANT:some_guid']);
assert.strictEqual(scopeData.tenantId, 'some_guid');
});
test('should extract tenant from issuer URL path', () => {
const issuer = Uri.parse('https://login.microsoftonline.com/tenant123/oauth2/v2.0');
const scopeData = new ScopeData(['custom_scope'], issuer);
assert.strictEqual(scopeData.tenant, 'tenant123');
});
test('should fallback to default tenant if issuer URL has no path segments', () => {
const issuer = Uri.parse('https://login.microsoftonline.com');
const scopeData = new ScopeData(['custom_scope'], issuer);
assert.strictEqual(scopeData.tenant, 'organizations');
});
test('should prioritize issuer URL over VSCODE_TENANT scope', () => {
const issuer = Uri.parse('https://login.microsoftonline.com/url_tenant/oauth2/v2.0');
const scopeData = new ScopeData(['custom_scope', 'VSCODE_TENANT:scope_tenant'], issuer);
assert.strictEqual(scopeData.tenant, 'url_tenant');
});
});