31

ASP.NET MVC 5 (現在 RC1 バージョンを使用) で Web サイトを開発しています。このサイトは、ユーザー認証と初期プロファイル データの取得に Facebook を使用します。

認証システムには、新しい OWIN ベースの ASP.NET Identity エンジンを使用しています ( http://blogs.msdn.com/b/webdev/archive/2013/07/03/understanding-owin-forms-authentication-in-mvc -5.aspx )。これは、外部プロバイダーによる認証プロセスが大幅に簡素化されるためです。

問題は、ユーザーが最初にログインしたら、Facebook プロファイルからメール アドレスを取得したいのですが、このデータが生成されたクレームに含まれていないことです。だから私はアドレスを取得するためにこれらの代替案を考えました:

  1. ASP.NET ID エンジンに、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 )の代わりに「me」を指定します。しかし、アクセス トークンが必要であり、ASP.NET がユーザー データを取得するために使用するアクセス トークンを取得する方法 (または可能かどうか) がわかりません。

質問は次のとおりです。

  1. Facebook から追加情報を取得してクレーム データに含めるように ASP.NET Identity エンジンに指示するにはどうすればよいですか?

  2. または、生成されたアクセス トークンを取得して、Facebook 自身に問い合わせるにはどうすればよいですか?

ありがとうございました!

注: 認証システムの場合、私のアプリケーションは、この SO 回答にリンクされているサンプル プロジェクトに基づくコードを使用します: https://stackoverflow.com/a/18423474/4574

4

6 に答える 6

29

Startup.ConfigureAuth (StartupAuth.cs) で新しい Microsoft.Owin.Security.Facebook.AuthenticationOptions オブジェクトを作成し、それに FacebookAppId、FacebookAppSecret、および新しい AuthenticationProvider を渡します。ラムダ式を使用して OnAuthenticated メソッドにコードを渡し、context.Identity から抽出した値を含む ID にクレームを追加します。これにはデフォルトで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 では、外部 Cookie を使用して AuthenticationManager から ClaimsIdentity を抽出します。次に、アプリケーション Cookie を使用して作成された ID にそれを追加します。「...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);
        } 
    }
}

最後に、LOCAL AUTHORITY からのものではない値を表示したいと思います。/Account/Manage ページに表示される部分ビュー _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

また、フィードバック、修正、または改善をいただければ幸いです。

于 2013-10-23T18:28:10.617 に答える
5

FacebookAuthenticationOptionsのインスタンスを作成して構成する必要がありますProvider。には、ログイン時にトリガーされるというイベントProviderが含まれています。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);

上記のコードでは、access_tokenbyにアクセスして、現在ログインしているユーザーにcontext.AccessToken追加しています。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");
于 2013-12-19T21:27:56.713 に答える
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/>");
        }
    
于 2013-10-10T18:26:52.837 に答える
0

すべての答えに私の数セント... Facebook に自分で質問したい場合は、既存のFacebookパッケージを調べるのが理にかなっています。既に実装されているいくつかの優れた機能を提供するため、自分で再実装する必要はありません...ここで見つけることができる ASP.NET MVC アプリケーション内で使用する方法の例。

于 2014-03-28T09:28:09.513 に答える