简体   繁体   中英

Google Authentication get user profile picture

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?

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;

显示从 Google People API 成功加载的个人资料图片


Thanks to @DaImTo response.

People.get method returns a person object which contains

Your user needs to be authenticated with the profile scope.

在此处输入图像描述

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.

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