无法验证第一个证书:WWW 身份验证:持有者错误 = "invalid_token" , error_description= "The signature is invalid"



我有一个spa应用程序,将创建令牌,我需要验证令牌在web api。在Azure

中使用以下方法配置应用程序https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow

我能够在Ui应用程序中生成令牌,但是当我尝试使用postman验证令牌时,它会抛出错误WWW-Authenticate: Bearer error="invalid_token", error_description="签名是无效的">

Angular代码:authconfig.js

const msalConfig = {
auth: {       
clientId: "id",       
authority: "https://login.microsoftonline.com/id",       
redirectUri: "http://localhost:3000/",
},
cache: {
cacheLocation: "sessionStorage", // This configures where your cache will be stored
storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
},
system: {   
loggerOptions: {    
loggerCallback: (level, message, containsPii) => {  
if (containsPii) {      
return;     
}       
switch (level) {        
case msal.LogLevel.Error:       
console.error(message);     
return;     
case msal.LogLevel.Info:        
console.info(message);      
return;     
case msal.LogLevel.Verbose:     
console.debug(message);     
return;     
case msal.LogLevel.Warning:     
console.warn(message);      
return;     
}   
}   
}   
}
};

const loginRequest = {
scopes: ["User.Read"]
};
const tokenRequest = {
scopes: ["User.Read", "Mail.Read"],
};

AuthPopUp.js

const myMSALObj = new msal.PublicClientApplication(msalConfig);
let username = "";
function selectAccount() {

const currentAccounts = myMSALObj.getAllAccounts();
if (currentAccounts.length === 0) {
return;
} else if (currentAccounts.length > 1) {
// Add choose account code here
console.warn("Multiple accounts detected.");
} else if (currentAccounts.length === 1) {
username = currentAccounts[0].username;
showWelcomeMessage(username);
}
}
function handleResponse(response) {

if (response !== null) {
username = response.account.username;
showWelcomeMessage(username);
} else {
selectAccount();
}
}
function signIn() {

myMSALObj.loginPopup(loginRequest)
.then(handleResponse)
.catch(error => {
console.error(error);
});
}
function signOut() {

const logoutRequest = {
account: myMSALObj.getAccountByUsername(username),
postLogoutRedirectUri: msalConfig.auth.redirectUri,
mainWindowRedirectUri: msalConfig.auth.redirectUri
};
myMSALObj.logoutPopup(logoutRequest);
}
function getTokenPopup(request) {
/**
* See here for more info on account retrieval: 
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md
*/
request.account = myMSALObj.getAccountByUsername(username);

return myMSALObj.acquireTokenSilent(request)
.catch(error => {
console.warn("silent token acquisition fails. acquiring token using popup");
if (error instanceof msal.InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
return myMSALObj.acquireTokenPopup(request)
.then(tokenResponse => {
console.log(tokenResponse);
return tokenResponse;
}).catch(error => {
console.error(error);
});
} else {
console.warn(error);   
}
});
}
function seeProfile() {
getTokenPopup(loginRequest)
.then(response => {
callMSGraph(graphConfig.graphMeEndpoint, response.accessToken, updateUI);
}).catch(error => {
console.error(error);
});
}
function readMail() {
getTokenPopup(tokenRequest)
.then(response => {
callMSGraph(graphConfig.graphMailEndpoint, response.accessToken, updateUI);
}).catch(error => {
console.error(error);
});
}
selectAccount();

AuthRedirect.js

// Create the main myMSALObj instance
// configuration parameters are located at authConfig.js
const myMSALObj = new msal.PublicClientApplication(msalConfig);
let username = "";
/**
* A promise handler needs to be registered for handling the
* response returned from redirect flow. For more information, visit:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/acquire-token.md
*/
myMSALObj.handleRedirectPromise()
.then(handleResponse)
.catch((error) => {
console.error(error);
});
function selectAccount () {
/**
* See here for more info on account retrieval: 
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md
*/
const currentAccounts = myMSALObj.getAllAccounts();
if (currentAccounts.length === 0) {
return;
} else if (currentAccounts.length > 1) {
// Add your account choosing logic here
console.warn("Multiple accounts detected.");
} else if (currentAccounts.length === 1) {
username = currentAccounts[0].username;
showWelcomeMessage(username);
}
}
function handleResponse(response) {
if (response !== null) {
username = response.account.username;
showWelcomeMessage(username);
} else {
selectAccount();
}
}
function signIn() {
/**
* You can pass a custom request object below. This will override the initial configuration. For more information, visit:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/request-response-object.md#request
*/
myMSALObj.loginRedirect(loginRequest);
}
function signOut() {
/**
* You can pass a custom request object below. This will override the initial configuration. For more information, visit:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/request-response-object.md#request
*/
const logoutRequest = {
account: myMSALObj.getAccountByUsername(username),
postLogoutRedirectUri: msalConfig.auth.redirectUri,
};
myMSALObj.logoutRedirect(logoutRequest);
}
function getTokenRedirect(request) {
/**
* See here for more info on account retrieval: 
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md
*/
request.account = myMSALObj.getAccountByUsername(username);
return myMSALObj.acquireTokenSilent(request)
.catch(error => {
console.warn("silent token acquisition fails. acquiring token using redirect");
if (error instanceof msal.InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
return myMSALObj.acquireTokenRedirect(request);
} else {
console.warn(error);   
}
});
}
function seeProfile() {
getTokenRedirect(loginRequest)
.then(response => {
callMSGraph(graphConfig.graphMeEndpoint, response.accessToken, updateUI);
}).catch(error => {
console.error(error);
});
}
function readMail() {
getTokenRedirect(tokenRequest)
.then(response => {
callMSGraph(graphConfig.graphMailEndpoint, response.accessToken, updateUI);
}).catch(error => {
console.error(error);
});
}

Graph.js

function callMSGraph(endpoint, token, callback) {
const headers = new Headers();
const bearer = `Bearer ${token}`;
console.log`Bearer ${token}`
headers.append("Authorization", bearer);
const options = {
method: "GET",
headers: headers
};
console.log('request made to Graph API at: ' + new Date().toString());
fetch(endpoint, options)
.then(response => response.json())
.then(response => callback(response, endpoint))
.catch(error => console.log(error));
}

GrpahConfig.js

const graphConfig = {
graphMeEndpoint: "https://graph.microsoft.com/v1.0/me",
graphMailEndpoint: "https://graph.microsoft.com/v1.0/me/messages"
};

在我的web api项目中,我使用JWT认证方案

Startup.cs

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)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"));
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "webapi", Version = "v1" });
});
}
// 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.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "webapi v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}

appsettings.json

{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "domainnmae.onmicrosoft.com",
"TenantId": "id",
"ClientId": "id"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}

控制器:

[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
// The Web API will only accept tokens 1) for users, and 2) having the "access_as_user" scope for this API
static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
}

我不确定如果我在这里错过了什么,或者我的方法是错误的。请告诉我你的建议

一开始,我不知道你想要实现什么…

让我们看看为什么我们得到错误在web api应用程序。我注意到你在控制器中有下面的代码。

static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };

虽然变量scopeRequiredByApi没有被看到使用,根据名称,我认为这是您试图验证的api范围。但是在您的SPA代码中,您设置的范围是用于MS图形api的scopes: ["User.Read", "Mail.Read"],,因此它们不匹配,然后问题发生了。

在你的场景中,你在Augular应用中设置了msgraphapi的作用域,我不确定你是否需要调用msgraphapi,如果需要,我认为你已经可以成功调用msgraphapi了。如果你不需要调用ms图形api,你只想调用你自己的web api,那么你应该先学会用Azure AD保护你的web api。其中包含关于如何注册应用程序的教程;

可以概括为,您需要先让用户登录完成认证,然后有了认证,您需要使用它来完成授权。

由于api是您的自定义api,因此您必须为它公开api权限,以便AAD可以帮助您管理和授权。在您的场景中,您需要公开委托权限。

然后你需要配置你的代码对应于你在Azure AD中所做的,包括改变你的Angular应用程序的作用域,像api://clientid_of_the_app_exposed_api/xxx。这是一个web api的示例。你也可以看看我对react + web api的回答。

相关内容

最新更新