7
7
AuthenticationGetSessionOptions ,
8
8
AuthenticationSession ,
9
9
} from "vscode" ;
10
- import { AzureAuthenticationSession , AzureSessionProvider , SignInStatus , Tenant } from "./types" ;
10
+ import { AzureAuthenticationSession , AzureSessionProvider , GetAuthSessionOptions , SignInStatus , Tenant } from "./types" ;
11
11
import { Errorable , bindAsync , getErrorMessage , map as errmap , succeeded } from "../commands/utils/errorable" ;
12
12
import { getDefaultScope , quickPickTenant } from "./azureAuth" ;
13
13
import { getConfiguredAzureEnv } from "../commands/utils/config" ;
@@ -119,7 +119,9 @@ class AzureSessionProviderImpl extends VsCodeDisposable implements AzureSessionP
119
119
// This allows the user to sign in to the Microsoft provider and list tenants,
120
120
// but the resulting session will not allow tenant-level operations. For that,
121
121
// 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 ) ;
123
125
124
126
// Get the tenants
125
127
const getTenantsResult = await bindAsync ( getSessionResult , ( session ) => getTenants ( session ) ) ;
@@ -149,7 +151,7 @@ class AzureSessionProviderImpl extends VsCodeDisposable implements AzureSessionP
149
151
* @returns The current Azure session, if available. If the user is not signed in, or there are no tenants,
150
152
* an error message is returned.
151
153
*/
152
- public async getAuthSession ( ) : Promise < Errorable < AzureAuthenticationSession > > {
154
+ public async getAuthSession ( options ?: GetAuthSessionOptions ) : Promise < Errorable < AzureAuthenticationSession > > {
153
155
await this . initializePromise ;
154
156
if ( this . signInStatusValue !== "SignedIn" ) {
155
157
return { succeeded : false , error : `Not signed in (${ this . signInStatusValue } ).` } ;
@@ -173,7 +175,9 @@ class AzureSessionProviderImpl extends VsCodeDisposable implements AzureSessionP
173
175
}
174
176
175
177
// 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 ) ;
177
181
}
178
182
179
183
private async getNewSelectedTenant (
@@ -202,22 +206,29 @@ class AzureSessionProviderImpl extends VsCodeDisposable implements AzureSessionP
202
206
}
203
207
204
208
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.
205
216
// 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
+ ) ;
207
220
const results = await Promise . all ( getSessionPromises ) ;
208
221
const accessibleTenants = results . filter ( succeeded ) . map ( ( r ) => r . result ) ;
209
222
return accessibleTenants . length === 1 ? findTenant ( tenants , accessibleTenants [ 0 ] . tenantId ) : null ;
210
223
}
211
224
212
225
private async getArmSession (
213
226
tenantId : string ,
227
+ scopes : string [ ] ,
214
228
authScenario : AuthScenario ,
215
229
) : Promise < Errorable < AzureAuthenticationSession > > {
216
230
this . handleSessionChanges = false ;
217
231
try {
218
- const tenantScopes = tenantId ? [ `VSCODE_TENANT:${ tenantId } ` ] : [ ] ;
219
- const scopes = [ getDefaultScope ( getConfiguredAzureEnv ( ) . resourceManagerEndpointUrl ) , ...tenantScopes ] ;
220
-
221
232
let options : AuthenticationGetSessionOptions ;
222
233
let silentFirst = false ;
223
234
switch ( authScenario ) {
@@ -263,6 +274,13 @@ function getConfiguredAuthProviderId(): AuthProviderId {
263
274
return getConfiguredAzureEnv ( ) . name === Environment . AzureCloud . name ? "microsoft" : "microsoft-sovereign-cloud" ;
264
275
}
265
276
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
+
266
284
async function getTenants ( session : AuthenticationSession ) : Promise < Errorable < Tenant [ ] > > {
267
285
const armEndpoint = getConfiguredAzureEnv ( ) . resourceManagerEndpointUrl ;
268
286
const credential : TokenCredential = {
@@ -273,14 +291,14 @@ async function getTenants(session: AuthenticationSession): Promise<Errorable<Ten
273
291
const subscriptionClient = new SubscriptionClient ( credential , { endpoint : armEndpoint } ) ;
274
292
275
293
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 } ) ) ) ;
277
295
}
278
296
279
297
function findTenant ( tenants : Tenant [ ] , tenantId : string ) : Tenant | null {
280
298
return tenants . find ( ( t ) => t . id === tenantId ) || null ;
281
299
}
282
300
283
- function asTenant ( tenant : TenantIdDescription ) : tenant is { tenantId : string ; displayName : string } {
301
+ function isTenant ( tenant : TenantIdDescription ) : tenant is { tenantId : string ; displayName : string } {
284
302
return tenant . tenantId !== undefined && tenant . displayName !== undefined ;
285
303
}
286
304
0 commit comments