Azure 身份验证受众验证失败
Posted
技术标签:
【中文标题】Azure 身份验证受众验证失败【英文标题】:Azure authentication Audience validation failed 【发布时间】:2021-10-16 04:03:13 【问题描述】:我已经构建了一个 ASP.net 核心单租户 Web API,它需要来自 Azure 的令牌,我还通过 react 构建了一个单租户 SPA,它使用 Azure 通过 MSAL-Brower 登录。我想在登录时使用 azure 提供的令牌来验证我的客户端 SPA 以调用我的 API。 令牌请求成功返回,但是当我去获取时,我的 api 上收到一个错误,指出
不匹配:validationParameters.ValidAudience: 'System.String' 或 validationParameters.ValidAudiences: 'System.String'。
我通过 MSAL 客户端方法 acquireTokenSilent 请求了一个令牌,该令牌具有在 Azure 上建立的权限范围。我已经尝试了所有方法,我在客户端和 Web API 中都更改了 ClientId 和 ResourceId。
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
这是一个调用api的功能组件,这个页面只有在用户登录后才能访问。
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();
);
这是我为身份验证配置中间件的启动类
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)
这是我正在测试身份验证的生成控制器之一
【问题讨论】:
【参考方案1】:恐怕问题来自启动时的身份验证配置。请允许我显示我的代码 sn-p 以很好地解释它。
在我看来,您可以改用services.AddMicrosoftIdentityWebApiAuthentication(Configuration);
。并且你应该正确地暴露 api。
公开api的步骤,可以按照文档进行。我想在这里重复的是,当您生成访问令牌时,它应该具有api://clientid_of_the_app_exposed_api/tiny/User.Read
之类的范围,可以匹配appsettings.json
中的配置
我的react代码,参考this sample:
import AuthenticatedTemplate, UnauthenticatedTemplate, useMsal from "@azure/msal-react";
const callApi = (accessToken) =>
const headers = new Headers();
const bearer = `Bearer $accessToken`;
headers.append("Authorization", bearer);
const options =
method: "GET",
headers: headers
;
fetch("https://localhost:44341/api/home", options)
.then(response =>
var a = response.json();
console.log(a);
)
.catch(error => console.log(error));
;
const ProfileContent = () =>
const instance , accounts = useMsal();
const [graphData, setGraphData] = useState(null);
const loginRequest = "scopes": ["api://clientid_of_the_app_exposed_api/tiny/User.Read"];
function RequestProfileData()
instance.acquireTokenSilent(
...loginRequest,
account: accounts[0]
).then((response) =>
callApi(response.accessToken);
);
我在启动文件中的ConfigureServices,这些被引用this document:
public void ConfigureServices(IServiceCollection services)
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
));
services.AddMicrosoftIdentityWebApiAuthentication(Configuration);
services.AddControllers();
我的应用设置:
"Logging":
"LogLevel":
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
,
"AllowedHosts": "*",
"AzureAd":
"Instance": "https://login.microsoftonline.com/",
"ClientId": "clientid_which_have_api_permission",
"Domain": "tenantname.onmicrosoft.com",
"TenantId": "common",
"Audience": "clientid_of_the_app_exposed_api"
我的控制器:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Web.Resource;
using System.Collections.Generic;
namespace WebApplication1.Controllers
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class HomeController : ControllerBase
[HttpGet]
[RequiredScope("User.Read")]
public ActionResult<IEnumerable<string>> Get()
return new string[] "value1", "value2" ;
【讨论】:
【参考方案2】:错误消息表明身份验证中间件无法成功验证请求,因为令牌中的受众不是有效受众的一部分。 要解决此问题,您可以提及配置中的有效受众是什么,并且您的令牌应具有该受众。要检查令牌中的受众,您可以在 jwt.io 中查看令牌字段 此外,如果您想跳过受众验证,可以在配置身份验证中间件时通过将 ValidateAudience 标记为 false 来执行此操作。
【讨论】:
以上是关于Azure 身份验证受众验证失败的主要内容,如果未能解决你的问题,请参考以下文章
Azure AD 身份验证 401 错误“受众无效” AddAzureADBearer .Net Core Web Api
Microsoft Graph API 身份验证错误:“访问令牌验证失败。无效的受众”
如何查找 Active Directory OAuth 身份验证的受众字段? (如何从 Azure Logic App 向 DevOps 发送发布请求?)