2016-03-23 6 views
2

J'ai créé une application Web MVC 5 hébergée sur Azure et un client WPF. Mon objectif à court terme (comme si je peux réaliser que je serai en mesure de mettre en œuvre toutes mes utilisations cas) est la suivante:Problèmes d'authentification du client natif à l'application Web MVC sécurisée Azure AD

  • Imposer Azure Authentification de l'annonce sur le client WPF
  • Demandez à l'application Web MVC vérifier par Azure Graph API l'appartenance au groupe AD de l'utilisateur authentifié dans le client
  • renvoyer Graph objet API au client (IUser, groupe ...)
  • utilisation appartenance à un groupe pour définir l'autorisation sur les contrôleurs

Mon numéro actuel est le suivant: L'utilisateur lance l'application et demande une authentification. Je suppose que cela fonctionne comme je peux afficher le courrier de l'utilisateur et j'ai un jeton d'accès. L'utilisateur essaie d'accéder à un contrôleur Web API et ça fonctionne bien L'utilisateur essaie d'accéder à un autre contrôleur Web API décoré avec [Authorize] et je reçois en retour une page HTML indiquant: "Nous ne pouvons pas vous connecter.Votre le navigateur est actuellement configuré pour bloquer JavaScript. Vous devez autoriser JavaScript à utiliser ce service. " D'après ce que j'ai trouvé sur le web, il semble que cela puisse être lié à mon application Web qui n'est pas configurée correctement (j'ai déjà essayé d'ajouter mon URL webapp dans des sites de confiance et je suis sûr que mon contrôleur L'URL est OK). Je ne trouve pas beaucoup de doc sur le client natif + AAD + MVC donc je ne sais pas vraiment comment le corriger.

Voici mon startup.auth.cs de la webapp:

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); 
        } 

       } 

      }); 
    } 
} 

est ici le contrôleur qui peut être accédé lorsqu'ils ne sont pas décorées avec [Authorize], mais dans ce cas, l'action lancer une exception nulle (mais si je ne peut pas le faire réparer je posterai une autre question):

[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); 
     } 

et enfin ici sont parties pertinentes du client:

Dans la classe 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 

Dans le constructeur de mainviewmodel:

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

Mon signe dans la méthode:

{ 
      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; 
      } 
     } 

La méthode que l'accès au contrôleur protégé:

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)); 

      } 

Dans ma réponse de cas a le code de statut 200, mais le jsonString contient la page Web me disant à propos de JavaScript désactivé.

Si quelqu'un a une idée ce serait génial!

Merci!

Répondre

0

Si quelqu'un pénètre dans cette question, j'ai réussi à le résoudre en changeant ma méthode de configureAuth cette façon:

 var azureADBearerAuthOptions = new WindowsAzureActiveDirectoryBearerAuthenticationOptions 
     { 
      Tenant = tenant 
     }; 

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

     app.UseWindowsAzureActiveDirectoryBearerAuthentication(azureADBearerAuthOptions); 
0

Ce message d'erreur est très trompeur. Je recevais le même problème, et j'ai constaté que mon problème était en réalité incompatible avec les paramètres Client Secret/AppURI.

À partir du message d'erreur, j'ai supposé que c'était quelque chose que je faisais dans la méthode ConfigureAuth. Il s'avère que je mélangeais les paramètres de dev et de test.

Peut-être que cela aidera d'autres personnes qui se retrouvent sur ce message d'erreur confus.