Skip to content

Commit e8c378d

Browse files
amitjoshi438Amit JoshiCopilot
authored
Fix CES survey auth to be non-blocking (#1527)
Suppress blocking CES survey auth failures so extension integration and feedback paths continue when CES service principal is disabled. Co-authored-by: Amit Joshi <amitjoshi@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent dd7729a commit e8c378d

4 files changed

Lines changed: 70 additions & 19 deletions

File tree

src/common/copilot/user-feedback/CESSurvey.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ export async function CESUserFeedback(context: vscode.ExtensionContext, sessionI
2424
feedbackPanel.dispose();
2525
}
2626

27+
const apiToken: string = await npsAuthentication(SurveyConstants.AUTHORIZATION_ENDPOINT, true);
28+
29+
if (!apiToken) {
30+
sendTelemetryEvent({ eventName: CopilotUserFeedbackFailureEvent, feedbackType: thumbType, copilotSessionId: sessionId, error: new Error(ERROR_CONSTANTS.NPS_FAILED_AUTH) });
31+
return;
32+
}
33+
34+
sendTelemetryEvent({ eventName: CopilotNpsAuthenticationCompleted, feedbackType: thumbType, copilotSessionId: sessionId });
35+
2736
feedbackPanel = createFeedbackPanel(context);
2837

2938
feedbackPanel.webview.postMessage({ type: "thumbType", value: thumbType });
@@ -36,14 +45,6 @@ export async function CESUserFeedback(context: vscode.ExtensionContext, sessionI
3645

3746
const feedbackData = initializeFeedbackData(sessionId, vscode.env.uiKind === vscode.UIKind.Web, geoName, messageScenario, tenantId);
3847

39-
const apiToken: string = await npsAuthentication(SurveyConstants.AUTHORIZATION_ENDPOINT);
40-
41-
if (apiToken) {
42-
sendTelemetryEvent({ eventName: CopilotNpsAuthenticationCompleted, feedbackType: thumbType, copilotSessionId: sessionId });
43-
} else {
44-
sendTelemetryEvent({ eventName: CopilotUserFeedbackFailureEvent, feedbackType: thumbType, copilotSessionId: sessionId, error: new Error(ERROR_CONSTANTS.NPS_FAILED_AUTH) });
45-
}
46-
4748
const endpointUrl = useEUEndpoint(geoName) ? `https://europe.ces.microsoftcloud.com/api/v1/portalsdesigner/Surveys/powerpageschatgpt/Feedbacks?userId=${userID}` :
4849
`https://world.ces.microsoftcloud.com/api/v1/portalsdesigner/Surveys/powerpageschatgpt/Feedbacks?userId=${userID}`;
4950

src/common/services/AuthenticationProvider.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ export async function dataverseAuthentication(
172172
}
173173

174174
export async function npsAuthentication(
175-
cesSurveyAuthorizationEndpoint: string
175+
cesSurveyAuthorizationEndpoint: string,
176+
silentOnly = false
176177
): Promise<string> {
177178
let accessToken = "";
178179
sendTelemetryEvent(
@@ -184,7 +185,7 @@ export async function npsAuthentication(
184185
[cesSurveyAuthorizationEndpoint, SCOPE_OPTION_OFFLINE_ACCESS],
185186
{ silent: true }
186187
);
187-
if (!session) {
188+
if (!session && !silentOnly) {
188189
session = await vscode.authentication.getSession(
189190
PROVIDER_ID,
190191
[cesSurveyAuthorizationEndpoint, SCOPE_OPTION_OFFLINE_ACCESS],
@@ -199,16 +200,20 @@ export async function npsAuthentication(
199200
{ eventName: VSCODE_EXTENSION_NPS_AUTHENTICATION_COMPLETED }
200201
);
201202
} catch (error) {
202-
showErrorDialog(
203-
vscode.l10n.t(
204-
"Authorization Failed. Please run again to authorize it"
205-
),
206-
vscode.l10n.t("There was a permissions problem with the server")
207-
);
203+
const errorMessage = (error as Error).message ?? "";
204+
const isCesSurveyServicePrincipalDisabledError = errorMessage.includes("AADSTS500014");
205+
if (!silentOnly && !isCesSurveyServicePrincipalDisabledError) {
206+
showErrorDialog(
207+
vscode.l10n.t(
208+
"Authorization Failed. Please run again to authorize it"
209+
),
210+
vscode.l10n.t("There was a permissions problem with the server")
211+
);
212+
}
208213
sendTelemetryEvent(
209214
{
210215
eventName: VSCODE_EXTENSION_NPS_AUTHENTICATION_FAILED,
211-
errorMsg: (error as Error).message
216+
errorMsg: errorMessage
212217
}
213218
);
214219
}

src/web/client/services/NPSService.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,17 @@ export class NPSService {
6666
try {
6767

6868
const baseApiUrl = this.getNpsSurveyEndpoint();
69-
const accessToken: string = await npsAuthentication(SurveyConstants.AUTHORIZATION_ENDPOINT);
69+
const accessToken: string = await npsAuthentication(SurveyConstants.AUTHORIZATION_ENDPOINT, true);
7070

7171
if (accessToken) {
7272
WebExtensionContext.telemetry.sendInfoTelemetry(webExtensionTelemetryEventNames.WEB_EXTENSION_NPS_AUTHENTICATION_COMPLETED);
73+
} else {
74+
WebExtensionContext.telemetry.sendErrorTelemetry(
75+
webExtensionTelemetryEventNames.WEB_EXTENSION_NPS_AUTHENTICATION_FAILED,
76+
this.setEligibility.name,
77+
"CES survey authentication failed or unavailable"
78+
);
79+
return;
7380
}
7481

7582
// eslint-disable-next-line @typescript-eslint/no-explicit-any

src/web/client/test/integration/AuthenticationProvider.test.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ import { expect } from "chai";
88
import {
99
dataverseAuthentication,
1010
getCommonHeaders,
11+
npsAuthentication,
1112
} from "../../../../common/services/AuthenticationProvider";
1213
import vscode from "vscode";
1314
import * as errorHandler from "../../../../common/utilities/errorHandlerUtil";
1415
import { oneDSLoggerWrapper } from "../../../../common/OneDSLoggerTelemetry/oneDSLoggerWrapper";
1516
import * as copilotTelemetry from "../../../../common/copilot/telemetry/copilotTelemetry";
16-
import { VSCODE_EXTENSION_DATAVERSE_AUTHENTICATION_FAILED } from "../../../../common/services/TelemetryConstants";
17+
import { VSCODE_EXTENSION_DATAVERSE_AUTHENTICATION_FAILED, VSCODE_EXTENSION_NPS_AUTHENTICATION_FAILED } from "../../../../common/services/TelemetryConstants";
1718

1819
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1920
let traceError: any
@@ -122,4 +123,41 @@ describe("Authentication Provider", () => {
122123
expect(result.accessToken).empty;
123124
expect(result.userId).empty;
124125
});
126+
127+
it("npsAuthentication_whenSilentOnlyAndNoSession_shouldNotShowErrorDialog", async () => {
128+
const _mockgetSession = sinon
129+
.stub(await vscode.authentication, "getSession")
130+
.resolves(undefined);
131+
132+
const showErrorDialog = sinon.spy(errorHandler, "showErrorDialog");
133+
const sendTelemetryEvent = sinon.spy(copilotTelemetry, "sendTelemetryEvent");
134+
135+
const result = await npsAuthentication("https://microsoft.onmicrosoft.com/cessurvey/user", true);
136+
137+
sinon.assert.calledOnce(_mockgetSession);
138+
sinon.assert.notCalled(showErrorDialog);
139+
sinon.assert.calledTwice(sendTelemetryEvent);
140+
expect(result).empty;
141+
});
142+
143+
it("npsAuthentication_whenAadsts500014_shouldSuppressErrorDialog", async () => {
144+
const errorMessage = "AADSTS500014: The service principal for resource 'https://microsoft.onmicrosoft.com/cessurvey' is disabled.";
145+
const _mockgetSession = sinon
146+
.stub(await vscode.authentication, "getSession")
147+
.throws(new Error(errorMessage));
148+
149+
const showErrorDialog = sinon.spy(errorHandler, "showErrorDialog");
150+
const sendTelemetryEvent = sinon.spy(copilotTelemetry, "sendTelemetryEvent");
151+
152+
const result = await npsAuthentication("https://microsoft.onmicrosoft.com/cessurvey/user");
153+
154+
sinon.assert.calledOnce(_mockgetSession);
155+
sinon.assert.notCalled(showErrorDialog);
156+
sinon.assert.calledTwice(sendTelemetryEvent);
157+
sinon.assert.calledWith(sendTelemetryEvent, {
158+
eventName: VSCODE_EXTENSION_NPS_AUTHENTICATION_FAILED,
159+
errorMsg: errorMessage
160+
});
161+
expect(result).empty;
162+
});
125163
});

0 commit comments

Comments
 (0)