Adding the TokenController

From Solution Explorer, right-click on the /Controllers/ folder and select Add | New Item, expand the ASP.NET Core node to the left, pick the Web API Controller class, call it TokenController.cs, and click on OK to create it. Here's the full source code of the new controller class (relevant lines are highlighted):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using System.Reflection.Metadata;
using TestMakerFreeWebApp.ViewModels;
using TestMakerFreeWebApp.Data;
using Microsoft.AspNetCore.Identity;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System.Text;

namespace TestMakerFreeWebApp.Controllers
{
public class TokenController : BaseApiController
{
#region Private Members
#endregion Private Members

#region Constructor
public TokenController(
ApplicationDbContext context,
RoleManager<IdentityRole> roleManager,
UserManager<ApplicationUser> userManager,
IConfiguration configuration
)
: base(context, roleManager, userManager, configuration) {
}
#endregion

[HttpPost("Auth")]
public async Task<IActionResult>
Jwt([FromBody]TokenRequestViewModel model)
{
// return a generic HTTP Status 500 (Server Error)
// if the client payload is invalid.
if (model == null) return new StatusCodeResult(500);

switch (model.grant_type)
{
case "password":
return await GetToken(model);
default:
// not supported - return a HTTP 401 (Unauthorized)
return new UnauthorizedResult();
}
}

private async Task<IActionResult>
GetToken(TokenRequestViewModel model)
{
try
{
// check if there's an user with the given username
var user = await
UserManager.FindByNameAsync(model.username);
// fallback to support e-mail address instead of
username
if (user == null && model.username.Contains("@"))
user = await
UserManager.FindByEmailAsync(model.username);


if (user == null
|| !await UserManager.CheckPasswordAsync(user,
model.password))
{
// user does not exists or password mismatch
return new UnauthorizedResult();
}

// username & password matches: create and return the
Jwt token.

DateTime now = DateTime.UtcNow;

// add the registered claims for JWT (RFC7519).
// For more info, see
https://tools.ietf.org/html/rfc7519#section-4.1
var claims = new[] {
new Claim(JwtRegisteredClaimNames.Sub, user.Id),
new Claim(JwtRegisteredClaimNames.Jti,
Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat,
new
DateTimeOffset(now).ToUnixTimeSeconds().ToString())
// TODO: add additional claims here
};

var tokenExpirationMins =
Configuration.GetValue<int>
("Auth:Jwt:TokenExpirationInMinutes");
var issuerSigningKey = new SymmetricSecurityKey(

Encoding.UTF8.GetBytes(Configuration["Auth:Jwt:Key"]));

var token = new JwtSecurityToken(
issuer: Configuration["Auth:Jwt:Issuer"],
audience: Configuration["Auth:Jwt:Audience"],
claims: claims,
notBefore: now,
expires:
now.Add(TimeSpan.FromMinutes(tokenExpirationMins)),
signingCredentials: new SigningCredentials(
issuerSigningKey,
SecurityAlgorithms.HmacSha256)
);
var encodedToken = new
JwtSecurityTokenHandler().WriteToken(token);

// build & return the response
var response = new TokenResponseViewModel()
{
token = encodedToken,
expiration = tokenExpirationMins
};
return Json(response);
}
catch (Exception ex)
{
return new UnauthorizedResult();
}
}
}
}

Despite the remarkable amount of code, the TokenController does nothing really special. The comments already explain most of the tasks, yet it can't hurt to quickly summarize what we did:

  • The whole workflow is triggered by the Auth() action method, which will intercept a POST HTTP request containing a set of parameters depicted in the TokenRequestViewModel class, which we'll see in a short while. This is the information that we expect when a client service is asking for a JWT token; we'll talk more about it later on.
  • The Jwt method will check the GrantType parameter value; in case of the password, it will execute the GetToken internal method that will generate a new token, otherwise it will respond with a HTTP error 500 and quit.
  • The GetToken method is where most of the magic takes place; it checks the username and password that came with the POST request against our identity data and, depending on the result, either generates a token and returns it within a JSON object built upon the TokenResponseViewModel class--which we'll be seeing shortly--or quits with an HTTP error 401 Unauthorized.

As we can easily guess, our new TokenController won't compile unless we add the two ViewModel classes that we mentioned as well for this very reason; the next step will be to add them.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.218.221