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

5

6

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?

Charlesettacharleston answered 24/8, 2017 at 7:39 Comment(0)
T
7

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;
        }
    };
});
Thylacine answered 28/8, 2018 at 12:35 Comment(4)
what is a life cycle of this "identity" claim? It is not "observable" in my contoller method.It seems like this identity exists only in context of communcation with oauth providerImparadise
It occurs after succesfull login on facebook. The default method is ExternalLoginCallback. Created from Asp.net Core template.Thylacine
Thanks Buno. But how you access them in the controller? ClaimsPrincipal is empty even after successfull login.. (Not full empty - it contains some items, but not those).Imparadise
The link provided in 8.0 download the picture, how can it used in img?Grueling
R
3

In ASP.NET Core 3.0 there was a breaking change in OAuthCreatingTicketContext, see https://learn.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();
Recall answered 6/1, 2020 at 21:38 Comment(1)
Thanks. This complete @Thylacine answer.Grueling
E
1

I got the image from the graph api only using the identifier

$"https://graph.facebook.com/{identifier}/picture?type=large";
Eel answered 30/10, 2019 at 2:45 Comment(1)
you are using an obsolete version of the api that will be deprecated. I wont downtvote your answer because is right but take consideration change your codeGrueling
E
1

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());
            
        })
Expiratory answered 28/7, 2020 at 10:15 Comment(1)
Not sure if it's good practice to create a custom claim or not - but anyways this done the job for me thanks!Incunabulum
C
1

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

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.

Chartist answered 17/8, 2020 at 11:2 Comment(6)
take consideration this only work with graph api 5.0, not 8.0 when image is downloadedGrueling
Hello, please what do you mean by when image is downloaded ?Chartist
With API GRAPH 8.0 the image is corrupt in the browser, can only be download, not migrated into <img src=url>Grueling
Please can you send a link, to the docs about this ? ThanksChartist
There is no documentation about this behavior, but you can test it in the API GRAPH selecting the version 8.0 (the latest). This is correct answer so I vote it up, but sadly doesn't work in the new version :( I've a question about it and not answers :(Grueling
#64074825Grueling

© 2022 - 2024 — McMap. All rights reserved.