issuer -> authorizationServer refactor (#250359)

* `issuer` -> `authorizationServer` refactor

Also:
* adds `authorizationServerGlobs` to the authentication contribution schema
* removes ugly MCP issuer hack and instead plumbs the authorizationServer down to the new auth providers
This commit is contained in:
Tyler James Leonhardt
2025-06-02 17:38:21 -07:00
committed by GitHub
parent a2ebac10a3
commit c235626145
26 changed files with 195 additions and 177 deletions
@@ -33,7 +33,7 @@
{
"label": "Microsoft",
"id": "microsoft",
"issuerGlobs": [
"authorizationServerGlobs": [
"https://login.microsoftonline.com/*/v2.0"
]
},
@@ -203,7 +203,7 @@ export class AzureActiveDirectoryService {
return this._sessionChangeEmitter.event;
}
public getSessions(scopes: string[] | undefined, { account, issuer }: vscode.AuthenticationProviderSessionOptions = {}): Promise<vscode.AuthenticationSession[]> {
public getSessions(scopes: string[] | undefined, { account, authorizationServer }: vscode.AuthenticationProviderSessionOptions = {}): Promise<vscode.AuthenticationSession[]> {
if (!scopes) {
this._logger.info('Getting sessions for all scopes...');
const sessions = this._tokens
@@ -226,8 +226,8 @@ export class AzureActiveDirectoryService {
if (!modifiedScopes.includes('offline_access')) {
modifiedScopes.push('offline_access');
}
if (issuer) {
const tenant = issuer.path.split('/')[1];
if (authorizationServer) {
const tenant = authorizationServer.path.split('/')[1];
if (tenant) {
modifiedScopes.push(`VSCODE_TENANT:${tenant}`);
}
@@ -303,7 +303,7 @@ export class AzureActiveDirectoryService {
.map(result => (result as PromiseFulfilledResult<vscode.AuthenticationSession>).value);
}
public createSession(scopes: string[], { account, issuer }: vscode.AuthenticationProviderSessionOptions = {}): Promise<vscode.AuthenticationSession> {
public createSession(scopes: string[], { account, authorizationServer }: vscode.AuthenticationProviderSessionOptions = {}): Promise<vscode.AuthenticationSession> {
let modifiedScopes = [...scopes];
if (!modifiedScopes.includes('openid')) {
modifiedScopes.push('openid');
@@ -317,8 +317,8 @@ export class AzureActiveDirectoryService {
if (!modifiedScopes.includes('offline_access')) {
modifiedScopes.push('offline_access');
}
if (issuer) {
const tenant = issuer.path.split('/')[1];
if (authorizationServer) {
const tenant = authorizationServer.path.split('/')[1];
if (tenant) {
modifiedScopes.push(`VSCODE_TENANT:${tenant}`);
}
@@ -45,14 +45,14 @@ export class ScopeData {
*/
readonly tenantId: string | undefined;
constructor(readonly originalScopes: readonly string[] = [], issuer?: Uri) {
constructor(readonly originalScopes: readonly string[] = [], authorizationServer?: 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, issuer);
this.tenant = this.getTenant(this.allScopes, authorizationServer);
this.tenantId = this.getTenantId(this.tenant);
}
@@ -65,10 +65,10 @@ export class ScopeData {
}, undefined) ?? DEFAULT_CLIENT_ID;
}
private getTenant(scopes: string[], issuer?: Uri): string {
if (issuer?.path) {
private getTenant(scopes: string[], authorizationServer?: Uri): string {
if (authorizationServer?.path) {
// Get tenant portion of URL
const tenant = issuer.path.split('/')[1];
const tenant = authorizationServer.path.split('/')[1];
if (tenant) {
return tenant;
}
@@ -75,21 +75,21 @@ suite('ScopeData', () => {
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);
test('should extract tenant from authorization server URL path', () => {
const authorizationServer = Uri.parse('https://login.microsoftonline.com/tenant123/oauth2/v2.0');
const scopeData = new ScopeData(['custom_scope'], authorizationServer);
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);
test('should fallback to default tenant if authorization server URL has no path segments', () => {
const authorizationServer = Uri.parse('https://login.microsoftonline.com');
const scopeData = new ScopeData(['custom_scope'], authorizationServer);
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);
test('should prioritize authorization server URL over VSCODE_TENANT scope', () => {
const authorizationServer = Uri.parse('https://login.microsoftonline.com/url_tenant/oauth2/v2.0');
const scopeData = new ScopeData(['custom_scope', 'VSCODE_TENANT:scope_tenant'], authorizationServer);
assert.strictEqual(scopeData.tenant, 'url_tenant');
});
});
@@ -170,7 +170,7 @@ export async function activate(context: vscode.ExtensionContext, telemetryReport
},
{
supportsMultipleAccounts: true,
supportedIssuers: [
supportedAuthorizationServers: [
vscode.Uri.parse('https://login.microsoftonline.com/*/v2.0')
]
}
@@ -81,7 +81,7 @@ export async function activate(context: ExtensionContext, mainTelemetryReporter:
authProvider,
{
supportsMultipleAccounts: true,
supportedIssuers: [
supportedAuthorizationServers: [
Uri.parse('https://login.microsoftonline.com/*/v2.0')
]
}
@@ -156,7 +156,7 @@ export class MsalAuthProvider implements AuthenticationProvider {
async getSessions(scopes: string[] | undefined, options: AuthenticationGetSessionOptions = {}): Promise<AuthenticationSession[]> {
const askingForAll = scopes === undefined;
const scopeData = new ScopeData(scopes, options?.issuer);
const scopeData = new ScopeData(scopes, options?.authorizationServer);
// Do NOT use `scopes` beyond this place in the code. Use `scopeData` instead.
this._logger.info('[getSessions]', askingForAll ? '[all]' : `[${scopeData.scopeStr}]`, 'starting');
@@ -186,7 +186,7 @@ export class MsalAuthProvider implements AuthenticationProvider {
}
async createSession(scopes: readonly string[], options: AuthenticationProviderSessionOptions): Promise<AuthenticationSession> {
const scopeData = new ScopeData(scopes, options.issuer);
const scopeData = new ScopeData(scopes, options.authorizationServer);
// Do NOT use `scopes` beyond this place in the code. Use `scopeData` instead.
this._logger.info('[createSession]', `[${scopeData.scopeStr}]`, 'starting');