31

我正在開發ASP.NET MVC 5中的網站(目前使用RC1版本)。該網站將使用Facebook進行用戶身份驗證並檢索初始配置文件數據。如何通過使用ASP.NET身份(OWIN)訪問Facebook隱私信息?

對於身份驗證系統,我使用基於OWIN的新的基於ASP.NET的身份引擎(http://blogs.msdn.com/b/webdev/archive/2013/07/03/understanding-owin-forms-authentication-in-mvc-5.aspx),因爲它極大地簡化了與外部提供者進行身份驗證的過程。

問題是,一旦用戶首次登錄,我想從Facebook配置文件中獲取其電子郵件地址,但這些數據不包含在生成的聲明中。於是我想到了這些替代品來獲取地址:

  1. 責成ASP.NET識別引擎包括電子郵件地址 的一組是從Facebook檢索,然後轉換 權利要求的數據。我不知道這是否可能。

  2. 使用Facebook圖形API (https://developers.facebook.com/docs/getting-started/graphapi)至 通過使用Facebook用戶ID(其包括在權利要求數據 )檢索的電子郵件地址。但是如果用戶有 將他的電子郵件地址設置爲私人,這將不起作用。

  3. 使用Facebook的圖形API,但指定 「我」 而不是 的Facebook用戶ID (https://developers.facebook.com/docs/reference/api/user)的。但是需要一個 訪問令牌,並且我不知道如何(或者如果它可能是 )檢索ASP.NET使用的訪問令牌來獲取用戶數據的 。

所以,問題是:

  1. 我如何可以指示ASP.NET識別引擎從Facebook獲取 更多的信息,包括它在權利要求 數據?

  2. 或者,我該如何檢索生成的訪問令牌,以便我可以詢問Facebook自己的 ?

謝謝!

注:我的應用程序使用基於該鏈接的示例項目代碼的認證系統,這樣回答:https://stackoverflow.com/a/18423474/4574

回答

24

爲了從Facebook獲取更多信息,您可以指定作用域你想,當你配置了Facebook,包括認證選項。獲得該檢索出可以通過實現供應商的OnAuthenticated方法是這樣實現的附加信息:

var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions() 
{ 
    Provider = new FacebookAuthenticationProvider() 
    { 
     OnAuthenticated = (context) => 
      { 
       // All data from facebook in this object. 
       var rawUserObjectFromFacebookAsJson = context.User; 

       // Only some of the basic details from facebook 
       // like id, username, email etc are added as claims. 
       // But you can retrieve any other details from this 
       // raw Json object from facebook and add it as claims here. 
       // Subsequently adding a claim here will also send this claim 
       // as part of the cookie set on the browser so you can retrieve 
       // on every successive request. 
       context.Identity.AddClaim(...); 

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

//Way to specify additional scopes 
facebookOptions.Scope.Add("..."); 

app.UseFacebookAuthentication(facebookOptions); 

每代碼here我看到了電子郵件地址已經被檢索並添加爲要求在這裏如果Facebook已經發送。你無法看到它嗎?

+3

謝謝!訣竅是調用'facebookOptions.Scope.Add(「email」)',然後如您所說,電子郵件數據會自動添加爲聲明,而無需解析json數據。然而,我現在有一個新問題:當使用你的代碼時,在回調中調用'AuthenticationManager.GetExternalIdentity'將返回null而不是'ClaimsIdentity'的實例。你有什麼想法可以發生? (是的,我將正確的應用程序ID和祕密添加到facebookOptions對象) – Konamiman

+1

好吧,找到解決方案從這裏調查:http://forums.asp.net/p/1927914/5516103.aspx?p=True&t= 635154678323993438。以下必須添加到facebookOptions對象初始化中:'SignInAsAuthenticationType =「External」'。我會接受你的答案,但也許你想編輯它,使其包含這些技巧。 :-) – Konamiman

+1

您可以請分享示例代碼以顯示您如何檢索回調actionresult任務內的其他信息(比如說BirthDate)嗎? – Santosh

2

這裏有一些步驟可以幫助你。我正在撰寫博客文章,但這需要一段時間...... - 添加作用域FB裏提供商,並添加從FB返回索賠

app.UseFacebookAuthentication(new FacebookAuthenticationOptions() 
     { 
      AppId = "", 
      AppSecret = "", 
      //Scope = "email,user_about_me,user_hometown,friends_about_me,friends_photos", 
      Provider = new FacebookAuthenticationProvider() 
      { 
       OnAuthenticated = async context => 
       { 
        foreach (var x in context.User) 
        { 
         context.Identity.AddClaim(new System.Security.Claims.Claim(x.Key, x.Value.ToString())); 
        } 
        //Get the access token from FB and store it in the database and use FacebookC# SDK to get more information about the user 
        context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken)); 
       } 
      }, 
      SignInAsAuthenticationType = "External", 
     });   
  • 使用訪問令牌,並呼籲Facebook的C#SDK獲取好友列表供用戶

    var claimsIdentity = HttpContext.User.Identity as ClaimsIdentity; 
        var access_token = claimsIdentity.FindAll("FacebookAccessToken").First().Value; 
        var fb = new FacebookClient(access_token); 
        dynamic myInfo = fb.Get("/me/friends"); 
        var friendsList = new List<FacebookViewModel>(); 
        foreach (dynamic friend in myInfo.data) 
        { 
         friendsList.Add(new FacebookViewModel() { Name = friend.name, ImageURL = @"https://graph.facebook.com/" + friend.id + "/picture?type=large" }); 
         //Response.Write("Name: " + friend.name + "<br/>Facebook id: " + friend.id + "<br/><br/>"); 
        } 
    
  • 數據
+0

我跟着你的示例,但claimIdentity = HttpContext.User.Identity作爲ClaimsIdentity只包含LOCAL索賠而不是facebook的索賠。 :〜/ –

+0

你可以試試HttpContext.Current.GetOwinContext()。Authentication.User? – Praburaj

+0

太好了,你如何從MS Live外部登錄中獲得用戶郵件,範圍爲「wl.basic」和「wl.emails」? – subsci

29

在Startup.ConfigureAuth(StartupAuth.cs)中創建一個新的Microsoft.Owin.Security.Facebook.AuthenticationOptions對象,並將它傳遞給FacebookAppId,FacebookAppSecret和一個新的AuthenticationProvider。您將使用一個lambda表達式來傳遞OnAuthenticated方法的一些代碼,以將聲明添加到包含您從context.Identity中提取的值的標識中。這將包括默認情況下access_token。您必須將電子郵件添加到範圍。其他用戶屬性可從context.User獲取(例如,請參閱底部的鏈接)。

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")); 
        context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:email", context.Email, XmlSchemaString, "Facebook")); 
        return Task.FromResult(0); 
       } 
     } 

    }; 
    facebookOptions.Scope.Add("email"); 
    app.UseFacebookAuthentication(facebookOptions); 
} 

在的AccountController,我提取使用外部餅乾從所述的AuthenticationManager ClaimsIdentity。然後我將它添加到使用應用程序cookie創建的身份。我忽略了任何以「... schemas.xmlsoap.org/ws/2005/05/identity/claims」開頭的聲明,因爲它似乎中斷登錄。

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

最後,我想顯示什麼值不是來自地方政府。我創建了出現在/Account/Manage page上的局部視圖_ExternalUserPropertiesListPartial。我得到我以前從AuthenticationManager.User.Claims存儲的聲明,然後將其傳遞給視圖。

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

而只是爲了徹底,認爲:

_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> 
} 

工作實例和完整的代碼是在GitHub上:https://github.com/johndpalm/IdentityUserPropertiesSample

和任何反饋,更正離子,或改進將不勝感激。

+0

當我嘗試這個並添加'accesstoken'聲明時,對User.Identity.GetUserId()方法的後續調用返回null。剛剛添加1聲明似乎打破了User.Identity對象... – creatiive

21

它爲我只有這Startup.Auth

var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions()  { 
    AppId = "*", 
    AppSecret = "**" 
}; 
facebookOptions.Scope.Add("email"); 
app.UseFacebookAuthentication(facebookOptions); 

然後在方法ExternalLoginCallbackExternalLoginConfirmation你的電子郵件:

ClaimsIdentity ext = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie); 
var email = ext.Claims.First(x => x.Type.Contains("emailaddress")).Value; 
+3

您可以將其用於第二個代碼片段:var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(); string email = loginInfo.Email; –

4

您需要創建的FacebookAuthenticationOptions實例並配置ProviderProvider包含一個名爲OnAuthenticated的事件,當您登錄時會觸發該事件。

var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions 
{ 
    Provider = new FacebookAuthenticationProvider() 
    { 
     OnAuthenticated = (context) => 
     { 
      context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, ClaimValueTypes.String, "Facebook")); 

      return Task.FromResult(0); 
     } 
    }, 

    // You can store these on AppSettings 
    AppId = ConfigurationManager.AppSettings["facebook:AppId"], 
    AppSecret = ConfigurationManager.AppSettings["facebook:AppSecret"] 
}; 

app.UseFacebookAuthentication(facebookOptions); 

在上面的代碼我被context.AccessToken訪問access_token,並將其添加到當前的loggedIn用戶的Claims

要訪問此值後,你需要這樣做:

var owinContext = HttpContext.GetOwinContext(); 
var authentication = owinContext.Authentication; 
var user = autentication.User; 
var claim = (user.Identity as ClaimsIdentity).FindFirst("urn:facebook:access_token"); 

string accessToken; 
if (claim != null) 
    accessToken = claim.Value; 

爲了簡化這一切,你可以創建一個BaseController,使你的所有Controllers繼承它。

BaseController代碼將是:

public class BaseController : Controller 
{ 
    public IOwinContext CurrentOwinContext 
    { 
     get 
     { 
      return HttpContext.GetOwinContext(); 
     } 
    } 

    public IAuthenticationManager Authentication 
    { 
     get 
     { 
      return CurrentOwinContext.Authentication; 
     } 
    } 

    public new ClaimsPrincipal User 
    { 
     get 
     { 
      return Authentication.User; 
     } 
    } 

    public ClaimsIdentity Identity 
    { 
     get 
     { 
      return Authentication.User.Identity as ClaimsIdentity; 
     } 
    } 

    public string FacebookAccessToken 
    { 
     get 
     { 
      var claim = Identity.FindFirst("urn:facebook:access_token"); 

      if (claim == null) 
       return null; 

      return claim.Value; 
     } 
    } 
} 

然後讓你的代碼的訪問令牌,你只需要訪問屬性FacebookAccessToken

string accessToken = FacebookAccessToken; 

它可以檢索某些其它值

context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:username", 
    context.User.Value<string>("username"), ClaimValueTypes.String, "Facebook")); 

context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:name", 
    context.User.Value<string>("name"), ClaimValueTypes.String, "Facebook")); 

請注意,並非所有字段將可用,讓你需要要求Scope電子郵件的電子郵件。

facebookOptions.Scope.Add("email"); 

然後在OnAuthenticated事件訪問作爲

context.User.Value<string>("email"); 
0

我的幾個美分,收於所有的答案...如果你還是要問自己的Facebook,是有意義的承擔現有Facebook看看包。它提供了一些已經實現的強大功能,所以你不必自己重新實現它...一些例子如何在ASP.NET MVC應用程序中使用它,你可以找到here

相關問題