I have a simple javascript project (SPA-application) that successfully authenticate a user with Azure AD. But when I call my Web API it returns this error:
Status 401. Bearer error="invalid_token", error_description="The signature is invalid"
Neither my web API or SPA-application is published on Azure AD, but I have registered them as two separate applications.
I have given the SPA-application permissions to access the API for both admin and users (with API permissions).
SPA-application javascript:
function acquireTokenPopupAndCallMSGraph() {
//Always start with acquireTokenSilent to obtain a token in the signed in user from cache
myMSALObj.acquireTokenSilent(requestObj).then(function (tokenResponse) {
MyAccessToken = tokenResponse.accessToken;
callMSGraph(graphConfig.graphMeEndpoint, tokenResponse.accessToken, graphAPICallback);
// Call the API - my code.
var accessToken = tokenResponse.accessToken;
var apiUrl = "https://localhost:44353/api/values";
callAPI(apiUrl, accessToken, APICallback);
}).catch(function (error) {
console.log(error);
// Upon acquireTokenSilent failure (due to consent or interaction or login required ONLY)
// Call acquireTokenPopup(popup window)
if (requiresInteraction(error.errorCode)) {
myMSALObj.acquireTokenPopup(requestObj).then(function (tokenResponse) {
callMSGraph(graphConfig.graphMeEndpoint, tokenResponse.accessToken, graphAPICallback);
}).catch(function (error) {
console.log(error);
});
}
});
}
function callMSGraph(theUrl, accessToken, callback) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200)
callback(JSON.parse(this.responseText));
}
xmlHttp.open("GET", theUrl, true); // true for asynchronous
xmlHttp.setRequestHeader('Authorization', 'Bearer ' + accessToken);
xmlHttp.send();
}
function callAPI(theUrl, accessToken, callback) {
console.log("Calling the API.");
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200)
callback(JSON.parse(this.responseText));
}
xmlHttp.open("GET", theUrl, true); // true for asynchronous
xmlHttp.setRequestHeader('Authorization', 'Bearer ' + accessToken);
xmlHttp.send();
}
The javascript code comes from this Azure AD sample code : https://github.com/Azure-Samples/active-directory-javascript-graphapi-v2/blob/quickstart/JavaScriptSPA/index.html
startup.cs (web API)
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors(builder =>
{
builder
.SetIsOriginAllowed(_ => true)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseMvc();
}
appsettings.json (web API)
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "[My API application ID]",
"Domain": "[mycompany.com]",
"TenantId": "[my tenant ID]"
},
ValuesController.cs
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
requestObj
is not explained). But as far as I can see you are using the same access token for both the graph API and your API. This won't work. You need to acquire a token specifically for your API. – Alex AIT