3

If user is authenticated via google, I need to get his profile picture.

If user is authenticated via facebook I get his profile picture with this code:

var info = await _signInManager.GetExternalLoginInfoAsync();
var identifier = info.Principal.FindFirstValue(ClaimTypes.NameIdentifier); 
var picture = $"https://graph.facebook.com/{identifier}/picture"; // 3

So, which code I need to use in 3 line for getting user's profile picture in case user is authenticated via google?

Roomey
  • 716
  • 2
  • 9
  • 16
  • You need to request the profile scope when requesting authorization for the users. Unfortunately Facebook code is not going to connect to google. Try using the google .net client library. I dont think it will be three lines of code though to authenticate with google – Linda Lawton - DaImTo Jul 02 '20 at 20:25
  • @DalmTo I know how to authenticate with google, I want to know how to get profile picture of user who had authenticated – Roomey Jul 02 '20 at 20:38
  • Did you try the People api as i suggested? – Linda Lawton - DaImTo Jul 03 '20 at 06:38
  • @DalmTo https://developers.google.com/people/v1/how-tos/authorizing#OAuth2Authorizing here is no information about profile image, only about email, age and e.t.c. – Roomey Jul 03 '20 at 08:40

2 Answers2

2

Accessing user profile picture, full solution.

Stack:
Asp Net Identity 4 v4 + Asp Net Identity + Google People Api

Steps

1. Preparation

1.1 I'm using default HttpClient to generate HTTP requests to Google API
1.2 Install NUGET System.Net.Http.Json to ease serialization. 1.3 Use a [json to c#] tool, to create a DTO like this:


/// <summary>
    ///  official docs.
    /// https://developers.google.com/people/api/rest/v1/people#Person.Photo
    /// </summary>
    public class PeopleApiPhotos    {
        
        public string resourceName { get; set; } 
        public string etag { get; set; } 
        public List<Photo> photos { get; set; }
        
        public class Source    {
            public string type { get; set; } 
            public string id { get; set; } 
        }

        public class Metadata    {
            public bool primary { get; set; } 
            public Source source { get; set; } 
        }

        public class Photo    {
            public Metadata metadata { get; set; } 
            public string url { get; set; } 
        }
    }

2. Identity Server: when users confirms external info data, send http get request, to retrieve avatar picture url:

Identity Server with Asp Net Identity > Login > OnPostConfirmationAsync: FULL METHOD


public async Task < IActionResult > OnPostConfirmationAsync(string returnUrl = null) {
  returnUrl = returnUrl ? ?Url.Content("~/");
  // Get the information about the user from the external login provider
  var info = await _signInManager.GetExternalLoginInfoAsync();
  if (info == null) {
    ErrorMessage = "Error loading external login information during confirmation.";
    return RedirectToPage("./Login", new {
      ReturnUrl = returnUrl
    });
  }

  // try to get profile picture
  string pictureUri = string.Empty;

  if (info.LoginProvider.ToLower() == "google") {
    var httpClient = _httpClientFactory.CreateClient();
    string peopleApiKey = _configuration["GoogleApiKey:PeopleApiKey"];;
    var googleAccountId = info.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
    var photosResponse = await httpClient.GetFromJsonAsync < PeopleApiPhotos > (
    $ "https://people.googleapis.com/v1/people/{googleAccountId}?personFields=photos&key={peopleApiKey}");
    pictureUri = photosResponse ? .photos.FirstOrDefault() ? .url;
  }

  if (ModelState.IsValid) {
    // Cria usuário
    var user = new AppUser {
      UserName = Input.Email,
      Email = Input.Email,
      FirstName = Input.FirstName,
      LastName = Input.LastName,
      ProfilePictureUrl = pictureUri
    };

    var result = await _userManager.CreateAsync(user);
    if (result.Succeeded) {
      result = await _userManager.AddLoginAsync(user, info);
      if (result.Succeeded) {
        _logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider);

        var userId = await _userManager.GetUserIdAsync(user);
        var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
        code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
        var callbackUrl = Url.Page("/Account/ConfirmEmail", pageHandler: null, values: new {
          area = "Identity",
          userId = userId,
          code = code
        },
        protocol: Request.Scheme);

        await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", $ "Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

        // If account confirmation is required, we need to show the link if we don't have a real email sender
        if (_userManager.Options.SignIn.RequireConfirmedAccount) {
          return RedirectToPage("./RegisterConfirmation", new {
            Email = Input.Email
          });
        }

        await _signInManager.SignInAsync(user, isPersistent: false, info.LoginProvider);

        return LocalRedirect(returnUrl);
      }
    }
    foreach(var error in result.Errors) {
      ModelState.AddModelError(string.Empty, error.Description);
    }
  }

  ProviderDisplayName = info.ProviderDisplayName;
  ReturnUrl = returnUrl;
  return Page();
}

2.1 Identity Server with Asp Net Identity > Login > OnPostConfirmationAsync: HIGHLIGHT:

❗️ this is only a little part from full method above.

Checks if external provider is Google, and use [NameIdentifier] and [Google Api Key] to reach People Endpoint.


// try to get profile picture
string pictureUri = string.Empty;

if (info.LoginProvider.ToLower() == "google") {
  var httpClient = _httpClientFactory.CreateClient();
// ApiKey can get generated in [Google Developers Console](https://console.developers.google.com/apis/credentials).
  string peopleApiKey = _configuration["GoogleApiKey:PeopleApiKey"];;
  var googleAccountId = info.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
  var photosResponse = await httpClient.GetFromJsonAsync < PeopleApiPhotos > (
  $ "https://people.googleapis.com/v1/people/{googleAccountId}?personFields=photos&key={peopleApiKey}");
  pictureUri = photosResponse ? .photos.FirstOrDefault() ? .url;
}

3. Identity Server > Profile Service Implementation:


public async Task GetProfileDataAsync(ProfileDataRequestContext context) {
  var sub = context.Subject.GetSubjectId();
  var user = await _userManager.FindByIdAsync(sub);
  var principal = await _claimsFactory.CreateAsync(user);

  var claims = principal.Claims.ToList();

  claims.Add(new Claim(JwtClaimTypes.GivenName, user.FirstName));
  claims.Add(new Claim(JwtClaimTypes.FamilyName, user.LastName));

  // Insert a new claim, that gets ProfilePictureUrl persisted in my app user in database.
  claims.Add(new Claim(JwtClaimTypes.Picture, user.ProfilePictureUrl));

  context.IssuedClaims = claims;
}

4. Acessing profile user in ReactJS Frontend:

To get user profile data in frontend, i'm using OidcClient-js


// here i'm using oidc-client.js
// user object is loaded with user full data.
let profilePictureUrl = user.profile.picture;

showing profile picture successfully loaded from Google People API


Thanks to @DaImTo response.

1

People.get method returns a person object which contains

Your user needs to be authenticated with the profile scope.

enter image description here

Raw http request

GET https://people.googleapis.com/v1/people/me?personFields=photos HTTP/1.1

Authorization: Bearer [YOUR_ACCESS_TOKEN]
Accept: application/json

response

{
  "resourceName": "people/117200475532672775346",
  "etag": "%EgQBAzcuGgQBAgUHIgxHcHNCRHZycjVkZz0=",
  "photos": [
    {
      "metadata": {
        "primary": true,
        "source": {
          "type": "PROFILE",
          "id": "1172004755672775346"
        }
      },
      "url": "https://lh3.googleusercontent.com/a-/AOh14GhroCYJp2P9xeYeYk1npchBPK-zbtTxzNQo0WAHI20=s100"
    },
    {
      "metadata": {
        "source": {
          "type": "CONTACT",
          "id": "3faa96eb0baa4be"
        }
      },
      "url": "https://lh6.googleusercontent.com/-vuhaM1mUvwE/VFOBzFDW-TI/AAAAAAAAAAA/izR9rgfDIyoVoHd7Mq_OJmdbwjhEnfhEQCOQCEAE/s100/photo.jpg"
    }
  ]
}

Note: You can also get this information from the userinfo endpoint however Google does not guarantee that they will send the claims everytime you make the request so IMO its best to go though the people api.

Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
  • Where can I find an API KEY for `GET https://people.googleapis.com/v1/people/me?personFields=photos&key=[YOUR_API_KEY] HTTP/1.1` ? – Roomey Jul 03 '20 at 09:53
  • And are you sure that it is only way to get user's profile image? For facebook its only 1 line of code, but this looks much more complicated. – Roomey Jul 03 '20 at 10:21
  • Is it possible to get profile image without `API_KEY`and use `user_id` instead of it? – Roomey Jul 03 '20 at 10:32
  • As long as you have an access token that you can send to the request. You also need to have authenticated the user using the profile scope. This is google not facebook the user needs to authenticate your application. There is now way for you to get the Google user id anyway unless you are authenticated as the user and i dont think the user id is associated with the picture in that way. – Linda Lawton - DaImTo Jul 03 '20 at 11:03
  • @Roomey , the API KEY can be created in this link: https://console.developers.google.com/apis/credentials – gabrielbarceloscn Aug 12 '20 at 21:23