@microsoft/vscode-azext-azureauth 5.1.0 → 5.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 5.1.1 - 2025-10-28
|
|
4
|
+
|
|
5
|
+
* [#2111](https://github.com/microsoft/vscode-azuretools/pull/2111) Same as https://github.com/microsoft/vscode-azuretools/pull/2110 but a better fix.
|
|
6
|
+
|
|
3
7
|
## 5.1.0 - 2025-10-27
|
|
4
8
|
|
|
5
9
|
* [#2102](https://github.com/microsoft/vscode-azuretools/pull/2102) Fixes an issue causing infinite event loops especially in https://vscode.dev/azure
|
|
@@ -52,73 +52,83 @@ let armSubs;
|
|
|
52
52
|
*/
|
|
53
53
|
class VSCodeAzureSubscriptionProvider {
|
|
54
54
|
logger;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
listenerDisposable;
|
|
56
|
+
onDidSignInEmitter = new vscode.EventEmitter();
|
|
57
|
+
onDidSignOutEmitter = new vscode.EventEmitter();
|
|
58
|
+
lastEventFired = 0;
|
|
59
|
+
suppressEvents = false;
|
|
58
60
|
priorAccounts;
|
|
61
|
+
accountsRemovedPromise;
|
|
59
62
|
// So that customers can easily share logs, try to only log PII using trace level
|
|
60
63
|
constructor(logger) {
|
|
61
64
|
this.logger = logger;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
void this.accountsRemoved(); // Initialize priorAccounts
|
|
66
|
+
}
|
|
67
|
+
dispose() {
|
|
68
|
+
this.listenerDisposable?.dispose();
|
|
69
|
+
this.onDidSignInEmitter.dispose();
|
|
70
|
+
this.onDidSignOutEmitter.dispose();
|
|
66
71
|
}
|
|
67
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
68
72
|
onDidSignIn(callback, thisArg, disposables) {
|
|
69
|
-
|
|
73
|
+
this.listenIfNeeded();
|
|
74
|
+
return this.onDidSignInEmitter.event(callback, thisArg, disposables);
|
|
70
75
|
}
|
|
71
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
72
76
|
onDidSignOut(callback, thisArg, disposables) {
|
|
73
|
-
|
|
77
|
+
this.listenIfNeeded();
|
|
78
|
+
return this.onDidSignOutEmitter.event(callback, thisArg, disposables);
|
|
74
79
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
this.priorAccounts = currentAccounts;
|
|
81
|
-
// The only way a sign out happens is if an account is removed entirely from the list of accounts
|
|
82
|
-
if (currentAccounts.length === 0 || currentAccounts.length < priorAccountCount) {
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
return true;
|
|
86
|
-
};
|
|
87
|
-
const wrappedCallback = () => {
|
|
88
|
-
const immediate = setImmediate(() => {
|
|
89
|
-
clearImmediate(immediate);
|
|
90
|
-
void callback.call(thisArg);
|
|
91
|
-
});
|
|
92
|
-
};
|
|
93
|
-
const disposable = vscode.authentication.onDidChangeSessions(async (e) => {
|
|
80
|
+
listenIfNeeded() {
|
|
81
|
+
if (this.listenerDisposable) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
this.listenerDisposable = vscode.authentication.onDidChangeSessions(async (e) => {
|
|
94
85
|
// Ignore any sign in that isn't for the configured auth provider
|
|
95
86
|
if (e.provider.id !== (0, configuredAzureEnv_1.getConfiguredAuthProviderId)()) {
|
|
96
87
|
return;
|
|
97
88
|
}
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
89
|
+
if (this.suppressEvents || Date.now() < this.lastEventFired + EventDebounce) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
this.lastEventFired = Date.now();
|
|
93
|
+
if (!await this.accountsRemoved()) {
|
|
94
|
+
this.logger?.debug('auth: Firing onDidSignIn event');
|
|
95
|
+
this.onDidSignInEmitter.fire();
|
|
106
96
|
}
|
|
107
97
|
else {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
else if (!await isASignInEvent()) {
|
|
112
|
-
this.lastSignOutEventFired = Date.now();
|
|
113
|
-
wrappedCallback();
|
|
114
|
-
}
|
|
98
|
+
this.logger?.debug('auth: Firing onDidSignOut event');
|
|
99
|
+
this.onDidSignOutEmitter.fire();
|
|
115
100
|
}
|
|
116
101
|
});
|
|
117
|
-
disposables?.push(disposable);
|
|
118
|
-
return disposable;
|
|
119
102
|
}
|
|
120
|
-
|
|
121
|
-
//
|
|
103
|
+
async accountsRemoved() {
|
|
104
|
+
// If there's already an ongoing accountsRemoved operation, return its result
|
|
105
|
+
if (this.accountsRemovedPromise) {
|
|
106
|
+
return this.accountsRemovedPromise;
|
|
107
|
+
}
|
|
108
|
+
// Create a new promise for this operation
|
|
109
|
+
this.accountsRemovedPromise = (async () => {
|
|
110
|
+
try {
|
|
111
|
+
this.suppressEvents = true;
|
|
112
|
+
const currentAccounts = Array.from(await vscode.authentication.getAccounts((0, configuredAzureEnv_1.getConfiguredAuthProviderId)()));
|
|
113
|
+
const priorAccountCount = this.priorAccounts?.length ?? 0;
|
|
114
|
+
this.priorAccounts = currentAccounts;
|
|
115
|
+
// The only way a sign out happens is if an account is removed entirely from the list of accounts
|
|
116
|
+
if (currentAccounts.length === 0 || currentAccounts.length < priorAccountCount) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
this.suppressEvents = false;
|
|
123
|
+
}
|
|
124
|
+
})();
|
|
125
|
+
try {
|
|
126
|
+
return await this.accountsRemovedPromise;
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
// Clear the promise when done so future calls can proceed
|
|
130
|
+
this.accountsRemovedPromise = undefined;
|
|
131
|
+
}
|
|
122
132
|
}
|
|
123
133
|
/**
|
|
124
134
|
* Gets a list of tenants available to the user.
|
|
@@ -179,7 +189,7 @@ class VSCodeAzureSubscriptionProvider {
|
|
|
179
189
|
const allSubscriptions = [];
|
|
180
190
|
let accountCount; // only used for logging
|
|
181
191
|
try {
|
|
182
|
-
this.
|
|
192
|
+
this.suppressEvents = true;
|
|
183
193
|
// Get the list of tenants from each account (filtered or all)
|
|
184
194
|
const accounts = (0, isGetSubscriptionsFilter_1.isGetSubscriptionsAccountFilter)(filter) ? [filter.account] : await vscode.authentication.getAccounts((0, configuredAzureEnv_1.getConfiguredAuthProviderId)());
|
|
185
195
|
accountCount = accounts.length;
|
|
@@ -197,7 +207,7 @@ class VSCodeAzureSubscriptionProvider {
|
|
|
197
207
|
}
|
|
198
208
|
}
|
|
199
209
|
finally {
|
|
200
|
-
this.
|
|
210
|
+
this.suppressEvents = false;
|
|
201
211
|
}
|
|
202
212
|
// It's possible that by listing subscriptions in all tenants and the "home" tenant there could be duplicate subscriptions
|
|
203
213
|
// Thus, we remove duplicate subscriptions. However, if multiple accounts have the same subscription, we keep them.
|
|
@@ -260,7 +270,7 @@ class VSCodeAzureSubscriptionProvider {
|
|
|
260
270
|
async signIn(tenantId, account) {
|
|
261
271
|
this.logger?.debug(`auth: Signing in (account="${account?.label ?? 'none'}") (tenantId="${tenantId ?? 'none'}")`);
|
|
262
272
|
try {
|
|
263
|
-
this.
|
|
273
|
+
this.suppressEvents = true;
|
|
264
274
|
const session = await (0, getSessionFromVSCode_1.getSessionFromVSCode)([], tenantId, {
|
|
265
275
|
createIfNone: true,
|
|
266
276
|
// If no account is provided, then clear the session preference which tells VS Code to show the account picker
|
|
@@ -270,7 +280,7 @@ class VSCodeAzureSubscriptionProvider {
|
|
|
270
280
|
return !!session;
|
|
271
281
|
}
|
|
272
282
|
finally {
|
|
273
|
-
this.
|
|
283
|
+
this.suppressEvents = false;
|
|
274
284
|
}
|
|
275
285
|
}
|
|
276
286
|
/**
|
|
@@ -8,15 +8,19 @@ import type { AzureTenant } from './AzureTenant';
|
|
|
8
8
|
*/
|
|
9
9
|
export declare class VSCodeAzureSubscriptionProvider implements AzureSubscriptionProvider, vscode.Disposable {
|
|
10
10
|
private readonly logger?;
|
|
11
|
-
private
|
|
12
|
-
private
|
|
13
|
-
private
|
|
11
|
+
private listenerDisposable;
|
|
12
|
+
private readonly onDidSignInEmitter;
|
|
13
|
+
private readonly onDidSignOutEmitter;
|
|
14
|
+
private lastEventFired;
|
|
15
|
+
private suppressEvents;
|
|
14
16
|
private priorAccounts;
|
|
17
|
+
private accountsRemovedPromise;
|
|
15
18
|
constructor(logger?: vscode.LogOutputChannel | undefined);
|
|
16
|
-
onDidSignIn(callback: () => any, thisArg?: any, disposables?: vscode.Disposable[]): vscode.Disposable;
|
|
17
|
-
onDidSignOut(callback: () => any, thisArg?: any, disposables?: vscode.Disposable[]): vscode.Disposable;
|
|
18
|
-
private onDidChangeSessions;
|
|
19
19
|
dispose(): void;
|
|
20
|
+
onDidSignIn(callback: () => unknown, thisArg?: unknown, disposables?: vscode.Disposable[]): vscode.Disposable;
|
|
21
|
+
onDidSignOut(callback: () => unknown, thisArg?: unknown, disposables?: vscode.Disposable[]): vscode.Disposable;
|
|
22
|
+
private listenIfNeeded;
|
|
23
|
+
private accountsRemoved;
|
|
20
24
|
/**
|
|
21
25
|
* Gets a list of tenants available to the user.
|
|
22
26
|
* Use {@link isSignedIn} to check if the user is signed in to a particular tenant.
|
|
@@ -16,73 +16,83 @@ let armSubs;
|
|
|
16
16
|
*/
|
|
17
17
|
export class VSCodeAzureSubscriptionProvider {
|
|
18
18
|
logger;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
listenerDisposable;
|
|
20
|
+
onDidSignInEmitter = new vscode.EventEmitter();
|
|
21
|
+
onDidSignOutEmitter = new vscode.EventEmitter();
|
|
22
|
+
lastEventFired = 0;
|
|
23
|
+
suppressEvents = false;
|
|
22
24
|
priorAccounts;
|
|
25
|
+
accountsRemovedPromise;
|
|
23
26
|
// So that customers can easily share logs, try to only log PII using trace level
|
|
24
27
|
constructor(logger) {
|
|
25
28
|
this.logger = logger;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
void this.accountsRemoved(); // Initialize priorAccounts
|
|
30
|
+
}
|
|
31
|
+
dispose() {
|
|
32
|
+
this.listenerDisposable?.dispose();
|
|
33
|
+
this.onDidSignInEmitter.dispose();
|
|
34
|
+
this.onDidSignOutEmitter.dispose();
|
|
30
35
|
}
|
|
31
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
36
|
onDidSignIn(callback, thisArg, disposables) {
|
|
33
|
-
|
|
37
|
+
this.listenIfNeeded();
|
|
38
|
+
return this.onDidSignInEmitter.event(callback, thisArg, disposables);
|
|
34
39
|
}
|
|
35
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
40
|
onDidSignOut(callback, thisArg, disposables) {
|
|
37
|
-
|
|
41
|
+
this.listenIfNeeded();
|
|
42
|
+
return this.onDidSignOutEmitter.event(callback, thisArg, disposables);
|
|
38
43
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
this.priorAccounts = currentAccounts;
|
|
45
|
-
// The only way a sign out happens is if an account is removed entirely from the list of accounts
|
|
46
|
-
if (currentAccounts.length === 0 || currentAccounts.length < priorAccountCount) {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
return true;
|
|
50
|
-
};
|
|
51
|
-
const wrappedCallback = () => {
|
|
52
|
-
const immediate = setImmediate(() => {
|
|
53
|
-
clearImmediate(immediate);
|
|
54
|
-
void callback.call(thisArg);
|
|
55
|
-
});
|
|
56
|
-
};
|
|
57
|
-
const disposable = vscode.authentication.onDidChangeSessions(async (e) => {
|
|
44
|
+
listenIfNeeded() {
|
|
45
|
+
if (this.listenerDisposable) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
this.listenerDisposable = vscode.authentication.onDidChangeSessions(async (e) => {
|
|
58
49
|
// Ignore any sign in that isn't for the configured auth provider
|
|
59
50
|
if (e.provider.id !== getConfiguredAuthProviderId()) {
|
|
60
51
|
return;
|
|
61
52
|
}
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
53
|
+
if (this.suppressEvents || Date.now() < this.lastEventFired + EventDebounce) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
this.lastEventFired = Date.now();
|
|
57
|
+
if (!await this.accountsRemoved()) {
|
|
58
|
+
this.logger?.debug('auth: Firing onDidSignIn event');
|
|
59
|
+
this.onDidSignInEmitter.fire();
|
|
70
60
|
}
|
|
71
61
|
else {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
else if (!await isASignInEvent()) {
|
|
76
|
-
this.lastSignOutEventFired = Date.now();
|
|
77
|
-
wrappedCallback();
|
|
78
|
-
}
|
|
62
|
+
this.logger?.debug('auth: Firing onDidSignOut event');
|
|
63
|
+
this.onDidSignOutEmitter.fire();
|
|
79
64
|
}
|
|
80
65
|
});
|
|
81
|
-
disposables?.push(disposable);
|
|
82
|
-
return disposable;
|
|
83
66
|
}
|
|
84
|
-
|
|
85
|
-
//
|
|
67
|
+
async accountsRemoved() {
|
|
68
|
+
// If there's already an ongoing accountsRemoved operation, return its result
|
|
69
|
+
if (this.accountsRemovedPromise) {
|
|
70
|
+
return this.accountsRemovedPromise;
|
|
71
|
+
}
|
|
72
|
+
// Create a new promise for this operation
|
|
73
|
+
this.accountsRemovedPromise = (async () => {
|
|
74
|
+
try {
|
|
75
|
+
this.suppressEvents = true;
|
|
76
|
+
const currentAccounts = Array.from(await vscode.authentication.getAccounts(getConfiguredAuthProviderId()));
|
|
77
|
+
const priorAccountCount = this.priorAccounts?.length ?? 0;
|
|
78
|
+
this.priorAccounts = currentAccounts;
|
|
79
|
+
// The only way a sign out happens is if an account is removed entirely from the list of accounts
|
|
80
|
+
if (currentAccounts.length === 0 || currentAccounts.length < priorAccountCount) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
this.suppressEvents = false;
|
|
87
|
+
}
|
|
88
|
+
})();
|
|
89
|
+
try {
|
|
90
|
+
return await this.accountsRemovedPromise;
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
// Clear the promise when done so future calls can proceed
|
|
94
|
+
this.accountsRemovedPromise = undefined;
|
|
95
|
+
}
|
|
86
96
|
}
|
|
87
97
|
/**
|
|
88
98
|
* Gets a list of tenants available to the user.
|
|
@@ -143,7 +153,7 @@ export class VSCodeAzureSubscriptionProvider {
|
|
|
143
153
|
const allSubscriptions = [];
|
|
144
154
|
let accountCount; // only used for logging
|
|
145
155
|
try {
|
|
146
|
-
this.
|
|
156
|
+
this.suppressEvents = true;
|
|
147
157
|
// Get the list of tenants from each account (filtered or all)
|
|
148
158
|
const accounts = isGetSubscriptionsAccountFilter(filter) ? [filter.account] : await vscode.authentication.getAccounts(getConfiguredAuthProviderId());
|
|
149
159
|
accountCount = accounts.length;
|
|
@@ -161,7 +171,7 @@ export class VSCodeAzureSubscriptionProvider {
|
|
|
161
171
|
}
|
|
162
172
|
}
|
|
163
173
|
finally {
|
|
164
|
-
this.
|
|
174
|
+
this.suppressEvents = false;
|
|
165
175
|
}
|
|
166
176
|
// It's possible that by listing subscriptions in all tenants and the "home" tenant there could be duplicate subscriptions
|
|
167
177
|
// Thus, we remove duplicate subscriptions. However, if multiple accounts have the same subscription, we keep them.
|
|
@@ -224,7 +234,7 @@ export class VSCodeAzureSubscriptionProvider {
|
|
|
224
234
|
async signIn(tenantId, account) {
|
|
225
235
|
this.logger?.debug(`auth: Signing in (account="${account?.label ?? 'none'}") (tenantId="${tenantId ?? 'none'}")`);
|
|
226
236
|
try {
|
|
227
|
-
this.
|
|
237
|
+
this.suppressEvents = true;
|
|
228
238
|
const session = await getSessionFromVSCode([], tenantId, {
|
|
229
239
|
createIfNone: true,
|
|
230
240
|
// If no account is provided, then clear the session preference which tells VS Code to show the account picker
|
|
@@ -234,7 +244,7 @@ export class VSCodeAzureSubscriptionProvider {
|
|
|
234
244
|
return !!session;
|
|
235
245
|
}
|
|
236
246
|
finally {
|
|
237
|
-
this.
|
|
247
|
+
this.suppressEvents = false;
|
|
238
248
|
}
|
|
239
249
|
}
|
|
240
250
|
/**
|