Skip to content

Commit c13992c

Browse files
authored
add options for getting session, and don't require active token for selected tenant (#695)
1 parent 0414f5f commit c13992c

File tree

2 files changed

+34
-11
lines changed

2 files changed

+34
-11
lines changed

src/auth/azureSessionProvider.ts

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
AuthenticationGetSessionOptions,
88
AuthenticationSession,
99
} from "vscode";
10-
import { AzureAuthenticationSession, AzureSessionProvider, SignInStatus, Tenant } from "./types";
10+
import { AzureAuthenticationSession, AzureSessionProvider, GetAuthSessionOptions, SignInStatus, Tenant } from "./types";
1111
import { Errorable, bindAsync, getErrorMessage, map as errmap, succeeded } from "../commands/utils/errorable";
1212
import { getDefaultScope, quickPickTenant } from "./azureAuth";
1313
import { getConfiguredAzureEnv } from "../commands/utils/config";
@@ -119,7 +119,9 @@ class AzureSessionProviderImpl extends VsCodeDisposable implements AzureSessionP
119119
// This allows the user to sign in to the Microsoft provider and list tenants,
120120
// but the resulting session will not allow tenant-level operations. For that,
121121
// we need to get a session for a specific tenant.
122-
const getSessionResult = await this.getArmSession("organizations", authScenario);
122+
const orgTenantId = "organizations";
123+
const scopes = getScopes(orgTenantId, {});
124+
const getSessionResult = await this.getArmSession(orgTenantId, scopes, authScenario);
123125

124126
// Get the tenants
125127
const getTenantsResult = await bindAsync(getSessionResult, (session) => getTenants(session));
@@ -149,7 +151,7 @@ class AzureSessionProviderImpl extends VsCodeDisposable implements AzureSessionP
149151
* @returns The current Azure session, if available. If the user is not signed in, or there are no tenants,
150152
* an error message is returned.
151153
*/
152-
public async getAuthSession(): Promise<Errorable<AzureAuthenticationSession>> {
154+
public async getAuthSession(options?: GetAuthSessionOptions): Promise<Errorable<AzureAuthenticationSession>> {
153155
await this.initializePromise;
154156
if (this.signInStatusValue !== "SignedIn") {
155157
return { succeeded: false, error: `Not signed in (${this.signInStatusValue}).` };
@@ -173,7 +175,9 @@ class AzureSessionProviderImpl extends VsCodeDisposable implements AzureSessionP
173175
}
174176

175177
// Get a session for a specific tenant.
176-
return await this.getArmSession(this.selectedTenantValue.id, AuthScenario.GetSession);
178+
const tenantId = this.selectedTenantValue.id;
179+
const scopes = getScopes(tenantId, options || {});
180+
return await this.getArmSession(tenantId, scopes, AuthScenario.GetSession);
177181
}
178182

179183
private async getNewSelectedTenant(
@@ -202,22 +206,29 @@ class AzureSessionProviderImpl extends VsCodeDisposable implements AzureSessionP
202206
}
203207

204208
private async getDefaultTenantId(tenants: Tenant[]): Promise<Tenant | null> {
209+
if (tenants.length === 1) {
210+
return tenants[0];
211+
}
212+
213+
// It may be the case that the user has access to multiple tenants, but only has a valid token for one of them.
214+
// This might happen if the user has signed in to one recently, but not the others. In this case, we would want
215+
// to default to the tenant that the user has a valid token for.
205216
// Use the 'Initialization' scenario to ensure this is silent (no user interaction).
206-
const getSessionPromises = tenants.map((t) => this.getArmSession(t.id, AuthScenario.Initialization));
217+
const getSessionPromises = tenants.map((t) =>
218+
this.getArmSession(t.id, getScopes(t.id, {}), AuthScenario.Initialization),
219+
);
207220
const results = await Promise.all(getSessionPromises);
208221
const accessibleTenants = results.filter(succeeded).map((r) => r.result);
209222
return accessibleTenants.length === 1 ? findTenant(tenants, accessibleTenants[0].tenantId) : null;
210223
}
211224

212225
private async getArmSession(
213226
tenantId: string,
227+
scopes: string[],
214228
authScenario: AuthScenario,
215229
): Promise<Errorable<AzureAuthenticationSession>> {
216230
this.handleSessionChanges = false;
217231
try {
218-
const tenantScopes = tenantId ? [`VSCODE_TENANT:${tenantId}`] : [];
219-
const scopes = [getDefaultScope(getConfiguredAzureEnv().resourceManagerEndpointUrl), ...tenantScopes];
220-
221232
let options: AuthenticationGetSessionOptions;
222233
let silentFirst = false;
223234
switch (authScenario) {
@@ -263,6 +274,13 @@ function getConfiguredAuthProviderId(): AuthProviderId {
263274
return getConfiguredAzureEnv().name === Environment.AzureCloud.name ? "microsoft" : "microsoft-sovereign-cloud";
264275
}
265276

277+
function getScopes(tenantId: string | null, options: GetAuthSessionOptions): string[] {
278+
const defaultScopes = options.scopes || [getDefaultScope(getConfiguredAzureEnv().resourceManagerEndpointUrl)];
279+
const tenantScopes = tenantId ? [`VSCODE_TENANT:${tenantId}`] : [];
280+
const clientIdScopes = options.applicationClientId ? [`VSCODE_CLIENT_ID:${options.applicationClientId}`] : [];
281+
return [...defaultScopes, ...tenantScopes, ...clientIdScopes];
282+
}
283+
266284
async function getTenants(session: AuthenticationSession): Promise<Errorable<Tenant[]>> {
267285
const armEndpoint = getConfiguredAzureEnv().resourceManagerEndpointUrl;
268286
const credential: TokenCredential = {
@@ -273,14 +291,14 @@ async function getTenants(session: AuthenticationSession): Promise<Errorable<Ten
273291
const subscriptionClient = new SubscriptionClient(credential, { endpoint: armEndpoint });
274292

275293
const tenantsResult = await listAll(subscriptionClient.tenants.list());
276-
return errmap(tenantsResult, (t) => t.filter(asTenant).map((t) => ({ name: t.displayName, id: t.tenantId })));
294+
return errmap(tenantsResult, (t) => t.filter(isTenant).map((t) => ({ name: t.displayName, id: t.tenantId })));
277295
}
278296

279297
function findTenant(tenants: Tenant[], tenantId: string): Tenant | null {
280298
return tenants.find((t) => t.id === tenantId) || null;
281299
}
282300

283-
function asTenant(tenant: TenantIdDescription): tenant is { tenantId: string; displayName: string } {
301+
function isTenant(tenant: TenantIdDescription): tenant is { tenantId: string; displayName: string } {
284302
return tenant.tenantId !== undefined && tenant.displayName !== undefined;
285303
}
286304

src/auth/types.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,18 @@ export type Tenant = {
1717
id: string;
1818
};
1919

20+
export type GetAuthSessionOptions = {
21+
applicationClientId?: string;
22+
scopes?: string[];
23+
};
24+
2025
export type AzureSessionProvider = {
2126
signIn(): Promise<void>;
2227
signInStatus: SignInStatus;
2328
availableTenants: Tenant[];
2429
selectedTenant: Tenant | null;
2530
signInStatusChangeEvent: Event<SignInStatus>;
26-
getAuthSession(): Promise<Errorable<AzureAuthenticationSession>>;
31+
getAuthSession(options?: GetAuthSessionOptions): Promise<Errorable<AzureAuthenticationSession>>;
2732
dispose(): void;
2833
};
2934

0 commit comments

Comments
 (0)