Hi friends. Nowadays, mobile applications are rarely standalone. They most often require a back-end with which they communicate. This back-end provides a means of storing, retrieving and processing data from any device which the app runs on. Taking this into consideration, it is obvious you will want to secure this data. This can be done by restricting access to the Back-end’s resources to trusted and identified users of your software. This is a common use case, and I think there is not enough documentation about implementing this with Asp.net core and Xamarin forms. So, I decided to make this blog post. Let’s talk about implementing social auth With Xamarin Forms and Asp.net core.
A fast and secure way to authenticate users of your system is to use social authentication providers like Google, Facebook… You might also want to implement email and password authentication alongside these, So don’t worry you are at the right place we will implement all 3.
Here is the source code for this tutorial.
If you like this post, don’t hesitate to follow me on Twitter or Github and subscribe to this page’s notifications to stay updated with new articles or like my page on Facebook.
How Social Authentication with a Back-end is Done
Basically, the Xamarin Forms mobile application will launch social authentication. This will cause the mobile web browser to be called, redirect to the social authentication provider. After the user enters his valid credentials, we redirect back to our app, passing the access token. This token can be used to access user information from the auth provider. We pass the token to the asp.net core API. The REST API will query the social auth provider’s server to validate that the token actually comes from them. This helps protect our software from attackers. When The back-end has the token validated by social authentication providers (Facebook, Google…). We then create a new JWT and send this to the user. It is with this token that all restricted API calls should be made.
This diagram illustrates clearly what we are trying to describe here:
Implementing Social Auth With Xamarin Forms and Asp.net core
To implement Social Auth With Xamarin Forms and Asp.net core, we have to first allow the client to authenticate via the social media and get an access token.
Step 1: Get the access token via the mobile app
Using Xamarin Forms, this is easy. You need to add Xamarin.Auth to all your projects (.Net standard and platform projects). Since there are already well documented examples for these, I will redirect you to the right resources and we will go straight to the point.
- For Google authentication, follow carefully this guide. In case you have any issue, I added the troubleshooting guide at the end of the documentation.
- For Facebook authentication, Create an app at “developers.facebook.com”. Then in the products tab (in the dashboard), select “Facebook Login” and add a redirect url this could be ” https://www.facebook.com/connect/login_success.html”. Then you can initiate facebook authentication as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | async Task FacebookAuth() { var facebookAppId = ""; _authenticator = new OAuth2Authenticator( clientId: facebookAppId, scope: "email", authorizeUrl: new Uri("https://www.facebook.com/dialog/oauth/"), redirectUrl: new Uri("https://www.facebook.com/connect/login_success.html") ); _authenticator.Completed += Authenticator_Facebook_Completed; _authenticator.Error += _authenticator_Error; AuthenticationState.Authenticator = _authenticator; var presenter = new Xamarin.Auth.Presenters.OAuthLoginPresenter(); presenter.Login(_authenticator); } |
Using Xamarin.Auth, we created an object of type “OAuth2Authenticator”. This object has an event called ” Completed” which is called when authentication is done. We subscribe to this event. This event passes us an Access Token generated by either Facebook or Google. We will use this to signin to the API.
Step 2: Add JWT and Social Authentication to our Asp.Net core REST API
NB: In the source code of the asp.net core project, I use MongoDB as Identity provider. But you can easily modify that and change it to SQL server. In the back-end, we add JWT authentication normally.
- In the Configure Services method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(cfg => { cfg.RequireHttpsMetadata = false; cfg.SaveToken = true; cfg.TokenValidationParameters = new TokenValidationParameters { ValidIssuer = Configuration["JwtIssuer"], ValidAudience = Configuration["JwtIssuer"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtKey"])), ClockSkew = TimeSpan.Zero // remove delay of token when expire }; }); |
In the Configure Method
1 2 | app.UseAuthentication(); app.UseAuthorization(); |
- We create a controller called AccountController. Which will contain the methods to signin via Google, Facebook and email/password.
- Both Google and Facebook signin actions are similar.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | [HttpPost] public async Task<IActionResult> Google([FromBody] UserTokenIdModel token) { try { var user = await _googleAuthService.Authenticate(token); await _signInManager.SignInAsync(user, true); var jwtToken = GenerateJwtToken(user.Email, user); return Ok(jwtToken); } catch (Exception e) { Debug.WriteLine(e.Message); return BadRequest(e.Message); } } |
In other to validate the access token passed to the API, we create a FacebookAuthService and a GoogleAuthService. Both of them will be in charge of validating the access token they receive and creating the user inside the API.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | //Authenticating with FacebookAuthService public async Task<AppUser> Authenticate(UserTokenIdModel token) { //NB: These config files are loaded from the user secrets. var appToken = await GenerateAppAccessToken(_configuration["Facebook:AppId"], _configuration["Facebook:AppSecret"]); var isValid = await DebugUserAccessToken(appToken, token.TokenId); if (isValid) { var user = await CreateOrGetUser(token); return user; } throw new Exception("Invalid Token"); } private async Task<string> GenerateAppAccessToken(string appId, string appSecret) { using var httpClient = new HttpClient(); var json = await httpClient.GetStringAsync($@"https://graph.facebook.com/oauth/access_token?client_id={appId}&client_secret={appSecret}&grant_type=client_credentials"); var obj = JsonConvert.DeserializeAnonymousType(json, new { access_token = "", token_type = "" }); return obj.access_token; } private async Task<bool> DebugUserAccessToken(string appAccessToken, string userAccessToken) { using var httpClient = new HttpClient(); var json = await httpClient.GetStringAsync($@"https://graph.facebook.com/debug_token?input_token={userAccessToken}&access_token={appAccessToken}"); var obj = JsonConvert.DeserializeAnonymousType(json, new { data = new { app_id = "", is_valid = false } }); return obj.data.is_valid; } |
1 2 3 4 5 6 7 | //Authenticating with GoogleAuthService public async Task<AppUser> Authenticate(UserTokenIdModel userTokenModel) { var payload = await GoogleJsonWebSignature.ValidateAsync(userTokenModel.TokenId, new GoogleJsonWebSignature.ValidationSettings()); return await CreateOrGetUser(payload, userTokenModel); } |
This is shorter with Google thanks to the package ” Google.Apis.Auth” which saves us a lot of time.
After getting user information, and validating the access token, you need to create a new JWT token which will be sent back to the Client calling the API. We do so as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | private object GenerateJwtToken(string email, IdentityUser user) { var claims = new List<Claim> { new Claim(JwtRegisteredClaimNames.Sub, email), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(ClaimTypes.NameIdentifier, user.Id), }; var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtKey"])); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var expires = DateTime.Now.AddDays(Convert.ToDouble(_configuration["JwtExpireDays"])); var token = new JwtSecurityToken( _configuration["JwtIssuer"], _configuration["JwtIssuer"], claims, expires: expires, signingCredentials: creds ); return new JwtSecurityTokenHandler().WriteToken(token); } |
Step 3: Signin in to the API via the mobile app
To signin, you just call the “SignInToAPI” method after Xamarin.Auth fires the event for completed authentication, do this only if authentication is successfull.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | private async Task SignInToAPI(APIUser user, AuthProvider authProvider) { //Used Ngrok to tunnel My endpoint. var endPoint = "https://5d13182b.ngrok.io"; if (authProvider == AuthProvider.Google) endPoint = $"{endPoint}/Account/Google"; else endPoint = $"{endPoint}/Account/Facebook"; using var httpClient = new HttpClient(); var json = JsonConvert.SerializeObject(user); var content = new StringContent(json, Encoding.UTF8, "application/json"); var res = await httpClient.PostAsync(endPoint, content); //We get the JWT Token which we will be used to make authorized request to the API. var jwt = await res.Content.ReadAsStringAsync(); } |
Step 4: Email and Password authentication
This part has nothing to do with social authentication. It is a bonus part which tells you how to implement email login and registration from the backend. To do so, you just need to leverage the SigninManager and the UserManager which you inject in the constructor. These will be in charge of managing user tasks related to authentication.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | [HttpPost] public async Task<IActionResult> Google([FromBody] UserTokenIdModel token) { try { var user = await _googleAuthService.Authenticate(token); await _signInManager.SignInAsync(user, true); var jwtToken = GenerateJwtToken(user.Email, user); return Ok(jwtToken); } catch (Exception e) { Debug.WriteLine(e.Message); return BadRequest(e.Message); } } [HttpPost] public async Task<IActionResult> Facebook([FromBody] UserTokenIdModel token) { try { var user = await _facebookAuthService.Authenticate(token); await _signInManager.SignInAsync(user, true); var jwtToken = GenerateJwtToken(user.Email, user); return Ok(jwtToken); } catch (Exception e) { Debug.WriteLine(e.Message); return BadRequest(e.Message); } } |
Conclusion
You should have implemented social authentication with the help of this tutorial and the source code on Github. To protect your API, add the [Authorize] attribute to the controllers and HTTP actions with restricted access. You will now need to pass a “bearer” token every time you make a web request to the controllers or methods of your API with the Authorize attribute.
You might be interested by this blog post too: Implementing Infinitescroll with the Xamarin Forms Collection View.
References
Image credit: https://medium.com/@blacksonic86/angular-2-authentication-revisited-611bf7373bf9
https://medium.com/mickeysden/react-and-google-oauth-with-net-core-backend-4faaba25ead0
https://docs.microsoft.com/en-us/xamarin/xamarin-forms/data-cloud/authentication/oauth
Follow me on social media and stay updated
Paul Pfeiffer
Damien Doumer
Paul Pfeiffer
Damien Doumer
Damien Doumer