1

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);
    }
}
helgetan
  • 1,367
  • 11
  • 16

1 Answers1

0

Use CreateIdentityAsync method from UserManager class instead of creating ClaimsIdentity by yourself.

For instance, in ApplicationOAuthProvider generated class in method GrantResourceOwnerCredentials following code:

ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
                   OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
CookieAuthenticationDefaults.AuthenticationType);

AuthenticationProperties properties = await CreateProperties(user);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
askito
  • 36
  • 3