我正在尝试使用Microsoft Graph SDK在已建立的ASP.NET MVC应用程序中对Azure Active Directory上的基本用户信息进行搜索。没有找到将两者结合使用的消息,而且我自己对实现的尝试都失败了,我想知道是否有人这样做了,或者是否知道这是不可能的。
当前,我们的应用程序托管在Azure应用服务(xyz.azurewebsites.net
)上,该服务被配置为确保在访问该应用之前对所有请求进行身份验证。据了解,here或here将此称为“简易身份验证”。
作为测试,我或多或少遵循了this tutorial中的步骤,并了解它适用于Azure Active Directory应用程序注册。我向项目添加了准系统控制器,该控制器调用了另一个身份验证挑战,如下所示:
public void Index()
{
// Signal OWIN to send an auth request to Azure
Request.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
身份验证质询可以成功完成(即使已经通过xyz.azurewebsites.net
对用户进行了授权)。检查挑战完成后的响应,表明我确实在表单数据中收到了code
响应。但是,期望执行和处理成功的身份验证的代码永远不会这样做(在本文的底部显示)。
基本上,当前的流是这样的:
xyz.azurewebsites.net
xyz.azurewebsites.net
看到用户未通过身份验证,并将他们带到login.microsoftonline.com/{tenant}/oauth2/authorize
xyz.azurewebsites.net
的根页面上所有超出这一点的都是测试
xyz.azurewebsites.net/signin
,上面的Index()
方法被触发(已通过远程调试确认)login.microsoftonline.com/common/oauth2/v2.0/authorize
,然后我再次输入凭据xyz.azurewebsites.net
,并告知“您无权查看此目录或页面”。我期望成功通过身份验证时遇到的方法永远不会触发(通过远程调试确认)对于测试,我希望看到的是用户再次输入凭据,已成功重定向到主页,并使控制器能够以某种方式访问Microsoft Graph。
想法
一些可能阻止成功的想法:
有可能我所要求的根本无法完成,我们将不得不从另一个角度对此进行攻击。
我认为步骤6中概述的权限被拒绝错误是由于Graph质询的回调不包括通常由Easy Auth添加到请求标头中的访问令牌。因此,随后的请求将被Easy Auth拦截并工作,但是由于初始回调失败,因此未命中预期在成功挑战后执行的代码。
如果可能,可能需要将应用程序服务配置为返回Microsoft Graph的访问令牌。
OWIN启动代码
using Owin;
using Microsoft.Graph;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Notifications;
using Microsoft.Owin.Security.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Identity.Client;
using System.Configuration;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using xyz.Utilities;
[assembly: OwinStartup(typeof(xyz.Startup))]
namespace xyz
{
public class Startup
{
private static string appId = ConfigurationManager.AppSettings["AzureActiveDirectoryAppId"];
private static string appSecret = ConfigurationManager.AppSettings["AzureActiveDirectoryAppSecret"];
private static string redirectUri = ConfigurationManager.AppSettings["AzureActiveDirectoryRedirectUri"];
private static string graphScopes = ConfigurationManager.AppSettings["AzureActiveDirectoryGraphScopes"];
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
// This executes upon startup and configures the app.
}
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = appId,
Authority = "https://login.microsoftonline.com/common/v2.0",
Scope = $"openid email profile offline_access {graphScopes}",
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false // May need to be changed. See https://docs.microsoft.com/en-us/dotnet/api/microsoft.identitymodel.tokens.tokenvalidationparameters?view=azure-dotnet
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailedAsync,
AuthorizationCodeReceived = OnAuthorizationCodeReceivedAsync
}
}
);
}
private Task OnAuthenticationFailedAsync(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
notification.HandleResponse();
throw new System.Exception("Authentication Failed"); // This may need to be changed to redirect the user so they can try again.
}
private async Task OnAuthorizationCodeReceivedAsync(AuthorizationCodeReceivedNotification notification)
{
// This function does not execute once authentication is complete through /SignIn
var idClient = ConfidentialClientApplicationBuilder.Create(appId)
.WithRedirectUri(redirectUri)
.WithClientSecret(appSecret)
.Build();
var signedInUser = new ClaimsPrincipal(notification.AuthenticationTicket.Identity);
var tokenStore = new SessionTokenStore(idClient.UserTokenCache, HttpContext.Current, signedInUser);
string[] scopes = graphScopes.Split(' ');
var result = await idClient.AcquireTokenByAuthorizationCode(scopes, notification.Code).ExecuteAsync();
User userDetails = await GraphUtility.GetUserDetailAsync(result.AccessToken);
string email = string.IsNullOrEmpty(userDetails.Mail) ? userDetails.UserPrincipalName : userDetails.Mail;
var cachedUser = new CachedUser()
{
DisplayName = userDetails.DisplayName,
Email = string.IsNullOrEmpty(userDetails.Mail) ? userDetails.UserPrincipalName : userDetails.Mail
};
tokenStore.SaveUserDetails(cachedUser);
}
}
}
答案 0 :(得分:0)
两次登录非常奇怪。 App Service通过使用特殊标头将用户声明传递给您的应用程序。对于ASP.NET 4.6应用, ClaimsPrincipal 会自动设置为适当的值。
一些示例标题包括:
如果您只想acess user claims,则可以使用这种方式。
如果您需要调用其他图形API,也可以在请求标头中get the access token。