麻烦将本机客户端验证到Azure AD安全的MVC Web应用程序

时间:2016-03-23 13:03:39

标签: asp.net-mvc wpf azure asp.net-mvc-5 adal

我创建了一个托管在Azure上的MVC 5 Web应用程序和一个WPF客户端。我的短期目的(好像我可以实现我能够实现我的所有用例)如下:

  • 在WPF客户端上强制执行A​​zure广告验证
  • 让MVC网络应用通过Azure Graph API检查在客户端验证的用户的AD组成员身份
  • 将Graph API对象发送回客户端(IUser,Group ...)
  • 使用组成员资格定义控制器上的授权

我的实际问题如下: 用户启动应用程序,并提示进行身份验证。我想它可以工作,因为我可以显示用户的邮件,我有一个访问令牌。 用户尝试访问web api控制器,它工作正常 用户试图访问另一个用[Authorize]修饰的web api控制器,我得到一些HTML页面,说明:“我们无法登录你。你的浏览器目前设置为阻止JavaScript。你需要允许JavaScript使用这项服务。“

从我在网上搜索到的内容看来,它可能与我的网络应用程序配置不正确(我已经尝试在受信任的网站中添加我的webapp网址,我确信我的控制器网址好的)。我在本机客户端+ AAD + MVC上找不到太多doc,所以我真的不知道如何纠正它。

这是我在webapp上的startup.auth.cs:

public partial class Startup
{

    private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
    private static string appKey = ConfigurationManager.AppSettings["ida:AppKey"];
    private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
    private static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
    private static string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
    private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
    private static string certName = ConfigurationManager.AppSettings["ida:CertName"];

    public static readonly string Authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);

    string graphResourceId = ConfigurationManager.AppSettings["ida:GraphUrl"];

    public void ConfigureAuth(IAppBuilder app)
    {
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
        app.UseCookieAuthentication(new CookieAuthenticationOptions());  

        app.UseOpenIdConnectAuthentication(
            new OpenIdConnectAuthenticationOptions
            {

                ClientId = clientId,
                Authority = Authority,
                PostLogoutRedirectUri = postLogoutRedirectUri,

                Notifications = new OpenIdConnectAuthenticationNotifications()
                {
                    //
                    // If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
                    //

                    AuthorizationCodeReceived = (context) =>
                    {
                        var code = context.Code;

                        #region Certs (not used)
                        if (certName.Length != 0)
                        {
                            // Create a Client Credential Using a Certificate
                            //
                            // Initialize the Certificate Credential to be used by ADAL.
                            // First find the matching certificate in the cert store.
                            //

                            X509Certificate2 cert = null;
                            X509Store store = new X509Store(StoreLocation.CurrentUser);
                            try
                            {
                                store.Open(OpenFlags.ReadOnly);
                                // Place all certificates in an X509Certificate2Collection object.
                                X509Certificate2Collection certCollection = store.Certificates;
                                // Find unexpired certificates.
                                X509Certificate2Collection currentCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
                                // From the collection of unexpired certificates, find the ones with the correct name.
                                X509Certificate2Collection signingCert = currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, certName, false);
                                if (signingCert.Count == 0)
                                {
                                    // No matching certificate found.
                                    return Task.FromResult(0);
                                }
                                // Return the first certificate in the collection, has the right name and is current.
                                cert = signingCert[0];
                            }
                            finally
                            {
                                store.Close();
                            }

                            // Then create the certificate credential.
                            ClientAssertionCertificate credential = new ClientAssertionCertificate(clientId, cert);

                            string userObjectID = context.AuthenticationTicket.Identity.FindFirst(
                                "http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
                            AuthenticationContext authContext = new AuthenticationContext(Authority, new NaiveSessionCache(userObjectID));
                            AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
                                code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
                            AuthenticationHelper.token = result.AccessToken;
                        } 
                        #endregion
                        else
                        {
                            // Create a Client Credential Using an Application Key
                            ClientCredential credential = new ClientCredential(clientId, appKey);
                            string userObjectID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
                            AuthenticationContext authContext = new AuthenticationContext(Authority, new NaiveSessionCache(userObjectID));
                            AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
                                code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
                            AuthenticationHelper.token = result.AccessToken;
                        }


                        return Task.FromResult(0);
                    }

                }

            });
    }
}

这是在没有[Authorize]修饰的情况下可以加入的控制器,但是在这种情况下,动作抛出一个空异常(但是如果我不能修复它,我会发布另一个问题):

[System.Web.Http.Authorize]
public class UserADGraphController : ApiController
{

    [ResponseType(typeof(IUser))]
    [System.Web.Http.Route("api/UserADGraphController/GetMyInformations")]
    public IHttpActionResult GetMyInformations()
    {
        try
        {
            string uID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
            if (uID == null)
                return Ok("UId null");
            ActiveDirectoryClient client = AuthenticationHelper.GetActiveDirectoryClient();
            if (client == null)
                return Ok("Client null");
            IUser adUser = client.Users.Where(u => u.ObjectId == uID).ExecuteAsync().Result.CurrentPage.SingleOrDefault();

            if (adUser == null)
            {
                return NotFound();
            }

            return Ok(adUser);
        }
        catch (Exception e)
        {
            return Ok(e.Message + " " + e.StackTrace);
        }

最后这里是客户的相关部分:

在mainviewmodel类中:

#region Azure AD auth properties
    private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
    private static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
    private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
    Uri redirectUri = new Uri(ConfigurationManager.AppSettings["ida:RedirectUri"]);

    private static string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);

    private static string AppServiceResourceId = ConfigurationManager.AppSettings["todo:AppServiceResourceId"];
    private static string AppServiceBaseAddress = ConfigurationManager.AppSettings["todo:AppServiceBaseAddress"];

    private HttpClient httpClient;
    private AuthenticationContext authContext = null;
    #endregion

在mainviewmodel构造函数中:

authContext = new AuthenticationContext(authority);
httpClient = new HttpClient();

我的登录方法:

{
            AuthenticationResult result = null;
            try
            {
                result = authContext.AcquireToken(AppServiceResourceId, clientId, redirectUri, PromptBehavior.Auto);
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
                SignInLabelContent = "Connected to azure AD as " + result.UserInfo.DisplayableId;

            }
            catch (AdalException ex)
            {
                if (ex.ErrorCode == "user_interaction_required")
                {

                }
                else
                {
                    // An unexpected error occurred.
                    string message = ex.Message;
                    if (ex.InnerException != null)
                    {
                        message += "Inner Exception : " + ex.InnerException.Message;
                    }
                    Messenger.Default.Send<NotificationMessage>(new NotificationMessage(message));
                    //MessageBox.Show(message);
                }
                return;
            }
        }

访问受保护控制器的方法:

IUser me = null;

            AuthenticationResult result = null;

            result = authContext.AcquireToken(AppServiceResourceId, clientId, redirectUri, PromptBehavior.Auto);

            string authHeader = result.CreateAuthorizationHeader();
            HttpClient client = new HttpClient();
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
            //HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, AppServiceBaseAddress + "/api/UserADGraphController/GetMyInformations");
            //request.Headers.TryAddWithoutValidation("Authorization", authHeader);
            //HttpResponseMessage response = await client.SendAsync(request);
            //string responseString = await response.Content.ReadAsStringAsync();
            //LogManager.log(responseString);
            //Messenger.Default.Send<NotificationMessage>(new NotificationMessage(responseString));

            HttpResponseMessage response = await httpClient.GetAsync(AppServiceBaseAddress + "/api/UserADGraphController/GetMyInformations");

            if (response.IsSuccessStatusCode)
            {
                var jsonString = await response.Content.ReadAsStringAsync();
                LogManager.log(jsonString);
                me = JsonConvert.DeserializeObject<IUser>(jsonString);

                //Messenger.Default.Send<NotificationMessage>(new NotificationMessage(jsonString));

            }

在我的情况下,响应的状态代码为200,但是jsonString包含告诉我有关禁用javascript的网页。

如果有人有想法那就太棒了!

谢谢!

2 个答案:

答案 0 :(得分:0)

如果有人遇到此问题,我设法通过以这种方式更改我的configureAuth方法来解决它:

       var azureADBearerAuthOptions = new WindowsAzureActiveDirectoryBearerAuthenticationOptions
        {
            Tenant = tenant
        };

        azureADBearerAuthOptions.TokenValidationParameters = new TokenValidationParameters()
        {
            ValidAudience = audience
        };

        app.UseWindowsAzureActiveDirectoryBearerAuthentication(azureADBearerAuthOptions);

答案 1 :(得分:0)

此错误消息非常具有误导性。我遇到了同样的问题,发现我的问题实际上与Client Secret / AppURI设置不匹配。

从错误消息中,我认为这是我在ConfigureAuth方法中所做的事情。事实证明我正在混合开发和测试设置。

也许这会帮助那些最终出现这个令人困惑的错误消息的人。