Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Core Website to WebApi authentication using JWT token

I am developing an ASP.NET Core 2.2 Website where users need login and then use it.

The AccountController in my Website calls another ASP.NET Core WebApi (with [AllowAnonymous] attribute) to get the JWT token from username and password.

All controllers except for AccountController within the Website will have [Authorize("Bearer")] attribute to check if the user has been authorized.

My WebApi will have other controllers too which will require [Authorize("Bearer")], so the JWT token will be passed from the Website when making http requests. See below configured Startup.cs > ConfigureServices() method file in WebApi project:

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    options.SaveToken = true;
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        ValidIssuer = "ZZZZ",
        ValidAudience = "ZZZZ",
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey))
    };
});
services.AddAuthorization(auth =>
{
    auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
        .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
        .RequireAuthenticatedUser().Build());
});

And the Configure() method:

app.UseAuthentication();

ASP.NET Core WebApi - generate JWT token:

JWTToken jwt = new JWTToken();
jwt.Token = "";
jwt.Expires = DateTime.UtcNow.AddMinutes(90);

var claims = new[]
{
    new Claim(ClaimTypes.UserData, UserId)
};

var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(privateSecretKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

var token = new JwtSecurityToken(
    issuer: "ZZZ",
    audience: "ZZZ",
    claims: claims,
    expires: jwt.Expires,
    signingCredentials: creds);

var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);

jwt.Token = tokenStr;
return jwt;

I have completed the WebApi method to generate a token and return back a JWT token. But what do I do with that token so that the Authentication/Authorization works in my ASP.NET Core Website.

[HttpPost]
public async Task<IActionResult> Login(LoginModel model)
{
    var httpClient = _httpClientFactory.CreateClient(ConstantNames.WebApi);
    var response = await httpClient.PostAsJsonAsync($"{ApiArea}/authenticate", model);
    if (response.IsSuccessStatusCode)
    {
        var jwtToken = await response.Content.ReadAsAsync<JWTToken>();

        /* --> WHAT DO I DO HERE? <-- */

    }
    else
    {
        ModelState.AddModelError("Password", "Invalid password");
        model.Password = "";
        return View(model);
    }

    return RedirectToAction("Index", "Home");
}

So to make matters complex, the overview of my project is as such:

ASP.NET Core Website - has login page and other controllers with ajax calls for datatables and Forms for Edit pages which must be authorized ASP.NET Core WebApi - generated JWT token and has methods for other api calls which must be authorized

How do I tell the Website that if user is not Authorized, then go to my /Account/Login page?

Is this process correct, if not do I still need to add Identity and do this differently for Website?

like image 414
Umair Avatar asked Feb 24 '19 01:02

Umair


People also ask

How JWT token works in Web API?

JSON Web Tokens are an open, standard way for you to represent your user's identity securely during a two-party interaction. First, the user or client app sends a sign-in request. In this step, essentially, a username, password, or any other type of sign-in credentials the user provides will travel to the API.

What is JWT token in Web API core?

JSON Web Tokens (commonly known as JWT) is an open standard to pass data between client and server, and enables you to transmit data back and forth between the server and the consumers in a secure manner. This article talks about how you can take advantage of JWTs to protect APIs.


1 Answers

If your ASP.NET Core Website and ASP.NET Web API are two different websites :

  • For the WebAPI, the client should always send a request by adding a header of Authorization : Bearer {access_token}. Or register an OnMessageReceivedhandler if you would like send it via cookie/querystring
  • For the ASP.NET Core Website, the browser should use cookies or JWT as credentials.

I'm not sure how your authentication looks like.

Assuming you choose to use cookies for ASP.NET Core Website, make sure you've set the LoginPath = "/Account/Login";

// the Startup::ConfigureServices of your ASP.NET Core Website
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(o => {
        o.LoginPath = "/Account/Login";
    });

And then as suggested by Camilo Terevinto, you need to sign the user in :

    [HttpPost]
    public async Task<IActionResult> Login(LoginModel model)
    {
        var httpClient = _httpClientFactory.CreateClient(ConstantNames.WebApi);
        var response = await httpClient.PostAsJsonAsync($"{ApiArea}/authenticate", model);
        if (response.IsSuccessStatusCode)
        {
            var jwtToken = await response.Content.ReadAsAsync<JWTToken>();

            var username = ...
            var others = ...
            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, username),
                // add other claims as you want ...
            };
            var iden= new ClaimsIdentity( claims, CookieAuthenticationDefaults.AuthenticationScheme);
            var principal = new ClaimsPrincipal(iden);
            await HttpContext.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme, principal);
            return Redirect("/")

        }
        else
        {
            ModelState.AddModelError("Password", "Invalid password");
            model.Password = "";
            return View(model);
        }

        return RedirectToAction("Index", "Home");
    }
like image 189
itminus Avatar answered Oct 13 '22 03:10

itminus