如果我的应用程序使用Azure“ Easy Auth”

时间:2019-07-11 15:26:59

标签: asp.net-mvc azure azure-active-directory azure-web-sites microsoft-graph

我正在尝试使用Microsoft Graph SDK在已建立的ASP.NET MVC应用程序中对Azure Active Directory上的基本用户信息进行搜索。没有找到将两者结合使用的消息,而且我自己对实现的尝试都失败了,我想知道是否有人这样做了,或者是否知道这是不可能的。

当前,我们的应用程序托管在Azure应用服务(xyz.azurewebsites.net)上,该服务被配置为确保在访问该应用之前对所有请求进行身份验证。据了解,herehere将此称为“简易身份验证”。

作为测试,我或多或少遵循了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响应。但是,期望执行和处理成功的身份验证的代码永远不会这样做(在本文的底部显示)。

基本上,当前的流是这样的:

  1. 用户导航到xyz.azurewebsites.net
  2. xyz.azurewebsites.net看到用户未通过身份验证,并将他们带到login.microsoftonline.com/{tenant}/oauth2/authorize
  3. 用户输入其AAD凭据并登录,登录xyz.azurewebsites.net的根页面上

所有超出这一点的都是测试

  1. 为了进行测试,我导航到xyz.azurewebsites.net/signin,上面的Index()方法被触发(已通过远程调试确认)
  2. 挑战将我重定向到login.microsoftonline.com/common/oauth2/v2.0/authorize,然后我再次输入凭据
  3. 成功登录后,我被重定向到xyz.azurewebsites.net,并告知“您无权查看此目录或页面”。我期望成功通过身份验证时遇到的方法永远不会触发(通过远程调试确认)
  4. 导航到站点中的其他位置后,我可以访问这些页面,表明我仍在使用“轻松身份验证”进行身份验证。我测试我是否也可以访问Microsoft Graph的方法失败。

对于测试,我希望看到的是用户再次输入凭据,已成功重定向到主页,并使控制器能够以某种方式访问​​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);

        }
    }
}

1 个答案:

答案 0 :(得分:0)

两次登录非常奇怪。 App Service通过使用特殊标头将用户声明传递给您的应用程序。对于ASP.NET 4.6应用, ClaimsPrincipal 会自动设置为适当的值。

一些示例标题包括:

  • X-MS-CLIENT-PRINCIPAL-NAME
  • X-MS-CLIENT-PRINCIPAL-ID

如果您只想acess user claims,则可以使用这种方式。

如果您需要调用其他图形API,也可以在请求标头中get the access token