Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Register External Login Web API

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":"[email protected]"}

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.

like image 322
CularBytes Avatar asked May 14 '15 07:05

CularBytes


People also ask

What is external authentication in Web API?

The user agent sends its credentials to the external authentication service, and if the user agent has successfully authenticated, the external authentication service will redirect the user agent to the original web application with some form of token which the user agent will send to the web application.

How do I bypass authorization in Web API?

If you want to allow anonymous access you can use the [AllowAnonymous] attribute. This will block access to all methods when a user is not authorized, except the GetData() method which can be called anonymously.


1 Answers

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

like image 71
CularBytes Avatar answered Nov 15 '22 16:11

CularBytes