microsoft-authentication-library-for-js: Cannot log in using a mobile and desktop applications redirect URL prefixed with `tauri://`
Core Library
MSAL.js (@azure/msal-browser)
Core Library Version
2.38.0
Wrapper Library
MSAL React (@azure/msal-react)
Wrapper Library Version
1.5.9
Public or Confidential Client?
Public
Description
Cannot log in using a mobile and desktop applications redirect URL prefixed with tauri://
. It works with http://localhost
but not from tauri://localhost
.
Error Message
{
"error": "invalid_request",
"error_description": "AADSTS90023: Cross-origin token redemption is permitted only for the 'Single-Page Application' client-type or 'Native' client-type with origin registered in AllowedOriginForNativeAppCorsRequestInOAuthToken allow list.\r\nTrace ID: xxx\r\nCorrelation ID: xxx\r\nTimestamp: 2023-07-17 16:28:13Z",
"error_codes": [
90023
],
"timestamp": "2023-07-17 16:28:13Z",
"trace_id": "xxx",
"correlation_id": "xxx"
}
MSAL Logs
[Debug] Permission granted (index-95a4e7e3.js, line 107)
[Error] Unhandled Promise Rejection: Scope not defined for window `main` and URL `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=xxx&scope=openid%20profile%20offline_access&redir...
[Info] Successfully preconnected to https://aadcdn.msauth.net/ (x2)
[Warning] [TAURI] Couldn't find callback id 1866876788 in window. This happens when the app is reloaded while Rust is running an asynchronous operation. (authorize, line 5)
[Error] Unhandled Promise Rejection: Scope not defined for window `main` and URL `https://login.live.com/oauth20_authorize.srf?client_id=xxx&scope=openid+profile+offline_access&redirect_uri=tauri%3a%2f%2f...
[Error] Unhandled Promise Rejection: Scope not defined for window `main` and URL `https://account.live.com/App/Confirm?mkt=EN-GB&uiflavor=host&id=293577&client_id=0000000048FB303C&ru=https://login.live.com/oauth20_authorize.srf%3fuaid%3d...
[Info] Successfully preconnected to https://acctcdn.msftauth.net/ (x4)
[Log] Mon, 17 Jul 2023 16:37:45 GMT:FlowController.showControl(appConfirm) (Confirm, line 78)
[Log] Mon, 17 Jul 2023 16:37:45 GMT:New State [appConfirm] from [none] (Confirm, line 78)
[Log] Mon, 17 Jul 2023 16:37:45 GMT:Hooking control events for [appConfirm] (Confirm, line 78)
[Log] Mon, 17 Jul 2023 16:37:45 GMT:PageDialogControl.show() (Confirm, line 78)
[Log] Mon, 17 Jul 2023 16:37:45 GMT:FlowController.handleControlEvent [onSetupEvents] for [appConfirm] (Confirm, line 78)
[Log] Mon, 17 Jul 2023 16:37:45 GMT:FlowController.handleControlEvent [onShow] for [appConfirm] (Confirm, line 78)
[Log] Mon, 17 Jul 2023 16:37:45 GMT:PageDialogControl.~show() (Confirm, line 78)
[Log] Mon, 17 Jul 2023 16:37:45 GMT:FlowController.notifyVisible [appConfirm] (Confirm, line 78)
[Error] Failed to load resource: the server responded with a status of 404 () (CustomFunctions.js.map, line 0)
[Error] Failed to load resource: the server responded with a status of 404 () (ms.properties-3.2.6.min.js.map, line 0)
[Log] Mon, 17 Jul 2023 16:37:47 GMT:FlowController.handleControlEvent [onAction] for [appConfirm] (Confirm, line 78)
[Log] Mon, 17 Jul 2023 16:37:47 GMT:FlowController.processActionEvent[next] for [appConfirm] (Confirm, line 78)
[Log] Mon, 17 Jul 2023 16:37:47 GMT:FlowController.processActionEvent newState [success] (Confirm, line 78)
[Log] Mon, 17 Jul 2023 16:37:47 GMT:FlowController.showControl(success) (Confirm, line 78)
[Log] Mon, 17 Jul 2023 16:37:47 GMT:New State [success] from [appConfirm] (Confirm, line 78)
[Log] Mon, 17 Jul 2023 16:37:47 GMT:Navigate to [https://login.live.com/oauth20_authorize.srf?uaid=65df5d6b0986473e8dcae64a41db9303&client_id=xxx&opid=39F05E265291377E&mkt=EN-GB&opidt=1689611864&route=C107_BL2&res=success] (Confirm, line 78)
[Debug] Permission granted (index-95a4e7e3.js, line 107)
[Error] Failed to load resource: the server responded with a status of 400 (Bad Request) (token, line 0)
MSAL Configuration
const pcaConfig = {
auth: {
clientId: import.meta.env.VITE_OIDC_CLIENT_ID,
navigateToLoginRequestUrl: true, // Go back to the original page after login
postLogoutRedirectUri: "/", // Go back to the app root after logout
redirectUri: "/", // Go back to the app root after login
},
cache: {
cacheLocation: "localStorage",
temporaryCacheLocation: "sessionStorage",
},
system: {
navigationClient: new CustomNavigationClient(router.navigate),
loggerOptions: {
logLevel: import.meta.env.DEV || import.meta.env.TAURI_DEBUG ? LogLevel.Verbose : LogLevel.Warning,
loggerCallback: (level, message, containsPii) => {
if (containsPii) {
return;
}
switch (level) {
case LogLevel.Error:
console.error(message);
return;
case LogLevel.Info:
console.info(message);
return;
case LogLevel.Verbose:
console.debug(message);
return;
case LogLevel.Warning:
console.warn(message);
return;
}
},
piiLoggingEnabled: false,
},
},
};
Relevant Code Snippets
The application is run from Tauri (embedded web app on desktop, no Node.js runtime).
const IS_TAURI = window.__TAURI_METADATA__ != undefined;
const login = async (instance) => {
IS_TAURI ? await instance.loginRedirect() : await instance.loginPopup();
};
const getIdToken = async (account, instance) => {
if (!account) return null;
const req = {
account: account,
loginHint: account.username,
scopes: ["openid", "profile", "email", "User.Read"],
};
// Try silent first
const idToken = await instance
.acquireTokenSilent(req)
.then((res) => {
return res.idToken;
})
.catch((error) => {
if (!(error instanceof InteractionRequiredAuthError)) {
console.error(error);
return null;
}
const onSuccess = (res) => {
return res.idToken;
};
const onError = (error) => {
console.error(error);
return null;
};
if (IS_TAURI) {
// Failback to redirect
return instance
.acquireTokenRedirect(req)
.then(onSuccess)
.catch(onError);
}
// Failback to popup
return instance.acquireTokenPopup(req).then(onSuccess).catch(onError);
});
return idToken;
};
AAD app manifest:
{
"id": "xxx",
"acceptMappedClaims": null,
"accessTokenAcceptedVersion": 2,
"addIns": [],
"allowPublicClient": true,
"appId": "e9d5f20f-7f14-4204-a9a2-0d91d6af5c82",
"appRoles": [
{
"allowedMemberTypes": [
"User"
],
"description": "Ability to access the application.",
"displayName": "Contributors",
"id": "687bbba0-26c1-435e-9e48-5cdd93d423cb",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "Contributors"
}
],
"oauth2AllowUrlPathMatching": false,
"createdDateTime": "2023-06-28T12:43:54Z",
"description": null,
"certification": null,
"disabledByMicrosoftStatus": null,
"groupMembershipClaims": "None",
"identifierUris": [],
"informationalUrls": {
"termsOfService": null,
"support": null,
"privacy": null,
"marketing": null
},
"keyCredentials": [],
"knownClientApplications": [],
"logoUrl": "https://aadcdn.msftauthimages.net/dbd5a2dd-arfqpbjsje9u4fcbla9kbny3eqocpihhyg0ntigcgqg/appbranding/kjco3fpdmdpzymxko05usfexpyybabekbazxi4jp40y/1033/bannerlogo?ts=638251791039670967",
"logoutUrl": null,
"name": "Private GPT",
"notes": null,
"oauth2AllowIdTokenImplicitFlow": true,
"oauth2AllowImplicitFlow": false,
"oauth2Permissions": [],
"oauth2RequirePostResponse": false,
"optionalClaims": {
"idToken": [
{
"name": "login_hint",
"source": null,
"essential": false,
"additionalProperties": []
}
],
"accessToken": [],
"saml2Token": []
},
"orgRestrictions": [],
"parentalControlSettings": {
"countriesBlockedForMinors": [],
"legalAgeGroupRule": "Allow"
},
"passwordCredentials": [],
"preAuthorizedApplications": [],
"publisherDomain": "xxx.onmicrosoft.com",
"replyUrlsWithType": [
{
"url": "https://private-gpt.shopping-cart-devops-demo.lesne.pro",
"type": "Spa"
},
{
"url": "http://localhost:8080",
"type": "Spa"
},
{
"url": "tauri://localhost",
"type": "InstalledClient"
},
{
"url": "https://private-gpt.shopping-cart-devops-demo.lesne.pro/auth",
"type": "Spa"
}
],
"requiredResourceAccess": [
{
"resourceAppId": "00000003-0000-0000-c000-000000000000",
"resourceAccess": [
{
"id": "14dad69e-099b-42c9-810b-d002981feec1",
"type": "Scope"
},
{
"id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d",
"type": "Scope"
},
{
"id": "37f7f235-527c-4136-accd-4a02d197296e",
"type": "Scope"
},
{
"id": "64a6cdd6-aab1-4aaf-94b8-3cc8405e90d0",
"type": "Scope"
}
]
}
],
"samlMetadataUrl": null,
"signInUrl": "https://github.com/clemlesne/private-gpt",
"signInAudience": "AzureADandPersonalMicrosoftAccount",
"tags": [
"NoLiveSdkSupport"
],
"tokenEncryptionKeyId": null
}
Expected Behavior
I would expect the logging to work the same it is working for the web version.
Identity Provider
Azure AD / MSA
Browsers Affected (Select all that apply)
Other
Related links (suggested)
- https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/324
- https://github.com/Azure-Samples/ms-identity-javascript-angular-tutorial/issues/37
- https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/5196
- https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/2482#issuecomment-717576089
- https://learn.microsoft.com/en-us/answers/questions/537694/cross-origin-token-redemption-is-permitted-only-fo
Source
Internal (Microsoft)
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 16 (13 by maintainers)
Yes, our verification of
redirectUri
depends on thehttps
messaging to the service. Since it is a SPA and no secrets are exchanged, this is the only way we can confirm the app’s validity to receive the tokens.More docs can be found here.
@clemlesne SPAs only support only URLs that start with
https:
for production apps andhttp://localhost
for local dev. The format you mentioned above can be supported only for mobile or web apps as they have a confidential component unlike browser apps. Hope this clarifies.This issue requires attention from the MSAL.js team and has not seen activity in 5 days. @konstantin-msft please follow up.