Which version of Microsoft Identity Web are you using?
Note that to get help, you need to run the latest version.
Where is the issue?
Is this a new or an existing app?
This is a new application
Repro
Followed SPA tutorial, I get my token, send the token to the backend (yes its missing awaits, hardly matters if it does not work)
const configuration = {
headers: {
"Authorization": "Bearer " + sessionStorage.getItem('DRAGONQUEST')
}
};
fetch('api/test', configuration);
The only code I have is this:
services.AddMicrosoftIdentityWebApiAuthentication(Configuration, "AzureAd");
in the TestController.cs
[Route("api/test"), ApiController, Authorize]
public class TestController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
HttpContext.VerifyUserHasAnyAcceptedScope(ApplicationWideConstants.SCOPE_REQUIRED_BY_API);
return Ok();
}
}
Expected behavior
I guess I would expect a call to api/test to work
Actual behavior
A clear and concise description of what happens, e.g. an exception is thrown, UI freezes.
Additional context / logs / screenshots
I'm willing to have a Teams meeting to show the issue live whenever is convenient.
@jrodrigoav what is the SPA tutorial? can you please share repro steps that we can repro (a link, or some code)
for the moment with the elements you provided, I think you got a token for an API other than your API (for example a token for Graph), and try to use it for your web API ? but I could be wrong ... I'm missing context.
So sorry for getting back to this so late
This is the article
https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-spa-app-configuration?tabs=javascript
I followed the steps to acquire a token and then followed the steps to Protect the WebAPI using Azure AD:
https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-protected-web-api-overview
I can share the code but not sure it will do much without the unique identifiers, that I can't share because "security reasons".
For what is worth, the token I get I can use to call https://graph.microsoft.com/v1.0/me using postman
Here is the HTML I am using, it is pretty much the example.
<!doctype html>
<html lang="en">
<head>
<title>Index</title>
</head>
<body>
<h1>Hello</h1>
<script type="text/javascript" src="https://alcdn.msauth.net/browser/2.5.2/js/msal-browser.min.js"></script>
<script>
const msalConfig = {
auth: {
clientId: '[REDACTED]',
authority: 'https://login.microsoftonline.com/[REDACTED_COMPANY_GUID]',
redirectUri: 'https://localhost:44303/index.html'
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: false
}
};
const REQUEST_SCOPES = {
scopes: ["openid", "profile", "User.Read","api://[SAME_AS_CLIENT_ID]/access_as_user"]
};
let account = null;
const msalInstance = new msal.PublicClientApplication(msalConfig);
function getAccounts(msalInstance) {
let currentAccounts = [];
let success = false;
try {
// In case multiple accounts exist, you can select
currentAccounts = msalInstance.getAllAccounts();
if (currentAccounts === null) {
// no accounts detected
} else if (currentAccounts.length >= 1) {
account = { account: currentAccounts[0] };
success = true;
}
}
catch (err) {
console.log(err);
}
return success;
}
function aquireToken(msalInstance) {
let success = false;
const request = Object.assign(REQUEST_SCOPES, account);
msalInstance.acquireTokenSilent(request).then(tokenResponse => {
// Do something with the tokenResponse
sessionStorage.setItem("DRAGONQUEST", tokenResponse.accessToken);
testApi();
success = true;
}).catch(async (error) => {
console.log(error);
}).catch(error => {
console.log(error);
});
return success;
}
function testApi() {
const headers = new Headers();
const token = sessionStorage.getItem('DRAGONQUEST');
const bearer = `Bearer ${token}`;
headers.append("Authorization", bearer);
const options = {
method: "GET",
headers: headers
};
//This is the call to the API hosting this html file it returns a 401
fetch('api/test', options);
}
if (getAccounts(msalInstance) === false) {
console.log('Calling microsoft');
msalInstance.loginPopup(REQUEST_SCOPES)
.then(function (loginResponse) {
//login success
console.log(loginResponse);
}).catch(function (error) {
//login failure
console.log(error);
});
}
else {
aquireToken(msalInstance);
}
</script>
</body>
</html>
The Startup.cs
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration);
//services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"));
//services.AddMicrosoftIdentityWebApiAuthentication(Configuration, "AzureAd");
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseDefaultFiles();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
TestController.cs
[Route("api/test"), ApiController, Authorize]
public class TestController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
HttpContext.VerifyUserHasAnyAcceptedScope(ApplicationWideConstants.SCOPE_REQUIRED_BY_API);
return Ok(User.Claims.Select(claim => new { claim.Type, claim.Value }));
}
}
@jrodrigoav
for what is worth, the token I get I can use to call https://graph.microsoft.com/v1.0/me using postman
The following web API sample illustrates your scenario : https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore-v2/tree/master/2.%20Web%20API%20now%20calls%20Microsoft%20Graph (but with a desktop app, instead of a SPA, but that's not the most difficult thing to change.
You might also be interested in these samples/tutorials:
cc: @kalyankrishna1 @derisen
Thank you, dropping kid to school and giving this a try.
And yes the ClientId is the same, the SPA and the webapi are the same project.
Yes that was it, thank you very much!
Most helpful comment
@jrodrigoav
The following web API sample illustrates your scenario : https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore-v2/tree/master/2.%20Web%20API%20now%20calls%20Microsoft%20Graph (but with a desktop app, instead of a SPA, but that's not the most difficult thing to change.
You might also be interested in these samples/tutorials:
cc: @kalyankrishna1 @derisen