(Continuation of question: Why is my SPA, which is calling my WebAPI, using Azure Active Directory, receiving "Authorization has been denied for this request."?)
My client SPA is trying to call a protected WebAPI (service). The client uses MSAL (Micosoft Authentication Libraries). The problem happens before calling the API, i.e. in the picture below somewhere between 1 and 2.
Here is the client
<!DOCTYPE html>
<html>
<head>
<title>Quickstart for MSAL JS</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.3.4/bluebird.min.js"></script>
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.2/js/msal.js"></script>
</head>
<body>
<div class="container">
<div>
<button id="GetTodos" onclick="getTodos()">Get Todos</button>
</div>
</div>
<script>
var msalConfig = {
auth: {
clientId: 'xxxxxxxxxxxxxxxxxxxxxxx2427',
authority: "https://login.microsoftonline.com/my.tenant"
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: true
}
};
var requestObj = {
// scopes: ["user.read"]
scopes: ["access_as_user"]
};
var myMSALObj = new Msal.UserAgentApplication(msalConfig);
myMSALObj.handleRedirectCallback(authRedirectCallBack);
function getTodos() {
console.log("getTodos ...");
myMSALObj.loginPopup(requestObj)
.then(response => {
myMSALObj.acquireTokenPopup(requestObj).then(function (tokenResponse) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
console.log("success");
//this.responseText
} else {
console.log("failure");
}
}
const apiUrl = "https://localhost:44321/api/todolist";
xmlHttp.open("GET", apiUrl, true); // true for asynchronous
xmlHttp.setRequestHeader('Authorization', 'Bearer ' + tokenResponse.accessToken);
xmlHttp.send();
}).catch(function (error) {
console.log(error);
});
})
.catch(err => {
// handle error
console.log("login popup failed.", err);
});
}
function authRedirectCallBack(error, response) {
console.log('authRedirectCallBack');
if (error) {
console.log(error);
} else {
if (response.tokenType === "access_token") {
console.log("response.tokenType === access_token");
} else {
console.log("token type is:" + response.tokenType);
}
}
}
</script>
</body>
</html>
Both client and service are registered apps on Azure Active Directory
The client has API permissions to access the service.
And the service does expose an API with the scope access_as_user
However the call
myMSALObj.loginPopup(requestObj)
causes
ServerError: "AADSTS650053: The application 'ClientSPA' asked for scope 'access_as_user' that doesn't exist on the resource '00000003-0000-0000-c000-000000000000'.
And when using Chrome I get this message
Further Investigation
Instead of asking for scope „access_as_user“, I ask for „user.read“ This gives me an access token. But this causes the WebAPI to respond with
Authorization has been denied for this request
And when I decode the access token I see that the audience is https://graph.microsoft.com. But I am expecting the audience to be „https://my.tenant/TodoListService-NativeDotNet“. See picture below (the obfuscated lines do contain information that is specific to my user and my registered app)
Questions
Where does the resource '00000003-0000-0000-c000-000000000000' ID come from (it is mentioned in the error message)? It is not the application ID of the client nor of the service.
What have I done incorrectly, either in the configuration on Azure Active Directory or in the client?
Could CORS be a problem? I have setup the service to allow CORS.