我的应用程序是一个混合的方法,其中使用ASP.NET核心MVC作为我的后端。我有各种各样的控制器,我的前端用来从我们的数据库拉数据,也做API调用MS图。我使用以下program.cs文件,以获得身份验证时,用户首次登录到网站:
//authentication pipline
builder.Services.AddHttpContextAccessor();
var initialScopes = builder.Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(options =>
{
builder.Configuration.Bind("AzureAd", options);
options.Events = new OpenIdConnectEvents
{
//Tap into this event to add a UserID Claim to a new HttpContext identity
OnTokenValidated = context =>
{
//This query returns the UserID from the DB by sending the email address in the claim from Azure AD
string query = "select dbo.A2F_0013_ReturnUserIDForEmail(@Email) as UserID";
string connectionString = builder.Configuration.GetValue<string>("ConnectionStrings:DBContext");
string signInEmailAddress = context.Principal.FindFirstValue("preferred_username");
using (var connection = new SqlConnection(connectionString))
{
var queryResult = connection.QueryFirst(query, new { Email = signInEmailAddress });
var claims = new List<Claim>
{
new Claim("UserID", queryResult.UserID.ToString())
};
var appIdentity = new ClaimsIdentity(claims);
context.Principal.AddIdentity(appIdentity);
}
return Task.CompletedTask;
},
};
}).EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
.AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();
//Add Transient Services
builder.Services.AddTransient<IOneDrive, OneDrive>();
builder.Services.AddControllers(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).AddMicrosoftIdentityUI();
builder.Services.AddRazorPages().AddRazorPagesOptions(options =>
{
options.Conventions.AllowAnonymousToFolder("/Login");
options.Conventions.AuthorizeFolder("/");
options.Conventions.AuthorizeFolder("/files");
}).AddMicrosoftIdentityUI();
// Add the UI support to handle claims challenges
builder.Services.AddServerSideBlazor()
.AddMicrosoftIdentityConsentHandler();
builder.Services.AddRequiredScopeAuthorization();
在Azure AD门户中,我的应用程序被注册为Web应用程序。因此,当用户最初访问该站点时,他们将被重定向到https://login.microsoftonline.com/blahblah以启动登录过程。这是由Azure AD身份平台自动执行的。然后,一旦登录,他们将被重定向到加载VueJS spa的localhost(localhost:43862)。我的spa使用各种axios请求来访问控制器,它们将提取数据并加载vue路由器组件。但是,我的问题是,用户需要重新登录,因为cookie已过期或他们在另一个选项卡中注销。过期会话发出的下一个axios请求不会将用户重定向到Azure登录屏幕,而是导致CORS错误。因此,我需要获取axios请求,以强制将页面重定向到Azure AD登录屏幕(这可能是最糟糕的主意,因为CORS策略导致错误),或者让它返回重定向到localhost/login,这是我自己的自定义登录屏幕,其中有一个Azure AD登录按钮,不应影响CORS。那么,我如何拦截此重定向到Azure AD登录
我也尝试过返回一个401错误代码,这样我就可以在我的axios请求中检查这个错误代码,但是没有任何效果。如果我在那里放置一个断点,它确实会命中这个代码,但是它不会改变响应的状态代码,我仍然得到302。我的代码是尝试添加到事件中:
OnRedirectToIdentityProvider = context =>
{
context.Response.StatusCode = 401;
return Task.CompletedTask;
}
My other ideas was maybe I should set my CORS policy to allow redirects from login.microsoft.com? Or would this be bad practice?
1条答案
按热度按时间0ve6wy6x1#
我可以回答您的部分问题...首先,对于受Azure AD保护的API应用程序,API应该做的是验证请求是否在请求标头中包含正确的访问令牌,如果是,则给予响应,如果否,则给出类似401或403的错误。正常的API应用程序不应该有用户登录的UI。无论如何,如果您希望在MVC项目中公开API,这是可以,但是对于API本身,它不应该有UI。
让我们看看下面的示例,我有一个.net 6 web API项目,下面是我的
program.cs
:而且它需要在appsetting.json中进行配置。
这是控制器:
我有一个Azure AD应用程序,我公开了一个API,如下所示:
我还为同一Azure AD应用添加了此API。
那么我们做一个测试,当我直接调用这个API的时候,会得到401错误:
如果我在请求中使用了过期的令牌,我也会得到401错误:
但如果我使用了正确的令牌(转到https://jwt.io来解码令牌,我们应该看到它包含正确的作用域,对我来说是
"scp": "Tiny.Read",
),我将得到响应:到目前为止,API部分已经完成。让我们看看客户端SPA。对于SPA,您应该集成
MSAL
,以便您可以让您的用户通过Azure AD登录,并生成访问令牌以调用MS图形API或您自己的API。生成访问令牌的代码应该相同,但您应该为不同的API设置不同的scope
。在我的场景中,我的API需要一个作用域Tiny.Read
,那么我应该在我的客户端应用程序中设置。这是generating access token in react的屏幕截图,你需要在代码中设置作用域。
现在你已经有了生成访问令牌的方法,你已经知道了API的url。然后你可以发送请求来调用API,使用 AJAX ,使用fetch,或者其他什么,发送http请求是可以的。而且在调用API部分,你还需要处理响应。如果响应代码是401,那么你需要做一些逻辑,也许会转到登录页面。你说你在这里遇到了麻烦,您遇到了CORS问题。我无法回答此部分。我认为这取决于您如何重定向到Azure AD登录页面。我'恐怕你可以看一下this sample来学习如何登录用户和调用图API。