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.
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.
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.
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:
What you want to do is this:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With