15

I don't understand why their isn't a clear tutorial or guideline on this, so I hope my question can be answered here.

So, trying to register users from facebook or google, via the Web Api.

The problem is, at the RegisterExternal method, on this line:

var info = await Authentication.GetExternalLoginInfoAsync();

It returns null, and thus returning a BadRequest()

What I got so far:

In Startup.Auth.cs I've hadded the id's and the secrets, note that I have also tried using Microsoft.Owin.Security.Facebook

var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions
            {
                AppId = "103596246642104",
                AppSecret = "1c9c8f696e47bbc661702821c5a8ae75",
                Provider = new FacebookAuthenticationProvider()
                {
                    OnAuthenticated = (context) =>
                    {
                        context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, ClaimValueTypes.String, "Facebook"));

                        return Task.FromResult(0);
                    }
                },
            };
            facebookOptions.Scope.Add("email");
            app.UseFacebookAuthentication(facebookOptions);



            app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
            {
            ClientId = "328779658984-t9d67rh2nr681bahfusan0m5vuqeck13.apps.googleusercontent.com",
            ClientSecret = "ZYcNHxBqH56Y0J2-tYowp9q0",
            CallbackPath = new PathString("/api/Account/ManageInfo")
        });

facebookOptions source: this post

That extra facebookOptions did not solve the problem.

I am able to retrieve an access_token from both Google and Facebook. I'm also able to Authenticate with this access_token to api/Account/UserInfo

GET http://localhost:4856/api/Account/UserInfo
in the header:
Authorization: Bearer R9BTVhI0...

Which returns: {"Email":"firstname lastname","HasRegistered":false,"LoginProvider":"Facebook"}

One issue I notice their, is that it returns my name as Email, not the actual Email adress.

Now I want to register the external login with a new user for my database, which I make a POST call like this:

POST http://localhost:4856/api/Account/RegisterExternal
[header]
authorization: bearer 6xcJoutY...
Content-Type: application/json
[body]
{"Email":"...@hotmail.com"}

source: this post

Now this returns a BadRequest on this code snippit, inside RegisterExternal():

    public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
    {
if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
            //AuthenticationManger?
            var info = await Authentication.GetExternalLoginInfoAsync();
            if (info == null)
            {
                return InternalServerError();
            }

In debugging, the ExternalLoginConfirmationViewModel does contain my email adress.

What am I doing wrong? Do I have to add something to the Startup.cs? Is there something more I have to do in the Startup.Auth.cs? Am I incorrectly calling RegisterExternal? In MVC it goes so smooth, why not in the Web API?

Aso looked at this answer from this question, But I didn't understand how to implement this.

Community
  • 1
  • 1
CularBytes
  • 9,924
  • 8
  • 76
  • 101
  • Was this ever resolved? I am having the same issue. – Greg Gum Sep 13 '15 at 23:33
  • 2
    Eventually I did the authentication with facebook on the App side, like it should be when developing apps. Then using the external access token to register my users at my API. Following this tutorial: http://bitoftech.net/2014/08/11/asp-net-web-api-2-external-logins-social-logins-facebook-google-angularjs-app/ If you have th external access token, you can use that to get the user info using the facebook graph api. Here is an answer for this in android: http://stackoverflow.com/questions/31314124/get-email-and-name-facebook-sdk-v4-4-0-swift – CularBytes Sep 14 '15 at 12:25
  • Thanks very much, very helpful. – Greg Gum Sep 14 '15 at 13:42
  • No prob, and sorry, just figured I send a link to IOS example..real android example: http://stackoverflow.com/questions/29295987/android-facebook-4-0-sdk-how-to-get-email-date-of-birth-and-gender-of-user – CularBytes Oct 19 '15 at 21:34
  • Since then, I found a really good sample app and article on how to use External Logins and use them in web api http://www.asp.net/web-api/overview/security/individual-accounts-in-web-api – Greg Gum Oct 20 '15 at 00:55
  • The client secret is called that for a reason. Never publish it on the web like you did in your post, @RageCompex. – gosr Jun 12 '16 at 17:25
  • @eightx2 Thanks for that but I already deleted the test apps in both google and facebook that were using this secret before posting this. – CularBytes Jun 13 '16 at 09:29
  • Did you get a proper solution for your question ? – Bimal Das Jul 03 '16 at 11:36
  • 1
    @BimalDas yes, see the other comments and answer below. – CularBytes Jul 04 '16 at 13:07

1 Answers1

7

This method is not really practical, since you are developing an API, that will most likely be used for apps, you best way is to handle the login with facebook by the API consumer, and let them send you an facebook auth token.

Basically I was trying to do this:

  1. Create external login link for facebook.
  2. Send user to that link that will bring them to facebook login page.
  3. After login facebook will redirect to api.
  4. User would be registered, but how does the app/website that is consuming the API know?

What you want to do is this:

  1. API consumer creates their own method to login with facebook (for apps via SDK's)
  2. API consumer will send an facebook token to the API to register/login.
  3. API will check token with facebook graph endpoint.
  4. When succeeded, API will return an bearer token for the API to make further authenticated requests.

So for you as an API developer, you would verify the token like so:

var verifyTokenEndPoint = string.Format("https://graph.facebook.com/debug_token?input_token={0}&access_token={1}", accessToken, appToken);

And then get the userId

var client = new HttpClient();
var uri = new Uri(verifyTokenEndPoint);
var response = await client.GetAsync(uri);

if (response.IsSuccessStatusCode)
{
    var content = await response.Content.ReadAsStringAsync();

    dynamic jObj = (JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(content);

    string user_id = jObj["data"]["user_id"];
    string app_id = jObj["data"]["app_id"];
}

Eventually you would create or find a user like so:

IdentityUser user = await _userManager.FindAsync(new UserLoginInfo(provider, verifiedAccessToken.user_id));

And then it's all up to you how to create an bearer token, if you follow the tutorial listed below, you could have this:

var tokenExpiration = TimeSpan.FromMinutes(30);

ClaimsIdentity identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);

identity.AddClaim(new Claim(ClaimTypes.Name, userName));
identity.AddClaim(new Claim("role", "user"));

var props = new AuthenticationProperties()
{
    IssuedUtc = DateTime.UtcNow,
    ExpiresUtc = DateTime.UtcNow.Add(tokenExpiration),
};

var ticket = new AuthenticationTicket(identity, props);

var accessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);

Source, with full tutorial here

I've also got the email via the SDK and send that along with the POST request, since I managed both the API and the consumer. Warning though: A facebook user might not want to give you an e-mail address.

Get e-mail after facebook login on Android and IOS

Community
  • 1
  • 1
CularBytes
  • 9,924
  • 8
  • 76
  • 101