I've built an ASP.net core Single tenant Web API that requires a token from Azure, I have also built a single tenant SPA via react that uses Azure to login Via MSAL-Brower. I want to use the token provided from azure when I log in to authenticate my client SPA to call my API. The token request comes back successfully but when I go to fetch I receive an error on my api stating that
Did not match: validationParameters.ValidAudience: 'System.String' or validationParameters.ValidAudiences: 'System.String'.
I requested a token via MSAL Client method acquireTokenSilent with the scope of permissions established on Azure. Ive tried it all, Ive changed the ClientId and ResourceId in both the Client and the Web API.
const PostToDataBase = () => {
const authenticationModule: AzureAuthenticationContext = new AzureAuthenticationContext();
const account = authenticationModule.myMSALObj.getAllAccounts()[0]
const endpoint = {
endpoint:"https://localhost:44309/api/values",
scopes:[], // redacted for SO
resourceId : "" // redacted for SO
}
async function postValues(value:string){
if(value.length < 1){
console.log("Value can not be null!")
return;
}
console.log(account)
if(account ){
console.log("acquiring token")
authenticationModule.myMSALObj.acquireTokenSilent({
scopes: endpoint.scopes,
account: account
}).then(response => {
console.log("token acquired posting to server")
const headers = new Headers();
const bearer = `Bearer ${response.accessToken}`;
headers.append("Authorization", bearer);
headers.append("Content-Type", "'application/json'")
const options = {
method: "POST",
headers: headers,
bodyInit: JSON.stringify(value)
};
return fetch(endpoint.endpoint, options)
.then(response => console.log(response))
.catch(error => console.log(error));
}).catch(err => {
console.error(err)
if(err instanceof InteractionRequiredAuthError){
if(account ){
authenticationModule.myMSALObj.acquireTokenPopup({
scopes: endpoint.scopes
}).then(response => {
const headers = new Headers();
const bearer = `Bearer ${response.accessToken}`;
headers.append("Authorization", bearer);
const options = {
method: "POST",
headers: headers,
body: value
};
return fetch(endpoint.endpoint, options)
.then(response => response.json())
.catch(error => console.log(error));
}).catch(err => console.error(err))
}
}
})
}
}
async function getValues(){
console.log(account)
if(account ){
console.log("acquiring token")
authenticationModule.myMSALObj.acquireTokenSilent({
scopes: endpoint.scopes,
account: account
}).then(response => {
console.log("token acquired posting to server")
const headers = new Headers();
const bearer = `Bearer ${response.accessToken}`;
headers.append("Authorization", bearer);
headers.append("Content-Type", "'application/json'")
const options = {
method: "GET",
headers: headers
};
return fetch(endpoint.endpoint, options)
.then(response => response.json())
.then(res => setValues(res))
.catch(error => console.log(error));
}).catch(err => {
console.error(err)
if(err instanceof InteractionRequiredAuthError){
if(account ){
authenticationModule.myMSALObj.acquireTokenPopup({
scopes: endpoint.scopes
}).then(response => {
const headers = new Headers();
const bearer = `Bearer ${response.accessToken}`;
headers.append("Authorization", bearer);
const options = {
method: "GET",
headers: headers,
};
return fetch(endpoint.endpoint, options)
.then(response => response.json())
.then(res => setValues(res))
.catch(error => console.log(error));
}).catch(err => console.error(err))
}
}
})
}
}
const [values, setValues] = useState([]);
const [inputValue, setInput] = useState("");
useEffect(() => {
// async function getinit(){
// const values = await fetch("https://localhost:44309/api/values")
// .then(res => res.json())
// .catch(e =>
// console.error(e))
// setValues(values)
// console.log(values)
// }
getValues()
}, [ getValues])
return (
<div>
{values === undefined ? <p>no values to show</p> :
values.map((n,i)=>( <p key={i}>{n}</p>))}
<form>
<input name="inputValues" value={inputValue} onChange={(e)=> setInput(e.target.value)} required></input>
</form>
<button onClick={() => postValues(inputValue)}>Post to Server</button>
</div>
)
}
export default PostToDataBase
This is functional component that makes a call to the api, this pages is only accessible after the user logs in.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Authentication.JwtBearer;
namespace McQuillingWebAPI
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//change to client url in production
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(opt =>
{
opt.Audience = Configuration["AAD:ResourceId"];
opt.Authority = $"{Configuration["AAD:Instance"]}{Configuration["AAD:TenantId"]}";
});
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors("MyPolicy");
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
This is my startup class where I configure middleware for Authentication
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Identity.Web.Resource;
namespace McQuillingWebAPI.Controllers
{
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return "value";
}
// POST api/values
[Authorize]
[HttpPost]
public IActionResult Post([FromBody] string value)
{
return Ok("Posted");
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
}
this is one of the generated controllers that I'm testing authentication with