AzureAD / microsoft-authentication-library-for-js

Microsoft Authentication Library (MSAL) for JS
http://aka.ms/aadv2
MIT License
3.61k stars 2.64k forks source link

Outlook add-in: Nested App Authentication does not work when third-party cookies are disabled in the browser #7280

Open adrianhrinko opened 2 weeks ago

adrianhrinko commented 2 weeks ago

Core Library

MSAL.js (@azure/msal-browser)

Core Library Version

3.21.0

Wrapper Library

Not Applicable

Wrapper Library Version

None

Public or Confidential Client?

Public

Description

Our Motivation for Using MSAL-Browser:

We encountered issues with SSO token retrieval using the OBO flow due to new third-party cookie restrictions in Safari, Firefox, and, most recently, in Chrome's incognito mode. As it is essential for our Outlook add-in to securely retrieve the SSO token, we decided to implement token retrieval using Nested App Authentication, even though it is currently marked as a preview feature. Our decision was based on information indicating that this approach should resolve the issue (source 1, source 2).

The Problem:

After implementing the solution according to the documentation, we encountered errors during both silent and interactive token retrieval, as shown below. We primarily need to acquire the token within the onMessageSend handler when an email is being sent. However, we also attempted to acquire the token within the task pane (as we are aware of potential issues with popups in event-executed code) and encountered the same results. Our add-in’s JavaScript files are hosted on the Azure CDN, where we have configured the CORS headers as recommended in this issue, but this did not resolve the problem.

Important Facts:

Questions:

Error Message

We get this error while trying to get token silently:

Unable to acquire NAA token silently: InteractionRequiredAuthError: login_required: AADSTS50058: A silent sign-in request was sent but no user is signed in. The cookies used to represent the user's session were not sent in the request to Azure AD. This can happen if the user is using Internet Explorer or Edge, and the web app sending the silent sign-in request is in different IE security zone than the Azure AD endpoint (login.microsoftonline.com). Trace ID: 025d5ec5-ee76-4c1a-a30c-09e4285a7600 Correlation ID: 01919457-542e-7b2f-a69a-a691c421d70b Timestamp: 2024-08-27 14:57:17Z
customLogMethod @ logger.ts:41
error @ logger.ts:139
_callee4$ @ ssoTokenHelper.ts:96
tryCatch @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
asyncGeneratorStep @ ssoTokenHelper.ts:2
_throw @ ssoTokenHelper.ts:2
Promise.then
asyncGeneratorStep @ ssoTokenHelper.ts:2
_next @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
_tryAcquireTokenSilent @ ssoTokenHelper.ts:98
tryAcquireTokenSilent @ ssoTokenHelper.ts:90
_callee3$ @ ssoTokenHelper.ts:81
tryCatch @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
asyncGeneratorStep @ ssoTokenHelper.ts:2
_next @ ssoTokenHelper.ts:2
Promise.then
asyncGeneratorStep @ ssoTokenHelper.ts:2
_next @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
_getSsoTokenFromNAA @ ssoTokenHelper.ts:88
getSsoTokenFromNAA @ ssoTokenHelper.ts:38
_callee2$ @ ssoTokenHelper.ts:24
tryCatch @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
asyncGeneratorStep @ ssoTokenHelper.ts:2
_next @ ssoTokenHelper.ts:2
Promise.then
asyncGeneratorStep @ ssoTokenHelper.ts:2
_next @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
_getSsoToken @ ssoTokenHelper.ts:36
getSsoToken @ ssoTokenHelper.ts:16
_callee$ @ apiHelper.ts:18
tryCatch @ apiHelper.ts:2
(anonymous) @ apiHelper.ts:2
(anonymous) @ apiHelper.ts:2
asyncGeneratorStep @ apiHelper.ts:2
_next @ apiHelper.ts:2
(anonymous) @ apiHelper.ts:2
(anonymous) @ apiHelper.ts:2
sendApiRequest @ apiHelper.ts:9
_callee$ @ emailHandling.ts:81
tryCatch @ emailHandling.ts:2
(anonymous) @ emailHandling.ts:2
(anonymous) @ emailHandling.ts:2
asyncGeneratorStep @ emailHandling.ts:2
_next @ emailHandling.ts:2
(anonymous) @ emailHandling.ts:2
(anonymous) @ emailHandling.ts:2
_evaluateMessage @ emailHandling.ts:93
evaluateMessage @ emailHandling.ts:80
_callee$ @ events.ts:54
tryCatch @ events.ts:2
(anonymous) @ events.ts:2
(anonymous) @ events.ts:2
asyncGeneratorStep @ events.ts:2
_next @ events.ts:2
Promise.then
asyncGeneratorStep @ events.ts:2
_next @ events.ts:2
(anonymous) @ events.ts:2
(anonymous) @ events.ts:2
_onItemSendHandlerInner @ events.ts:73
onItemSendHandlerInner @ events.ts:44
_callee8$ @ events.ts:254
tryCatch @ events.ts:2
(anonymous) @ events.ts:2
(anonymous) @ events.ts:2
asyncGeneratorStep @ events.ts:2
_next @ events.ts:2
Promise.then
asyncGeneratorStep @ events.ts:2
_next @ events.ts:2
Promise.then
asyncGeneratorStep @ events.ts:2
_next @ events.ts:2
Promise.then
asyncGeneratorStep @ events.ts:2
_next @ events.ts:2
(anonymous) @ events.ts:2
(anonymous) @ events.ts:2
_onItemSendHandler @ events.ts:258
onItemSendHandler @ events.ts:195
(anonymous) @ outlook-web-16.01.js:20
setTimeout
a._processAppCommandInvocation @ outlook-web-16.01.js:20
fireEvent @ outlook-web-16.01.js:20
onEvent @ outlook-web-16.01.js:20
(anonymous) @ outlook-web-16.01.js:20
M @ outlook-web-16.01.js:20

And then, when we fallback to get the token interactively, we get:

Unable to acquire NAA token interactively: ServerError: user_cancelled: User cancelled the flow.
customLogMethod @ logger.ts:41
error @ logger.ts:139
_callee5$ @ ssoTokenHelper.ts:105
tryCatch @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
asyncGeneratorStep @ ssoTokenHelper.ts:2
_throw @ ssoTokenHelper.ts:2
Promise.then
asyncGeneratorStep @ ssoTokenHelper.ts:2
_next @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
_tryAcquireTokenPopup @ ssoTokenHelper.ts:107
tryAcquireTokenPopup @ ssoTokenHelper.ts:100
_callee3$ @ ssoTokenHelper.ts:87
tryCatch @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
asyncGeneratorStep @ ssoTokenHelper.ts:2
_next @ ssoTokenHelper.ts:2
Promise.then
asyncGeneratorStep @ ssoTokenHelper.ts:2
_next @ ssoTokenHelper.ts:2
Promise.then
asyncGeneratorStep @ ssoTokenHelper.ts:2
_next @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
_getSsoTokenFromNAA @ ssoTokenHelper.ts:88
getSsoTokenFromNAA @ ssoTokenHelper.ts:38
_callee2$ @ ssoTokenHelper.ts:24
tryCatch @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
asyncGeneratorStep @ ssoTokenHelper.ts:2
_next @ ssoTokenHelper.ts:2
Promise.then
asyncGeneratorStep @ ssoTokenHelper.ts:2
_next @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
_getSsoToken @ ssoTokenHelper.ts:36
getSsoToken @ ssoTokenHelper.ts:16
_callee$ @ apiHelper.ts:18
tryCatch @ apiHelper.ts:2
(anonymous) @ apiHelper.ts:2
(anonymous) @ apiHelper.ts:2
asyncGeneratorStep @ apiHelper.ts:2
_next @ apiHelper.ts:2
(anonymous) @ apiHelper.ts:2
(anonymous) @ apiHelper.ts:2
sendApiRequest @ apiHelper.ts:9
_callee$ @ emailHandling.ts:81
tryCatch @ emailHandling.ts:2
(anonymous) @ emailHandling.ts:2
(anonymous) @ emailHandling.ts:2
asyncGeneratorStep @ emailHandling.ts:2
_next @ emailHandling.ts:2
(anonymous) @ emailHandling.ts:2
(anonymous) @ emailHandling.ts:2
_evaluateMessage @ emailHandling.ts:93
evaluateMessage @ emailHandling.ts:80
_callee$ @ events.ts:54
tryCatch @ events.ts:2
(anonymous) @ events.ts:2
(anonymous) @ events.ts:2
asyncGeneratorStep @ events.ts:2
_next @ events.ts:2
Promise.then
asyncGeneratorStep @ events.ts:2
_next @ events.ts:2
(anonymous) @ events.ts:2
(anonymous) @ events.ts:2
_onItemSendHandlerInner @ events.ts:73
onItemSendHandlerInner @ events.ts:44
_callee8$ @ events.ts:254
tryCatch @ events.ts:2
(anonymous) @ events.ts:2
(anonymous) @ events.ts:2
asyncGeneratorStep @ events.ts:2
_next @ events.ts:2
Promise.then
asyncGeneratorStep @ events.ts:2
_next @ events.ts:2
Promise.then
asyncGeneratorStep @ events.ts:2
_next @ events.ts:2
Promise.then
asyncGeneratorStep @ events.ts:2
_next @ events.ts:2
(anonymous) @ events.ts:2
(anonymous) @ events.ts:2
_onItemSendHandler @ events.ts:258
onItemSendHandler @ events.ts:195
(anonymous) @ outlook-web-16.01.js:20
setTimeout
a._processAppCommandInvocation @ outlook-web-16.01.js:20
fireEvent @ outlook-web-16.01.js:20
onEvent @ outlook-web-16.01.js:20
(anonymous) @ outlook-web-16.01.js:20
M @ outlook-web-16.01.js:20
events_beta.js:1916 [Safetica]: Send handler failed with error TimeoutError: Operation timed out
customLogMethod @ logger.ts:41
error @ logger.ts:139
_callee6$ @ events.ts:126
tryCatch @ events.ts:2
(anonymous) @ events.ts:2
(anonymous) @ events.ts:2
asyncGeneratorStep @ events.ts:2
_next @ events.ts:2
(anonymous) @ events.ts:2
(anonymous) @ events.ts:2
_handleSendError @ events.ts:154
handleSendError @ events.ts:125
_callee8$ @ events.ts:256
tryCatch @ events.ts:2
(anonymous) @ events.ts:2
(anonymous) @ events.ts:2
asyncGeneratorStep @ events.ts:2
_throw @ events.ts:2
Promise.then
asyncGeneratorStep @ events.ts:2
_next @ events.ts:2
Promise.then
asyncGeneratorStep @ events.ts:2
_next @ events.ts:2
Promise.then
asyncGeneratorStep @ events.ts:2
_next @ events.ts:2
Promise.then
asyncGeneratorStep @ events.ts:2
_next @ events.ts:2
(anonymous) @ events.ts:2
(anonymous) @ events.ts:2
_onItemSendHandler @ events.ts:258
onItemSendHandler @ events.ts:195
(anonymous) @ outlook-web-16.01.js:20
setTimeout
a._processAppCommandInvocation @ outlook-web-16.01.js:20
fireEvent @ outlook-web-16.01.js:20
onEvent @ outlook-web-16.01.js:20
(anonymous) @ outlook-web-16.01.js:20
M @ outlook-web-16.01.js:20

Ultimately we have another fallback implemented, when NAA does not work, to at least try the old OBO flow. Then we get:

ssoTokenHelper.ts:32 Uncaught (in promise) AccessDeniedError: Do Office není nikdo přihlášený. Před odesláním se přihlaste.
    at _callee2$ (ssoTokenHelper.ts:32:11)
    at tryCatch (ssoTokenHelper.ts:2:1)
    at Generator.<anonymous> (ssoTokenHelper.ts:2:1)
    at Generator.throw (ssoTokenHelper.ts:2:1)
    at asyncGeneratorStep (ssoTokenHelper.ts:2:1)
    at _throw (ssoTokenHelper.ts:2:1)
_callee2$ @ ssoTokenHelper.ts:32
tryCatch @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
(anonymous) @ ssoTokenHelper.ts:2
asyncGeneratorStep @ ssoTokenHelper.ts:2
_throw @ ssoTokenHelper.ts:2
Promise.catch
sendLogs @ justificationInput.tsx:123
(anonymous) @ useARIAButtonProps.js:39
(anonymous) @ useEventCallback.js:26
callCallback @ react-dom.development.js:4164
invokeGuardedCallbackDev @ react-dom.development.js:4213
invokeGuardedCallback @ react-dom.development.js:4277
invokeGuardedCallbackAndCatchFirstError @ react-dom.development.js:4291
executeDispatch @ react-dom.development.js:9041
processDispatchQueueItemsInOrder @ react-dom.development.js:9073
processDispatchQueue @ react-dom.development.js:9086
dispatchEventsForPlugins @ react-dom.development.js:9097
(anonymous) @ react-dom.development.js:9288
batchedUpdates$1 @ react-dom.development.js:26179
batchedUpdates @ react-dom.development.js:3991
dispatchEventForPluginEventSystem @ react-dom.development.js:9287
dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay @ react-dom.development.js:6465
dispatchEvent @ react-dom.development.js:6457
dispatchDiscreteEvent @ react-dom.development.js:6430

MSAL Logs

logger.ts:41 [Safetica]: Creating NPCA for SSO retrieval ssoTokenHelper.ts:62 [Wed, 28 Aug 2024 10:15:29 GMT] : [] : @azure/msal-browser@3.21.0 : Info - Nested App Auth Bridge available: true ssoTokenHelper.ts:65 [Wed, 28 Aug 2024 10:15:29 GMT] : [] : @azure/msal-browser@3.21.0 : Verbose - BrowserCrypto: modern crypto interface available logger.ts:41 [Safetica]: Getting SSO from NAA silently logger.ts:41 [Safetica]: Getting SSO from NAA silently ssoTokenHelper.ts:62 [Wed, 28 Aug 2024 10:15:29 GMT] : [] : @azure/msal-browser@3.21.0 : Info - Emitting event: msal:acquireTokenStart ssoTokenHelper.ts:65 [Wed, 28 Aug 2024 10:15:29 GMT] : [] : @azure/msal-browser@3.21.0 : Verbose - No active account found, falling back to the host ssoTokenHelper.ts:59 [Wed, 28 Aug 2024 10:15:29 GMT] : [] : @azure/msal-browser@3.21.0 : Error - Cached tokens are not found for the account, proceeding with silent token request. loggerCallback @ ssoTokenHelper.ts:59 executeCallback @ Logger.mjs:83 logMessage @ Logger.mjs:76 error @ Logger.mjs:90 acquireTokenFromCache @ NestedAppAuthController.mjs:198 await in acquireTokenFromCache acquireTokenSilentInternal @ NestedAppAuthController.mjs:135 acquireTokenSilent @ NestedAppAuthController.mjs:277 acquireTokenSilent @ PublicClientApplication.mjs:91 _callee4$ @ ssoTokenHelper.ts:93 tryCatch @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 _tryAcquireTokenSilent @ ssoTokenHelper.ts:98 tryAcquireTokenSilent @ ssoTokenHelper.ts:90 _callee3$ @ ssoTokenHelper.ts:81 tryCatch @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 Promise.then asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 _getSsoTokenFromNAA @ ssoTokenHelper.ts:88 getSsoTokenFromNAA @ ssoTokenHelper.ts:38 _callee2$ @ ssoTokenHelper.ts:24 tryCatch @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 Promise.then asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 _getSsoToken @ ssoTokenHelper.ts:36 getSsoToken @ ssoTokenHelper.ts:16 _callee$ @ apiHelper.ts:18 tryCatch @ apiHelper.ts:2 (anonymous) @ apiHelper.ts:2 (anonymous) @ apiHelper.ts:2 asyncGeneratorStep @ apiHelper.ts:2 _next @ apiHelper.ts:2 (anonymous) @ apiHelper.ts:2 (anonymous) @ apiHelper.ts:2 _sendApiRequest @ apiHelper.ts:90 sendApiRequest @ apiHelper.ts:9 _callee3$ @ logger.ts:168 tryCatch @ logger.ts:2 (anonymous) @ logger.ts:2 (anonymous) @ logger.ts:2 asyncGeneratorStep @ logger.ts:2 _next @ logger.ts:2 Promise.then asyncGeneratorStep @ logger.ts:2 _next @ logger.ts:2 Promise.then asyncGeneratorStep @ logger.ts:2 _next @ logger.ts:2 (anonymous) @ logger.ts:2 (anonymous) @ logger.ts:2 sendLogsToBackendAsync @ logger.ts:169 sendLogs @ justificationInput.tsx:123 (anonymous) @ useARIAButtonProps.js:39 (anonymous) @ useEventCallback.js:26 callCallback @ react-dom.development.js:4164 invokeGuardedCallbackDev @ react-dom.development.js:4213 invokeGuardedCallback @ react-dom.development.js:4277 invokeGuardedCallbackAndCatchFirstError @ react-dom.development.js:4291 executeDispatch @ react-dom.development.js:9041 processDispatchQueueItemsInOrder @ react-dom.development.js:9073 processDispatchQueue @ react-dom.development.js:9086 dispatchEventsForPlugins @ react-dom.development.js:9097 (anonymous) @ react-dom.development.js:9288 batchedUpdates$1 @ react-dom.development.js:26179 batchedUpdates @ react-dom.development.js:3991 dispatchEventForPluginEventSystem @ react-dom.development.js:9287 dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay @ react-dom.development.js:6465 dispatchEvent @ react-dom.development.js:6457 dispatchDiscreteEvent @ react-dom.development.js:6430 Show 24 more frames Show less ssoTokenHelper.ts:62 [Wed, 28 Aug 2024 10:15:29 GMT] : [] : @azure/msal-browser@3.21.0 : Info - Emitting event: msal:acquireTokenFailure An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can escape its sandboxing. An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can escape its sandboxing. An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can escape its sandboxing. An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can escape its sandboxing. An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can escape its sandboxing. An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can escape its sandboxing. logger.ts:41 [Safetica]: Justification saved: 0, aadcdn.msauth.net/shared/1.0/content/js/BssoInterrupt_Core_JQnUxWSvwsd9FrpspQmznw2.js:18 BSSO Telemetry: {"result":"Error","error":"NoExtension","type":"ChromeSsoTelemetry","data":{},"traces":["BrowserSSO Initialized","Creating ChromeBrowserCore provider","Sending message for method CreateProviderAsync","Received message for method CreateProviderAsync","Error: ChromeBrowserCore error NoExtension: Extension is not installed."]} ssoTokenHelper.ts:62 [Wed, 28 Aug 2024 10:15:31 GMT] : [] : @azure/msal-browser@3.21.0 : Info - Emitting event: msal:acquireTokenFailure logger.ts:41 [Safetica]: Unable to acquire NAA token silently: InteractionRequiredAuthError: login_required: AADSTS50058: A silent sign-in request was sent but no user is signed in. The cookies used to represent the user's session were not sent in the request to Azure AD. This can happen if the user is using Internet Explorer or Edge, and the web app sending the silent sign-in request is in different IE security zone than the Azure AD endpoint (login.microsoftonline.com). Trace ID: 610938fa-0f9f-4054-bfec-375137adbe00 Correlation ID: 0191987b-b55f-7531-8ec0-5dab532c2dd8 Timestamp: 2024-08-28 10:15:31Z customLogMethod @ logger.ts:41 error @ logger.ts:139 _callee4$ @ ssoTokenHelper.ts:96 tryCatch @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 asyncGeneratorStep @ ssoTokenHelper.ts:2 _throw @ ssoTokenHelper.ts:2 Promise.then asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 _tryAcquireTokenSilent @ ssoTokenHelper.ts:98 tryAcquireTokenSilent @ ssoTokenHelper.ts:90 _callee3$ @ ssoTokenHelper.ts:81 tryCatch @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 Promise.then asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 _getSsoTokenFromNAA @ ssoTokenHelper.ts:88 getSsoTokenFromNAA @ ssoTokenHelper.ts:38 _callee2$ @ ssoTokenHelper.ts:24 tryCatch @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 Promise.then asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 _getSsoToken @ ssoTokenHelper.ts:36 getSsoToken @ ssoTokenHelper.ts:16 _callee$ @ apiHelper.ts:18 tryCatch @ apiHelper.ts:2 (anonymous) @ apiHelper.ts:2 (anonymous) @ apiHelper.ts:2 asyncGeneratorStep @ apiHelper.ts:2 _next @ apiHelper.ts:2 (anonymous) @ apiHelper.ts:2 (anonymous) @ apiHelper.ts:2 _sendApiRequest @ apiHelper.ts:90 sendApiRequest @ apiHelper.ts:9 _callee3$ @ logger.ts:168 tryCatch @ logger.ts:2 (anonymous) @ logger.ts:2 (anonymous) @ logger.ts:2 asyncGeneratorStep @ logger.ts:2 _next @ logger.ts:2 Promise.then asyncGeneratorStep @ logger.ts:2 _next @ logger.ts:2 Promise.then asyncGeneratorStep @ logger.ts:2 _next @ logger.ts:2 (anonymous) @ logger.ts:2 (anonymous) @ logger.ts:2 sendLogsToBackendAsync @ logger.ts:169 sendLogs @ justificationInput.tsx:123 (anonymous) @ useARIAButtonProps.js:39 (anonymous) @ useEventCallback.js:26 callCallback @ react-dom.development.js:4164 invokeGuardedCallbackDev @ react-dom.development.js:4213 invokeGuardedCallback @ react-dom.development.js:4277 invokeGuardedCallbackAndCatchFirstError @ react-dom.development.js:4291 executeDispatch @ react-dom.development.js:9041 processDispatchQueueItemsInOrder @ react-dom.development.js:9073 processDispatchQueue @ react-dom.development.js:9086 dispatchEventsForPlugins @ react-dom.development.js:9097 (anonymous) @ react-dom.development.js:9288 batchedUpdates$1 @ react-dom.development.js:26179 batchedUpdates @ react-dom.development.js:3991 dispatchEventForPluginEventSystem @ react-dom.development.js:9287 dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay @ react-dom.development.js:6465 dispatchEvent @ react-dom.development.js:6457 dispatchDiscreteEvent @ react-dom.development.js:6430 Show 17 more frames Show less logger.ts:41 [Safetica]: Getting SSO from NAA interactively ssoTokenHelper.ts:62 [Wed, 28 Aug 2024 10:15:31 GMT] : [] : @azure/msal-browser@3.21.0 : Info - Emitting event: msal:acquireTokenStart BssoInterrupt_Core_JQnUxWSvwsd9FrpspQmznw2.js:18 BSSO Telemetry: {"result":"Error","error":"NoExtension","type":"ChromeSsoTelemetry","data":{},"traces":["BrowserSSO Initialized","Creating ChromeBrowserCore provider","Sending message for method CreateProviderAsync","Received message for method CreateProviderAsync","Error: ChromeBrowserCore error NoExtension: Extension is not installed."]} ssoTokenHelper.ts:62 [Wed, 28 Aug 2024 10:15:32 GMT] : [] : @azure/msal-browser@3.21.0 : Info - Emitting event: msal:acquireTokenFailure logger.ts:41 [Safetica]: Unable to acquire NAA token interactively: ServerError: user_cancelled: User cancelled the flow. customLogMethod @ logger.ts:41 error @ logger.ts:139 _callee5$ @ ssoTokenHelper.ts:105 tryCatch @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 asyncGeneratorStep @ ssoTokenHelper.ts:2 _throw @ ssoTokenHelper.ts:2 Promise.then asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 _tryAcquireTokenPopup @ ssoTokenHelper.ts:107 tryAcquireTokenPopup @ ssoTokenHelper.ts:100 _callee3$ @ ssoTokenHelper.ts:87 tryCatch @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 Promise.then asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 Promise.then asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 _getSsoTokenFromNAA @ ssoTokenHelper.ts:88 getSsoTokenFromNAA @ ssoTokenHelper.ts:38 _callee2$ @ ssoTokenHelper.ts:24 tryCatch @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 Promise.then asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 _getSsoToken @ ssoTokenHelper.ts:36 getSsoToken @ ssoTokenHelper.ts:16 _callee$ @ apiHelper.ts:18 tryCatch @ apiHelper.ts:2 (anonymous) @ apiHelper.ts:2 (anonymous) @ apiHelper.ts:2 asyncGeneratorStep @ apiHelper.ts:2 _next @ apiHelper.ts:2 (anonymous) @ apiHelper.ts:2 (anonymous) @ apiHelper.ts:2 _sendApiRequest @ apiHelper.ts:90 sendApiRequest @ apiHelper.ts:9 _callee3$ @ logger.ts:168 tryCatch @ logger.ts:2 (anonymous) @ logger.ts:2 (anonymous) @ logger.ts:2 asyncGeneratorStep @ logger.ts:2 _next @ logger.ts:2 Promise.then asyncGeneratorStep @ logger.ts:2 _next @ logger.ts:2 Promise.then asyncGeneratorStep @ logger.ts:2 _next @ logger.ts:2 (anonymous) @ logger.ts:2 (anonymous) @ logger.ts:2 sendLogsToBackendAsync @ logger.ts:169 sendLogs @ justificationInput.tsx:123 (anonymous) @ useARIAButtonProps.js:39 (anonymous) @ useEventCallback.js:26 callCallback @ react-dom.development.js:4164 invokeGuardedCallbackDev @ react-dom.development.js:4213 invokeGuardedCallback @ react-dom.development.js:4277 invokeGuardedCallbackAndCatchFirstError @ react-dom.development.js:4291 executeDispatch @ react-dom.development.js:9041 processDispatchQueueItemsInOrder @ react-dom.development.js:9073 processDispatchQueue @ react-dom.development.js:9086 dispatchEventsForPlugins @ react-dom.development.js:9097 (anonymous) @ react-dom.development.js:9288 batchedUpdates$1 @ react-dom.development.js:26179 batchedUpdates @ react-dom.development.js:3991 dispatchEventForPluginEventSystem @ react-dom.development.js:9287 dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay @ react-dom.development.js:6465 dispatchEvent @ react-dom.development.js:6457 dispatchDiscreteEvent @ react-dom.development.js:6430 Show 17 more frames Show less BssoInterrupt_Core_JQnUxWSvwsd9FrpspQmznw2.js:18 BSSO Telemetry: {"result":"Error","error":"NoExtension","type":"ChromeSsoTelemetry","data":{},"traces":["BrowserSSO Initialized","Creating ChromeBrowserCore provider","Sending message for method CreateProviderAsync","Received message for method CreateProviderAsync","Error: ChromeBrowserCore error NoExtension: Extension is not installed."]} logger.ts:41 [Safetica]: Unable to acquire Office runtime token: [object Object] customLogMethod @ logger.ts:41 error @ logger.ts:139 _callee6$ @ ssoTokenHelper.ts:113 tryCatch @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 asyncGeneratorStep @ ssoTokenHelper.ts:2 _throw @ ssoTokenHelper.ts:2 Promise.then asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 _getSsoTokenFromOfficeRuntime @ ssoTokenHelper.ts:116 getSsoTokenFromOfficeRuntime @ ssoTokenHelper.ts:109 _callee2$ @ ssoTokenHelper.ts:29 tryCatch @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 Promise.then asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 Promise.then asyncGeneratorStep @ ssoTokenHelper.ts:2 _next @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 _getSsoToken @ ssoTokenHelper.ts:36 getSsoToken @ ssoTokenHelper.ts:16 _callee$ @ apiHelper.ts:18 tryCatch @ apiHelper.ts:2 (anonymous) @ apiHelper.ts:2 (anonymous) @ apiHelper.ts:2 asyncGeneratorStep @ apiHelper.ts:2 _next @ apiHelper.ts:2 (anonymous) @ apiHelper.ts:2 (anonymous) @ apiHelper.ts:2 _sendApiRequest @ apiHelper.ts:90 sendApiRequest @ apiHelper.ts:9 _callee3$ @ logger.ts:168 tryCatch @ logger.ts:2 (anonymous) @ logger.ts:2 (anonymous) @ logger.ts:2 asyncGeneratorStep @ logger.ts:2 _next @ logger.ts:2 Promise.then asyncGeneratorStep @ logger.ts:2 _next @ logger.ts:2 Promise.then asyncGeneratorStep @ logger.ts:2 _next @ logger.ts:2 (anonymous) @ logger.ts:2 (anonymous) @ logger.ts:2 sendLogsToBackendAsync @ logger.ts:169 sendLogs @ justificationInput.tsx:123 (anonymous) @ useARIAButtonProps.js:39 (anonymous) @ useEventCallback.js:26 callCallback @ react-dom.development.js:4164 invokeGuardedCallbackDev @ react-dom.development.js:4213 invokeGuardedCallback @ react-dom.development.js:4277 invokeGuardedCallbackAndCatchFirstError @ react-dom.development.js:4291 executeDispatch @ react-dom.development.js:9041 processDispatchQueueItemsInOrder @ react-dom.development.js:9073 processDispatchQueue @ react-dom.development.js:9086 dispatchEventsForPlugins @ react-dom.development.js:9097 (anonymous) @ react-dom.development.js:9288 batchedUpdates$1 @ react-dom.development.js:26179 batchedUpdates @ react-dom.development.js:3991 dispatchEventForPluginEventSystem @ react-dom.development.js:9287 dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay @ react-dom.development.js:6465 dispatchEvent @ react-dom.development.js:6457 dispatchDiscreteEvent @ react-dom.development.js:6430 Show 17 more frames Show less ssoTokenHelper.ts:32 Uncaught (in promise) AccessDeniedError: Do Office není nikdo přihlášený. Před odesláním se přihlaste. at _callee2$ (ssoTokenHelper.ts:32:11) at tryCatch (ssoTokenHelper.ts:2:1) at Generator. (ssoTokenHelper.ts:2:1) at Generator.throw (ssoTokenHelper.ts:2:1) at asyncGeneratorStep (ssoTokenHelper.ts:2:1) at _throw (ssoTokenHelper.ts:2:1) _callee2$ @ ssoTokenHelper.ts:32 tryCatch @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 (anonymous) @ ssoTokenHelper.ts:2 asyncGeneratorStep @ ssoTokenHelper.ts:2 _throw @ ssoTokenHelper.ts:2 Promise.catch sendLogs @ justificationInput.tsx:123 (anonymous) @ useARIAButtonProps.js:39 (anonymous) @ useEventCallback.js:26 callCallback @ react-dom.development.js:4164 invokeGuardedCallbackDev @ react-dom.development.js:4213 invokeGuardedCallback @ react-dom.development.js:4277 invokeGuardedCallbackAndCatchFirstError @ react-dom.development.js:4291 executeDispatch @ react-dom.development.js:9041 processDispatchQueueItemsInOrder @ react-dom.development.js:9073 processDispatchQueue @ react-dom.development.js:9086 dispatchEventsForPlugins @ react-dom.development.js:9097 (anonymous) @ react-dom.development.js:9288 batchedUpdates$1 @ react-dom.development.js:26179 batchedUpdates @ react-dom.development.js:3991 dispatchEventForPluginEventSystem @ react-dom.development.js:9287 dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay @ react-dom.development.js:6465 dispatchEvent @ react-dom.development.js:6457 dispatchDiscreteEvent @ react-dom.development.js:6430 Show 17 more frames Show less owa.JquerySignalR.m.abc5d963.js:1

Network Trace (Preferrably Fiddler)

MSAL Configuration

{
    auth: {
      clientId: clientId,
      authority: "https://login.microsoftonline.com/common"
    }
}

Relevant Code Snippets

import env from "../../env";
import { TokenRetrievalFailedError } from "../definitions/errors";
import logger from "../logging/logger";
import { isNAASupported } from "./featureSetHelper";
import { createNestablePublicClientApplication, IPublicClientApplication, LogLevel } from "@azure/msal-browser";

const defaultSsoOptions: OfficeRuntime.AuthOptions = {
  allowSignInPrompt: true,
  allowConsentPrompt: false,
  forMSGraphAccess: false,
};

export async function getSsoToken(): Promise<string> {
  try {
    var token = undefined;
    const clientId = env.CLIENT_ID;

    if(isNAASupported() && clientId)
      token = await getSsoTokenFromNAA(clientId)

    if (token)
      return token;

    return await getSsoTokenFromOfficeRuntime();
  } catch (error: any) {
    const msg = getErrorMessage(error);
    throw new TokenRetrievalFailedError(msg)
  } 
}

async function getSsoTokenFromNAA(clientId: string): Promise<string | undefined> {
  const tokenRequest = {
    scopes: ["openid", "profile"],
  };

  logger.debug("Creating NPCA for SSO retrieval")

  const npca = await createNestablePublicClientApplication({
    auth: {
      clientId: clientId,
      authority: "https://login.microsoftonline.com/common"
    }
  });

  logger.debug("Getting SSO from NAA silently")

  var authResult = await tryAcquireTokenSilent(npca, tokenRequest)
  if (authResult)
    return authResult

  logger.debug("Getting SSO from NAA interactively")

  return await tryAcquireTokenPopup(npca, tokenRequest)
}

async function tryAcquireTokenSilent(pca: IPublicClientApplication, tokenRequest: { scopes: string[] }): Promise<string | undefined> {
  try {
    logger.debug("Getting SSO from NAA silently")
    const userAccount = await pca.acquireTokenSilent(tokenRequest);
    return userAccount.accessToken;
  } catch (error) {
    logger.error(`Unable to acquire NAA token silently: ${error}`);
  }
}

async function tryAcquireTokenPopup(pca: IPublicClientApplication, tokenRequest: { scopes: string[] }): Promise<string | undefined> {
  try {
    const userAccount = await pca.acquireTokenPopup(tokenRequest);
    return userAccount.accessToken;
  } catch (error) {
    logger.error(`Unable to acquire NAA token interactively: ${error}`);
  }
}

async function getSsoTokenFromOfficeRuntime(): Promise<string> {
  try {
    return await OfficeRuntime.auth.getAccessToken(JSON.parse(JSON.stringify(defaultSsoOptions)));
  } catch (error: any) {
    logger.error(`Unable to acquire Office runtime token: ${error}`);
    throw error;
  }
}

Reproduction Steps

  1. Create plain outlook add-in project with onItemSend event handler and task pane.
  2. Create new app registration within your tenant with delegated permissions (["openid", "profile"]) and SPA redirect URL (can be brk-multihub://localhost:3000 if you run the add-in locally).
  3. replace clientId within our snippet with your clientId
  4. use the snippet to retrieve SSO token inside Safari or chrome browser (incognito window): a. when sending mail (in event handler) b. from task pane (on button click, for example)

Now you should get similar errors.

Expected Behavior

We expect this new way of authentication to work also within browser environment with third-party cookies restriction implemented. The add-in should get the token successfully.

Identity Provider

Entra ID (formerly Azure AD) / MSA

Browsers Affected (Select all that apply)

Chrome, Firefox, Safari

Regression

None

Source

External (Customer)

github-actions[bot] commented 2 weeks ago

Invalid Issue Template: Please update the original issue and make sure to fill out the entire issue template so we can better assist you.