简体   繁体   中英

Instagram OAuth GetExternalLoginInfoAsync always returns null in .NET Core 2.0

I'm able to configure Instagram authentication in .NET Core 1 using this library , but I can't seem to get it working in .NET Core 2.

There don't seem to be any Instagram OAuth libraries that are compatible with .NET Core 2.0 so I'm trying to use the built-in AddOAuth method. I'm able to get everything to authenticate without a problem, but for some reason my GetExternalLoginInfoAsync method in the LinkLoginCallback always returns null. As all of that stuff is happening in AspNetCore.Authentication I'm assuming either my setup and configuration is not correct (or missing something critical), or this solution isn't going to work for some other reason.

My config in ConfigureServices looks like this:

services.AddAuthentication().AddOAuth("Instagram", "Instagram", options =>
        {
            options.ClientId = "MYID";
            options.ClientSecret = "MYSECRET";
            options.AuthorizationEndpoint = "https://api.instagram.com/oauth/authorize/";
            options.CallbackPath = "/signin-instagram";
            options.TokenEndpoint = "https://api.instagram.com/oauth/access_token";
            options.Scope.Add("basic");
            options.ClaimsIssuer = "Instagram";
            options.UserInformationEndpoint = "https://api.instagram.com/v1/users/self";
        });

Has anybody been able to successfully authenticate with Instagram in .NET Core 2.0? Any help would be greatly appreciated!

Not specifically for Instagram but I have got custom OAuth validation working for Twitch using a similar setup as above.

I was getting the same Null errors from GetExternalLoginInfoAsync and there were a number of steps I had to go through to get it working.

In the end I found the problem by looking at the source code for the GetExternalLoginInfoAsync method found here - Microsoft.AspNetCore.Identity.SignInManager.cs

  1. The first thing I found is literally on the first line of the method

     var auth = await Context.AuthenticateAsync(IdentityConstants.ExternalScheme); 

    The method is set to use always the ExternalScheme constant so if you set a custom SignInScheme in your Startup.cs it wont be used when calling this method. Leaving SignInScheme set as the default value also didn't seem to work for me.

  2. The LoginProviderKey was null.

     if (auth?.Principal == null || items==null||!items.ContainsKey(LoginProviderKey)){return null;} 
  3. And after fixing the above I found that no Claims were being set so I had to set that up manually as well. I found an example of this on another Stackoverflow question - AddOAuth linkedin dotnet core 2.0

Please see below my final code:

Startup.cs Configuring OAuth Middleware

In the Startup I set the SignInScheme to use the IdentityConstants.ExternalScheme value and added the customer Claims setup event in OnCreatingTicket.

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddOAuth("Twitch", "Twitch", o =>
              {
                  o.SignInScheme = IdentityConstants.ExternalScheme;
                  o.ClientId = "MY CLIENT ID";
                  o.ClientSecret = "MY CLIENT SECRET";
                  o.CallbackPath = "/signin-twitch";
                  o.ClaimsIssuer = "Twitch";
                  o.AuthorizationEndpoint = "https://api.twitch.tv/kraken/oauth2/authorize";
                  o.TokenEndpoint = "https://api.twitch.tv/api/oauth2/token";
                  o.UserInformationEndpoint = "https://api.twitch.tv/helix/users";
                  o.Scope.Add("openid");
                  o.Scope.Add("user:read:email");

                  o.Events = new Microsoft.AspNetCore.Authentication.OAuth.OAuthEvents
                  {
                      OnCreatingTicket = async context =>
                      {
                          var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
                          request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
                          request.Headers.Add("x-li-format", "json");

                          var response = await context.Backchannel.SendAsync(request, context.HttpContext.RequestAborted);
                          response.EnsureSuccessStatusCode();
                          var user = JObject.Parse(await response.Content.ReadAsStringAsync());

                          var data = user.SelectToken("data")[0];

                          var userId = (string)data["id"];
                          if (!string.IsNullOrEmpty(userId))
                          {
                              context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userId, ClaimValueTypes.String, context.Options.ClaimsIssuer));
                          }

                          var formattedName = (string)data["display_name"];
                          if (!string.IsNullOrEmpty(formattedName))
                          {
                              context.Identity.AddClaim(new Claim(ClaimTypes.Name, formattedName, ClaimValueTypes.String, context.Options.ClaimsIssuer));
                          }

                          var email = (string)data["email"];
                          if (!string.IsNullOrEmpty(email))
                          {
                              context.Identity.AddClaim(new Claim(ClaimTypes.Email, email, ClaimValueTypes.String,
                                  context.Options.ClaimsIssuer));
                          }
                          var pictureUrl = (string)data["profile_image_url"];
                          if (!string.IsNullOrEmpty(pictureUrl))
                          {
                              context.Identity.AddClaim(new Claim("profile-picture", pictureUrl, ClaimValueTypes.String,
                                  context.Options.ClaimsIssuer));
                          }
                      }
                  };
              });

AccountController.cs creating the Challenge

When we issue the challenge we also have to include the LoginProvider value.

public IActionResult LoginWithTwich(string returnUrl = null)
    {

        var authProperties = _signInManager.ConfigureExternalAuthenticationProperties("Twitch", returnUrl);

        return Challenge(authProperties, "Twitch");
    }

AccountController.cs Handling the Callback

Finally when we handle the callback the GetExternalLoginInfoAsync method no longer returns null.

public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null)
    {
        ExternalLoginInfo info = await _signInManager.GetExternalLoginInfoAsync();
        //to sign the user in if there's a local account associated to the login provider
        var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
        if (!result.Succeeded)
        {
            return RedirectToAction("ConfirmTwitchLogin", new { ReturnUrl = returnUrl });
        }
        if (string.IsNullOrEmpty(returnUrl))
        {
            return Redirect("~/");
        }
        else
        {
            return RedirectToLocal(returnUrl);
        }
    }

This work for me (version net core 2.2)

  1. Install the next package:

Install-Package AspNet.Security.OAuth.Instagram -Version 2.0.1

  1. After register the client keys like user-secrets

dotnet user-secrets set Authentication:Instagram:ClientId YourClientId

dotnet user-secrets set Authentication:Instagram:ClientSecret YourClientSecret

Finally in startup class:

services.AddAuthentication()
    .AddInstagram(instagramOptions =>
                {
                    instagramOptions.ClientId = Configuration["Authentication:Instagram:ClientId"];
                    instagramOptions.ClientSecret = Configuration["Authentication:Instagram:ClientSecret"];
                }
    );

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