What Will I Learn?
- How to use Facebook API
- How to create facebook login to your application
- How to use JWT tokens
Requirements
- C#
- ASP.NET Core
- Visual Studio 2015+/ Visual Studio Code
Difficulty
- Intermediate
Tutorial Contents
In this tutorial I will present how to create logic for Facebook login, using ASP.NET Core Web API. I will show you how to make full service and REST communication with another part application witch can be mobile application or web application.
At the beginning we have to create new project. I will create new ASP.NET Core 2.0 Web API project. I will show you how do it using Visual Studio 2017.
How will it work?
When user click in the mobile application that he want to login by facebook, mobile application will send login request to facebook API, when credentials are correctly he will receive accessToken from facebook, this token is sending to our API where our API will send to facebook API request for identify user by accessToken. If everything will be ok facebook will return all information about particular user. Our service will create ours tokens and return this to mobile application. Since then mobile appplication will be using our access token for all request.
In the new project I will create new folder with 4 classes. It will be Resource classes, in this classes we will define the models, witch we will be receiving or sending by REST.
I created 4 classes:
- FacebookLoginresource
- FacebookUserResource
- TokenResource
- AuthorizationTokenResource
First and second class will be used for facebook login, next 2 classes will be used for authorization particular user in our application using JWT Tokens.
FacebookLoginResource:
public class FacebookLoginResource
{
[Required]
[StringLength(255)]
public string facebookToken { get; set; }
}
FacebookUserResource
public class FacebookUserResource
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Picture { get; set; }
}
TokenResource
public class TokenResource
{
public string Token { get; set; }
public long Expiry { get; set; }
}
AuthorizationTokenResource
public class AuthorizationTokensResource
{
public TokenResource AccessToken { get; set; }
public TokenResource RefreshToken { get; set; }
}
Next step is creating a couple of services responsible for logic. I will create 3 service, one for communication with our mobile application, second for communication with facebook API, and third for creating JWT tokens.
AccountService
public class AccountService
{
private readonly FacebookService _facebookService;
private readonly JwtHandler _jwtHandler;
public AccountService(FacebookService facebookService, JwtHandler jwtHandler)
{
_facebookService = facebookService;
_jwtHandler = jwtHandler;
}
public async Task<AuthorizationTokensResource> FacebookLoginAsync(FacebookLoginResource facebookLoginResource)
{
if (string.IsNullOrEmpty(facebookLoginResource.facebookToken))
{
throw new Exception("Token is null or empty");
}
var facebookUser = await _facebookService.GetUserFromFacebookAsync(facebookLoginResource.facebookToken);
var domainUser = await unitOfWork.Users.GetAsync(facebookUser.Email);
return await CreateAccessTokens(domainUser, facebookLoginResource.DeviceId,
facebookLoginResource.DeviceName);
}
private async Task<AuthorizationTokensResource> CreateAccessTokens(User user, string deviceId,
string deviceName)
{
var accessToken = _jwtHandler.CreateAccessToken(user.Id, user.Email, user.Role);
var refreshToken = _jwtHandler.CreateRefreshToken(user.Id);
return new AuthorizationTokensResource { AccessToken = accessToken, RefreshToken = refreshToken };
}
}
FacebookService
public class FacebookService
{
private readonly HttpClient _httpClient;
public FacebookService()
{
_httpClient = new HttpClient
{
BaseAddress = new Uri("https://graph.facebook.com/v2.8/")
};
_httpClient.DefaultRequestHeaders
.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public async Task<FacebookUserResource> GetUserFromFacebookAsync(string facebookToken)
{
var result = await GetAsync<dynamic>(facebookToken, "me", "fields=first_name,last_name,email,picture.width(100).height(100)");
if (result == null)
{
throw new Exception("User from this token not exist");
}
var account = new FacebookUserResource()
{
Email = result.email,
FirstName = result.first_name,
LastName = result.last_name,
Picture = result.picture.data.url
};
return account;
}
private async Task<T> GetAsync<T>(string accessToken, string endpoint, string args = null)
{
var response = await _httpClient.GetAsync($"{endpoint}?access_token={accessToken}&{args}");
if (!response.IsSuccessStatusCode)
return default(T);
var result = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(result);
}
}
JwtHandler
public class JwtHandler
{
private readonly IConfiguration _configuration;
public JwtHandler(IConfiguration configuration)
{
_configuration = configuration;
}
public TokenResource CreateAccessToken(Guid userId, string email, Role role)
{
var now = DateTime.UtcNow;
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()),
new Claim(JwtRegisteredClaimNames.UniqueName, userId.ToString()),
new Claim(ClaimTypes.Email, email),
new Claim(ClaimTypes.Role, role.ToString()),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, now.ToTimeStamp().ToString(), ClaimValueTypes.Integer64),
};
var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Tokens:Key"])),
SecurityAlgorithms.HmacSha256);
var expiry = now.AddMinutes(double.Parse(_configuration["Tokens:AccessExpireMinutes"]));
var jwt = CreateSecurityToken(claims, now, expiry, signingCredentials);
var token = new JwtSecurityTokenHandler().WriteToken(jwt);
return CreateTokenResource(token, expiry.ToTimeStamp());
}
public TokenResource CreateRefreshToken(Guid userId)
{
var now = DateTime.UtcNow;
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()),
new Claim(JwtRegisteredClaimNames.UniqueName, userId.ToString()),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, now.ToTimeStamp().ToString(), ClaimValueTypes.Integer64),
};
var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Tokens:Key"])),
SecurityAlgorithms.HmacSha256);
var expiry = now.AddMinutes(double.Parse(_configuration["Tokens:RefreshExpireMinutes"]));
var jwt = CreateSecurityToken(claims, now, expiry, signingCredentials);
var token = new JwtSecurityTokenHandler().WriteToken(jwt);
return CreateTokenResource(token, expiry.ToTimeStamp());
}
private JwtSecurityToken CreateSecurityToken(IEnumerable<Claim> claims, DateTime now, DateTime expiry, SigningCredentials credentials)
=> new JwtSecurityToken(claims: claims, notBefore: now, expires: expiry, signingCredentials: credentials);
private static TokenResource CreateTokenResource(string token, long expiry)
=> new TokenResource { Token = token, Expiry = expiry };
}
AccountService has 2 methods:
- FacebookLogin
- CreateAccessTokens
Facebook Login method is taking facebookLoginresource as parameter, then check is facebokToken is null, next step is checking if user with this accesstoken exist in facebook. If everything is ok createaccess token method is calling.
FacebookService has also 2 methods:
- GetUserFromFacebook
- GetAsync
first method is taking accessToken as parameter and is using this token to check if is in facebook exist user connected with this token. If facebook return basic information about this user is creating a facebookUser resource witch is returning to access token.
JWTService has method responsible for creating JwtTokens, more information about it you can find in my other tutorial:
JwtTokens
Last part is creating a new Controller
private readonly AccountService _accountService;
public AccountController(AccountService accountService)
{
_accountService = accountService;
}
[HttpPost]
[Route("account/login/facebook")]
public async Task<IActionResult> FacebookLoginAsync([FromBody] FacebookLoginResource resource)
{
var authorizationTokens = await _accountService.FacebookLoginAsync(resource);
return Ok(authorizationTokens);
}
This controller has only one method for taking facebookLoginResource and returning a authorization tokens.
Curriculum
- Saving image in database
- Asp.Net Core with MSSql
- Email sending
- Manage Refresh token
- Custom error handling
Posted on Utopian.io - Rewarding Open Source Contributors
Hey @babelek I am @utopian-io. I have just upvoted you!
Achievements
Community-Driven Witness!
I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!
Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x
Thank you for the contribution. It has been approved.
You can contact us on Discord.
[utopian-moderator]
Congratulations @babelek! You have received a personal award!
1 Year on Steemit
Click on the badge to view your Board of Honor.
Congratulations @babelek! You received a personal award!
You can view your badges on your Steem Board and compare to others on the Steem Ranking
Vote for @Steemitboard as a witness to get one more award and increased upvotes!