简体   繁体   中英

How to retrieve Facebook profile picture from logged in user with ASP.Net Core Identity?

I've got a working solution for this, but I'm wondering if this is the correct way to do it. Here's what I got so far.

I'm using ASP.Net Core 1.1.2 with ASP.NET Core Identity 1.1.2.

The important part in Startup.cs looks like this:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        //...
        app.UseFacebookAuthentication(new FacebookOptions
        {
            AuthenticationScheme = "Facebook",
            AppId = Configuration["ExternalLoginProviders:Facebook:AppId"],
            AppSecret = Configuration["ExternalLoginProviders:Facebook:AppSecret"]
        });
    }

FacebookOptionscomes with Microsoft.AspNetCore.Authentication.Facebook nuget package.

The callback function in AccountController.cs looks like this:

    [HttpGet]
    [AllowAnonymous]
    public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
    {
        //... SignInManager<User> _signInManager; declared before
        ExternalLoginInfo info = await _signInManager.GetExternalLoginInfoAsync();
        SignInResult signInResult = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);

        byte[] thumbnailBytes = null;

        if (info.LoginProvider == "Facebook")
        {
            string nameIdentifier = info.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
            string thumbnailUrl = $"https://graph.facebook.com/{nameIdentifier}/picture?type=large";
            using (HttpClient httpClient = new HttpClient())
            {
                thumbnailBytes = await httpClient.GetByteArrayAsync(thumbnailUrl);
            }
        }
        //...
    }

So this code is working absolutely fine but, as mentioned before, is this the correct way (technically, not opinion-based) to do it?

To get profile picture from Facebook, you need to configure Facebook options and subscribe at OnCreatingTicket event from OAuth.

services.AddAuthentication().AddFacebook("Facebook", options =>
{

    options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
    options.ClientId = configuration.GetSection("ExternalLogin").GetSection("Facebook").GetSection("ClientId").Value;
    options.ClientSecret = configuration.GetSection("ExternalLogin").GetSection("Facebook").GetSection("ClientSecret").Value;
    options.Fields.Add("picture");
    options.Events = new OAuthEvents
    {
        OnCreatingTicket = context =>
        {
            var identity = (ClaimsIdentity)context.Principal.Identity;
            var profileImg = context.User["picture"]["data"].Value<string>("url");
            identity.AddClaim(new Claim(JwtClaimTypes.Picture, profileImg));
            return Task.CompletedTask;
        }
    };
});

In ASP.NET Core 3.0 there was a breaking change in OAuthCreatingTicketContext, see https://docs.microsoft.com/en-US/dotnet/core/compatibility/2.2-3.0

I changed

var profileImg = context.User["picture"]["data"].Value<string>("url");

to

var profileImg = context.User.GetProperty("picture").GetProperty("data").GetProperty("url").ToString();

我仅使用标识符从图形 api 中获取图像

$"https://graph.facebook.com/{identifier}/picture?type=large";

in asp.net core 3.1, I did this by calling Facebook APIs directly with the access token returned when the authentication is done. Here is the process:

In a controller method, you can challenge.

        var auth = await Request.HttpContext.AuthenticateAsync("Facebook");

This will redirect the user to Facebook login in the browser.

If the authentication succeeds, that is: auth.Succeeded && auth.Principal.Identities.Any(id => id.IsAuthenticated) && ! string.IsNullOrEmpty(auth.Properties.GetTokenValue("access_token") auth.Succeeded && auth.Principal.Identities.Any(id => id.IsAuthenticated) && ! string.IsNullOrEmpty(auth.Properties.GetTokenValue("access_token")

Retrieve the authentication token facebook provided like this: auth.Properties.GetTokenValue("access_token")

Then use the token to get the user's profil picture manually like this:

    public async Task<string> GetFacebookProfilePicURL(string accessToken)
    {
        using var httpClient = new HttpClient();
        var picUrl = $"https://graph.facebook.com/v5.0/me/picture?redirect=false&type=large&access_token={accessToken}";
        var res = await httpClient.GetStringAsync(picUrl);
        var pic = JsonConvert.DeserializeAnonymousType(res, new { data = new PictureData() });
        return pic.data.Url;
    }

Where PictureData is just a class representing the response from Facebook's graph API with all the info about the picture; Height, Width, url etc.

There is also a possibility to use custom claim actions to map json user content to claims (claim type to use is up to you). So image url will be added to claims collection, no need in OAuthEvents (if you don't need them for other purposes).

.AddFacebook("FbCustom", x =>
        {
            x.AppId = settings.FacebookAppId;
            x.AppSecret = settings.FacebookSecret;
            x.Scope.Add("email");
            x.Scope.Add("user_hometown");
            x.Scope.Add("user_birthday");
            x.Fields.Add("birthday");
            x.Fields.Add("picture");
            x.Fields.Add("name");
            x.Fields.Add("email");
            //HERE IS CUSTOM A MAPPING
            x.ClaimActions.MapCustomJson(CustomClaimTypes.AvatarUrl, 
            json => json.GetProperty("picture").GetProperty("data").GetProperty("url").GetString());
            
        })

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM