2013-10-18 6 views
13

Sto cercando di recuperare le proprietà degli utenti che vengono restituiti come contesto OnAuthenticated e aggiunto come affermazioni che seguono questo esempio: How to access Facebook private information by using ASP.NET Identity (OWIN)?Come accedere a Microsoft.Owin.Security.xyz ContestoAutenticato Valori AddClaims?

posso vedere che i dati mi aspetto che viene restituita al login e viene aggiunto come Rivendica all'interno di Starup.Auth.cs. Tuttavia, quando sono all'interno del controller account, le sole affermazioni che appaiono all'interno di UserManager o UserStore sono emesse dall'autorità locale. Nessun reclamo può essere trovato per Facebook (o altri fornitori esterni). Dove finiscono le affermazioni aggiunte al contesto? (Sto usando VS2013 RTM.)

sorgente completo e il sito in diretta su Azure legata qui: https://github.com/johndpalm/IdentityUserPropertiesSample/tree/VS2013rtm

Ecco quello che ho in Startup.Auth.cs:

var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions() 
{ 
    AppId = ConfigurationManager.AppSettings.Get("FacebookAppId"), 
    AppSecret = ConfigurationManager.AppSettings.Get("FacebookAppSecret"), 
    Provider = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationProvider() 
    { 
     OnAuthenticated = (context) => 
      { 
       const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string"; 
       foreach (var x in context.User) 
       { 
        var claimType = string.Format("urn:facebook:{0}", x.Key); 
        string claimValue = x.Value.ToString(); 
        if (!context.Identity.HasClaim(claimType, claimValue)) 
         context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, XmlSchemaString, "Facebook")); 

       } 
       context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, XmlSchemaString, "Facebook")); 
       return Task.FromResult(0); 
      } 
    } 

}; 

facebookOptions.Scope.Add("email"); 

app.UseFacebookAuthentication(facebookOptions); 

Un modo alternativo per catturare le proprietà d'accesso esterne sarebbe quella di aggiungere un singolo reclamo per il token di accesso e popolarlo con le proprietà:

const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string"; 
var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions 
{ 
    AppId = ConfigurationManager.AppSettings.Get("FacebookAppId"), 
    AppSecret = ConfigurationManager.AppSettings.Get("FacebookAppSecret"), 
    Provider = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationProvider() 
    { 
     OnAuthenticated = (context) => 
     { 
      var claim = new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, XmlSchemaString, "Facebook"); 
      foreach (var x in context.User) 
      { 
       string key = string.Format("urn:facebook:{0}", x.Key); 
       string value = x.Value.ToString(); 
       claim.Properties.Add(key, value); 
      } 

      context.Identity.AddClaim(claim); 

      return Task.FromResult(0); 
     } 
    } 
}; 

NOTA - Questo esempio non funziona: tu Sarebbe bello passare un singolo reclamo con proprietà. Il cookie esterno sembra sottolineare le proprietà dei crediti. Le proprietà sono vuote quando le recuperano in seguito dall'identità.

risposta

16

Sono stato in grado di creare un esempio funzionante, utilizzando modelli RTM MVC 5, OWIN e bit Identity ASP.NET. È possibile trovare la fonte completa e un link ad un esempio di lavoro vivo qui: https://github.com/johndpalm/IdentityUserPropertiesSample

Ecco cosa ha funzionato per me:

Creare un nuovo (nome del provider inserto qui) AuthenticationOptions oggetto in Startup.ConfigureAuth (StartupAuth.cs), passandogli l'id del client, il segreto del client e un nuovo AuthenticationProvider. Userai un'espressione lambda per passare il metodo OnAuthenticated del codice per aggiungere attestazioni all'identità che contengono i valori estratti da context.Identity.

StartUp.Auth.cs

// Facebook : Create New App 
// https://dev.twitter.com/apps 
if (ConfigurationManager.AppSettings.Get("FacebookAppId").Length > 0) 
{ 
    var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions() 
    { 
     AppId = ConfigurationManager.AppSettings.Get("FacebookAppId"), 
     AppSecret = ConfigurationManager.AppSettings.Get("FacebookAppSecret"), 
     Provider = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationProvider() 
     { 
      OnAuthenticated = (context) => 
       { 
        context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, XmlSchemaString, "Facebook")); 
        foreach (var x in context.User) 
        { 
         var claimType = string.Format("urn:facebook:{0}", x.Key); 
         string claimValue = x.Value.ToString(); 
         if (!context.Identity.HasClaim(claimType, claimValue)) 
          context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, XmlSchemaString, "Facebook")); 

        } 
        return Task.FromResult(0); 
       } 
     } 

    }; 
    app.UseFacebookAuthentication(facebookOptions); 
} 

NOTA: Il provider di autenticazione Facebook lavora con il codice utilizzato qui.Se si utilizza questo stesso codice con il fornitore di account Microsoft (o Foursquare provider creato utilizzando il codice account MS come modello), non riesce ad accedere. Se si seleziona solo il parametro access_token, funziona correttamente. Sembra che alcuni parametri interrompano il processo di accesso. (An issue has been opened on katanaproject.codeplex.com if progress on this is of interest to you.) Aggiornerò se trovo la causa. Non ho fatto molto con Twitter o Google oltre a verificare che potevo ottenere il token di accesso.

var msaccountOptions = new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationOptions() 
{ 
    ClientId = ConfigurationManager.AppSettings.Get("MicrosoftClientId"), 
    ClientSecret = ConfigurationManager.AppSettings.Get("MicrosoftClientSecret"), 
    Provider = new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationProvider() 
    { 
     OnAuthenticated = (context) => 
      { 
       context.Identity.AddClaim(new System.Security.Claims.Claim("urn:microsoftaccount:access_token", context.AccessToken, XmlSchemaString, "Microsoft")); 
       return Task.FromResult(0); 
      } 
    }     
}; 

app.UseMicrosoftAccountAuthentication(msaccountOptions); 

In AccountController, ho estrarre il ClaimsIdentity dal AuthenticationManager utilizzando il cookie esterno. Quindi lo aggiungo all'identità creata utilizzando il cookie dell'applicazione. Ho ignorato qualsiasi affermazione che inizia con "... schemas.xmlsoap.org/ws/2005/05/identity/claims" poiché sembrava interrompere l'accesso.

AccountController.cs

private async Task SignInAsync(CustomUser user, bool isPersistent) 
{ 
    AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie); 
    var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); 

// Extracted the part that has been changed in SignInAsync for clarity. 
    await SetExternalProperties(identity); 

    AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity); 
} 

private async Task SetExternalProperties(ClaimsIdentity identity) 
{ 
    // get external claims captured in Startup.ConfigureAuth 
    ClaimsIdentity ext = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie); 

    if (ext != null) 
    { 
     var ignoreClaim = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims"; 
     // add external claims to identity 
     foreach (var c in ext.Claims) 
     { 
      if (!c.Type.StartsWith(ignoreClaim)) 
       if (!identity.HasClaim(c.Type, c.Value)) 
        identity.AddClaim(c); 
     } 
    } 
} 

E, infine, voglio visualizzare tutto ciò che i valori non sono dalla LOCALE AUTORITA '. Ho creato una vista parziale _ExternalUserPropertiesListPartial visualizzata sullo /Account/Manage page. Ottengo le attestazioni che ho precedentemente memorizzato da AuthenticationManager.User.Claims e poi lo passo alla vista.

AccountController.cs

[ChildActionOnly] 
public ActionResult ExternalUserPropertiesList() 
{ 
    var extList = GetExternalProperties(); 
    return (ActionResult)PartialView("_ExternalUserPropertiesListPartial", extList); 
} 

private List<ExtPropertyViewModel> GetExternalProperties() 
{ 
    var claimlist = from claims in AuthenticationManager.User.Claims 
        where claims.Issuer != "LOCAL AUTHORITY" 
        select new ExtPropertyViewModel 
        { 
         Issuer = claims.Issuer, 
         Type = claims.Type, 
         Value = claims.Value 
        }; 

    return claimlist.ToList<ExtPropertyViewModel>(); 
} 

E tanto per essere approfondita, il punto di vista:

_ExternalUserPropertiesListPartial.cshtml

@model IEnumerable<MySample.Models.ExtPropertyViewModel> 

@if (Model != null) 
{ 
    <legend>External User Properties</legend> 
    <table class="table"> 
     <tbody> 
      @foreach (var claim in Model) 
      { 
       <tr> 
        <td>@claim.Issuer</td> 
        <td>@claim.Type</td> 
        <td>@claim.Value</td> 
       </tr> 
      } 
     </tbody> 
    </table> 
} 

Anche in questo caso, l'esempio di lavoro e il codice completo è su GitHub: https://github.com/johndpalm/IdentityUserPropertiesSample

E qualsiasi feedback, correzioni o miglioramenti sarebbero apprezzati.

+0

Ricevo un codice di errore 404 di Google quando utilizzo il codice. Da quello che posso dire a Google non supporta più questo, oauth2. –

1

Quindi questo articolo spiega come tutto questo funziona abbastanza bene: Decoupling owin external auth

Ma la risposta breve è, quando si arriva autenticato da Facebook, che si sta dando un'identità esterna. È quindi necessario prendere quell'identità esterna e "accedere" a un'identità dell'app locale, nel suo passaggio in cui è necessario aggiungere eventuali rivendicazioni dall'identità esterna a ClaimsIdentity che diventa User.Identity.

Edit: Per chiarire ulteriormente, si potrebbe farlo all'interno di ExternalLoginCallback:

// GET: /Account/ExternalLoginCallback 
    [AllowAnonymous] 
    public async Task<ActionResult> ExternalLoginCallback(string returnUrl) { 
     var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(); 
     if (loginInfo == null) { 
      return RedirectToAction("Login"); 
     } 

     // Sign in this external identity if its already linked 
     var user = await UserManager.FindAsync(loginInfo.Login); 
     if (user != null) { 
      await SignInAsync(user, isPersistent: false); 
      return RedirectToLocal(returnUrl); 
     } 

    private async Task SignInAsync(ApplicationUser user, bool isPersistent) { 
     AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie); 
     var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); 
     AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity); 
    } 

così hai bisogno di passare a dati aggiuntivi al SignIn, che sarà simile a questa:

ClaimsIdentity id = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie); 

Questo ClaimsIdentity avrà il reclamo aggiunto e sarà necessario aggiungere tale rivendicazione all'identità creata nel metodo SignInAsync affinché venga visualizzata.

+0

Hao, che è la mia comprensione ... Quando il debug in ExternalLoginCallback non vedo alcun reclamo ma "LOCALE AUTORITA" in AuthenticationManager (o UserManager). Dove dovrei vedere le affermazioni che ho aggiunto nel codice sopra? –

+0

Ho esattamente lo stesso problema. Sto aggiungendo le affermazioni e posso vederle aggiunte, ma quando provo a leggerle in seguito dal Contesto tutto quello che vedo è una sola affermazione LOCAL AUTHORITY:/ –

+0

L'unico modo in cui sono riuscito a ottenere un po 'di informazioni da Startup.Auth class è tramite un cookie che è semplicemente orribile e avrebbe bisogno di essere crittografato. La sessione non è disponibile e il metodo AddClaim è vuoto. Aggiungere un cookie criptato ogni volta sembra solo sbagliato. –

0

In breve la linea che è richiesta una volta AddClaim viene utilizzata è la seguente:

Tratto da Johns risposta sopra.

ClaimsIdentity ext = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);