skip to Main Content

I’m implementing Facebook logins on an iOS app with a .net core web api backend.

  1. I created the app in Facebook with a client id and secret.
  2. I added iOS and web pages to my app
  3. The iOS app successfully connects and aquires a token
  4. I added the app.UseFacebook code to my startup.cs and configured it with the app id etc.
  5. I added the authorize attribute to an action I’m restricting access to
  6. I call this web api action from iOS with an https get, and add an http header Authorization Bearer (token I acquired from Facebook)

The get returns status code 401 as if my token was invalid. I’m wondering if iOS tokens can be used with a web page app as well? I have those 2 configured for my Facebook app.

2

Answers


  1. Chosen as BEST ANSWER

    I'm not sure if this was the right way to do it, but my implementation is working.

    The workflow is,

    • iOS app gets a facebook access token using the facebook sdk
    • iOS app creates a user in the .NET back-end using the facebook access token
    • .NET backend verifies the facebook access token is valid and downloads user data
    • .NET backend creates a jwt bearer token and returns it to the iOS app
    • iOS app calls .NET backend with the jwt bearer token in the Authorization http header

    I check the Facebook access token is valid by calling https://graph.facebook.com -- refer to: Task VerifyAccessToken(string email, string accessToken)

    AccountController.cs

    [AllowAnonymous, HttpPost("[action]")]
    public async Task<ActionResult> FacebookAuth([FromBody] ExternalLoginModel model)
    {
        try
        {
            await _interactor.VerifyAccessToken(model.Email, model.Token);
    
            var result = await _interactor.SignInWithFacebook(model.Email);
    
            return Ok(result);               
        }
        catch (ValidationException ex)
        {
            return BadRequest(ex.Message.ErrorMessage(Strings.ValidationException));
        }
    }
    
    [AllowAnonymous, HttpPost("[action]")]
    public async Task<ActionResult> CreateAccountWithFacebook(AccountModel account, string token)
    {
      try
      {   
         await _interactor.VerifyAccessToken(account.Email, token);
    
         if (ModelState.IsValid)
         {               
             var result = await _interactor.CreateFacebookLogin(account);
    
             return Ok(result);
         }
    
         return BadRequest(ModelState);
      }
      catch (ValidationException ex)
      {
         return BadRequest(ex.Message.ErrorMessage(Strings.ValidationException));
      }
    }
    

    call facebook graph service to verify the access token is valid

    public async Task<FacebookMeResponse> VerifyAccessToken(string email, string accessToken)
    {
        if (string.IsNullOrEmpty(accessToken))
        {
            throw new ValidationException("Invalid Facebook token");
        }
    
        string facebookGraphUrl = "https://graph.facebook.com/me?fields=cover,age_range,first_name,location,last_name,hometown,gender,birthday,email&access_token=" + accessToken;
        WebRequest request = WebRequest.Create(facebookGraphUrl);
        request.Credentials = CredentialCache.DefaultCredentials;
    
        using (WebResponse response = await request.GetResponseAsync())
        {
            var status = ((HttpWebResponse)response).StatusCode;
    
            Stream dataStream = response.GetResponseStream();
    
            StreamReader reader = new StreamReader(dataStream);
            string responseFromServer = reader.ReadToEnd();
            var facebookUser = JsonConvert.DeserializeObject<FacebookMeResponse>(responseFromServer);
    
            bool valid = facebookUser != null && !string.IsNullOrWhiteSpace(facebookUser.Email) && facebookUser.Email.ToLower() == email.ToLower();
            facebookUser.PublicProfilePhotoUrl = "http://graph.facebook.com/" + facebookUser.Id + "/picture";
    
            if (!valid)
            {
                throw new ValidationException("Invalid Facebook token");
            }
    
            return facebookUser;
        }
    }
    

    Create a jwt bearer token for your middleware, the iOS app will use the jwt bearer token for calling your .NET apis (it won't use the facebook access_token)

    public async Task<FacebookResponse> SignInWithFacebook(string email)
    {
        var claims = new List<Claim>();
    
        var user = await _userManager.FindByEmailAsync(email);
    
        var identity = new ClaimsIdentity(claims, "oidc");
    
        var jwtBearerToken= Guid.NewGuid().ToString();
        var properties = new AuthenticationProperties();
        properties.Items.Add(".Token.access_token", jwtBearerToken);
    
        await _signInManager.SignInAsync(user, properties, "oidc");
    
        var principal = await _signInManager.CreateUserPrincipalAsync(user);
    
        var token = new Token();
        token.Key = jwtBearerToken;
        token.Expiry = DateTime.UtcNow.AddMinutes(30);
        token.UserId = user.Id;
        token.TokenType = "FacebookLogin";
    
        await _tokensRepository.Save(token);
        var result = _signInManager.IsSignedIn(principal);
    
        return new FacebookResponse("success", result, jwtBearerToken);
    }
    

    Create a user if it doesnt exist

    public async Task<FacebookResponse> CreateFacebookLogin(AccountModel model)
    {   
        User user = await _userManager.FindByEmailAsync(model.Email);
    
        if (user == null)
        {
            var createResult = await _userManager.CreateAsync(_mapper.Map<AccountModel, User>(model));
            if (!createResult.Succeeded)
            {
               // handle failure..
            }                
        }
    
        return await SignInWithFacebook(model.Email);   
    }
    

    Classes for de-serialising the response from the facebook graph REST service

    public class FacebookAgeRange
    {
        public int Min { get; set; }
    }
    
    public class FacebookMeResponse
    {
        public string Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public string Gender { get; set; }
        public FacebookAgeRange AgeRange { get; set; }
        public string PublicProfilePhotoUrl { get; set; }
    }
    
    public class FacebookResponse : IResponse
    {
        public bool Ok { get; set; }
        public string Message { get; set; }
        public string JwtToken { get; set; }
    
        public FacebookResponse(string message, bool ok = true, string jwtToken = "")
        {
            this.Message = message;
            this.Ok = ok;
            this.JwtToken = jwtToken;
        }
    }
    

  2. ASP.NET Security repo contains different auth middlewares, where you can find how to check and verify jwt tokens and create identity with claims. Look into FacebookHandler.cs if you need Facebook JWT toen validation

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search