Question

I'm developing a C# ASP.NET MVC 5 application that uses Google sign in as a default provider. The login functionality works ok and I can get the e-mail and name of the user. One thing that I need is to get the profile picture of the user.

How can I achieve that?

So far I using the default MVC auth "UseGoogleAuthentication".

Microsoft.Owin.Security.Google.GoogleAuthenticationOptions a = new Microsoft.Owin.Security.Google.GoogleAuthenticationOptions();

var googleOption = new GoogleAuthenticationOptions()
{
    Provider = new GoogleAuthenticationProvider()
    {
         OnAuthenticated = (context) =>
         {
              var rawUserObjectFromFacebookAsJson = context.Identity;
              context.Identity.AddClaim(new Claim("urn:google:name", context.Identity.FindFirstValue(ClaimTypes.Name)));
              context.Identity.AddClaim(new Claim("urn:google:email", context.Identity.FindFirstValue(ClaimTypes.Email)));
              return Task.FromResult(0);
         }
    }
};

app.UseGoogleAuthentication(googleOption);

This is how I can get the email address. But what about the profile picture?

Do I need to use another form of authentication?

Was it helpful?

Solution

I know this is a late answer, but found your question while working on the same problem. Here is my solution.

Instead of using GoogleAuthenticationOptions I used GoogleOAuth2AuthenticationOptions which means you'll need to set up a project at https://console.developers.google.com/project first to get a ClientId and ClientSecret.

  1. At that link (https://console.developers.google.com/project), create a project and then select it.

  2. Then on the left side menu, click on "APIs & auth".

  3. Under "APIs", ensure you have "Google+ API" set to "On".

  4. Then click on "Credentials" (in the left side menu).

  5. Then click on the button "Create new Client ID". Follow the instructions and you will then be provided with a ClientId and ClientSecret, take note of both.

Now you have those, the GoogleOAuth2AuthenticationOptions code looks like this:

var googleOptions = new GoogleOAuth2AuthenticationOptions()
{
    ClientId = [INSERT CLIENT ID HERE],
    ClientSecret = [INSERT CLIENT SECRET HERE],
    Provider = new GoogleOAuth2AuthenticationProvider()
    {
        OnAuthenticated = (context) =>
        {
            context.Identity.AddClaim(new Claim("urn:google:name", context.Identity.FindFirstValue(ClaimTypes.Name)));
            context.Identity.AddClaim(new Claim("urn:google:email", context.Identity.FindFirstValue(ClaimTypes.Email)));
            //This following line is need to retrieve the profile image
            context.Identity.AddClaim(new System.Security.Claims.Claim("urn:google:accesstoken", context.AccessToken, ClaimValueTypes.String, "Google"));

            return Task.FromResult(0);
        }
    }
};

app.UseGoogleAuthentication(googleOptions);

Notice that this also adds the access token as a claim so we can use it to retrieve the profile image. The next bit may vary depending on how you have your project set up, but for me, it was in the AccountController.

In my ExternalLoginCallback method I check for which login provider is being used and handle the data for Google login. In this section I retrieve the profile image url and store it in a variable with the following code:

//get access token to use in profile image request
var accessToken = loginInfo.ExternalIdentity.Claims.Where(c => c.Type.Equals("urn:google:accesstoken")).Select(c => c.Value).FirstOrDefault();
Uri apiRequestUri = new Uri("https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + accessToken);
//request profile image
using (var webClient = new System.Net.WebClient())
{
    var json = webClient.DownloadString(apiRequestUri);
    dynamic result = JsonConvert.DeserializeObject(json);
    userPicture = result.picture;
}

This uses the access token to request the user info from google. It then retrieves the image url from the json data return. You can then save the url to the database in the most appropriate way for your project.

Hope that helps someone.

OTHER TIPS

This might be done easier. Not sure if it was always like this, but here's my code (ASP.NET 5):

private static Task OnGoogleAuthenticated(GoogleAuthenticatedContext context)
{
    var identity = ((ClaimsIdentity)context.Principal.Identity);
    var pictureUrl = context.User["image"].Value<string>("url");
    // Pass the picture url as a claim to be used later in the application
    identity.AddClaim(new Claim("pictureUrl", pictureUrl));
    return Task.FromResult(0);
}

In the ExternalLoginCallback, the pictureUrl can then be retrieved directly using:

var info = await signInManager.GetExternalLoginInfoAsync();
...
var picture = info.ExternalPrincipal.FindFirstValue("pictureUrl");

The "image" is no longer valid, nor "url". Right now you just need to get

context.User["picture"].ToString()

Tested on .NET Core 2.2 with IdentityServer4. Maybe it's related to the latest shutdown of the Google+ API.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top