Basically i followed the Answer to this Questions: WebApi ASP.NET Identity Facebook login
I'm using the native iOS8 Facebook SDK to receive the FB Access Token, send it to api/Account/FacebookLogin and i managed to fully create a user, add the claims etc. and validate the FB Token against Graph API. But i just cant signin the user with the token produced in my FacebookLogin method ...
Here's my Code for the moment:
api/AccountController.cs
// POST api/Account/FacebookLogin
[HttpPost]
[AllowAnonymous]
[Route("FacebookLogin")]
public async Task<IHttpActionResult> FacebookLogin([FromBody] FacebookLoginModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (string.IsNullOrEmpty(model.token))
{
return BadRequest("No access token");
}
var tokenExpirationTimeSpan = TimeSpan.FromDays(14);
ApplicationUser user = null;
string username;
// Get the fb access token and make a graph call to the /me endpoint
var fbUser = await VerifyFacebookAccessToken(model.token);
if (fbUser == null)
{
return BadRequest("Invalid OAuth access token");
}
UserLoginInfo loginInfo = new UserLoginInfo("Facebook", model.userid);
user = await UserManager.FindAsync(loginInfo);
// If user not found, register him with username.
if (user == null)
{
if (String.IsNullOrEmpty(model.username))
return BadRequest("unregistered user");
user = new ApplicationUser
{
UserName = model.username,
FirstName = model.firstname,
LastName = model.lastname,
Email = model.username,
};
var result = await UserManager.CreateAsync(user);
if (result.Succeeded)
{
result = await UserManager.AddLoginAsync(user.Id, loginInfo);
username = model.username;
if (!result.Succeeded)
return BadRequest("cannot add facebook login");
}
else
{
return BadRequest("cannot create user");
}
}
else
{
// existed user.
username = user.UserName;
}
// common process: Facebook claims update, Login token generation
user = await UserManager.FindByNameAsync(username);
// Optional: make email address confirmed when user is logged in from Facebook.
user.Email = fbUser.email;
user.EmailConfirmed = true;
await UserManager.UpdateAsync(user);
// Sign-in the user using the OWIN flow
//var identity = new ClaimsIdentity(Startup.OAuthBearerOptions.AuthenticationType);
var claims = await UserManager.GetClaimsAsync(user.Id);
var newClaim = new Claim("FacebookAccessToken", model.token); // For compatibility with ASP.NET MVC AccountController
var oldClaim = claims.FirstOrDefault(c => c.Type.Equals("FacebookAccessToken"));
if (oldClaim == null)
{
var claimResult = await UserManager.AddClaimAsync(user.Id, newClaim);
if (!claimResult.Succeeded)
return BadRequest("cannot add claims");
}
else
{
await UserManager.RemoveClaimAsync(user.Id, oldClaim);
await UserManager.AddClaimAsync(user.Id, newClaim);
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager,
OAuthDefaults.AuthenticationType);
var currentUtc = new Microsoft.Owin.Infrastructure.SystemClock().UtcNow;
AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName);
properties.IssuedUtc = currentUtc;
properties.ExpiresUtc = currentUtc.Add(tokenExpirationTimeSpan);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
var accesstoken = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);
Request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accesstoken);
Authentication.SignIn(oAuthIdentity);
// Create the response building a JSON object that mimics exactly the one issued by the default /Token endpoint
JObject blob = new JObject(
new JProperty("userName", user.UserName),
new JProperty("userID", user.Id),
new JProperty("access_token", accesstoken),
new JProperty("token_type", "bearer"),
new JProperty("expires_in", tokenExpirationTimeSpan.TotalSeconds.ToString()),
new JProperty(".issued", ticket.Properties.IssuedUtc.ToString()),
new JProperty(".expires", ticket.Properties.ExpiresUtc.ToString()),
new JProperty("facebook.token", model.token)
);
// Return OK
return Ok(blob);
}
Startup.Auth.cs
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
OAuthBearerOptions.AccessTokenFormat = OAuthOptions.AccessTokenFormat;
OAuthBearerOptions.AccessTokenProvider = OAuthOptions.AccessTokenProvider;
OAuthBearerOptions.AuthenticationMode = OAuthOptions.AuthenticationMode;
OAuthBearerOptions.AuthenticationType = OAuthOptions.AuthenticationType;
OAuthBearerOptions.Description = OAuthOptions.Description;
OAuthBearerOptions.Provider = new CustomBearerAuthenticationProvider();
OAuthBearerOptions.SystemClock = OAuthOptions.SystemClock;
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
OAuthBearerAuthenticationExtensions.UseOAuthBearerAuthentication(app, OAuthBearerOptions);
app.UseFacebookAuthentication(
appId: "****",
appSecret: "****");
}
}
public class CustomBearerAuthenticationProvider : OAuthBearerAuthenticationProvider
{
// This validates the identity based on the issuer of the claim.
// The issuer is set in the API endpoint that logs the user in
public override Task ValidateIdentity(OAuthValidateIdentityContext context)
{
var claims = context.Ticket.Identity.Claims;
if( claims.Count() == 0 || !claims.Any(claim => claim.Type == "FacebookAccessToken"))
// if (!claims.Any() || claims.Any(claim => claim.Type != "FacebookAccessToken")) // modify claim name
context.Rejected();
return Task.FromResult<object>(null);
}
}