7

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?

5
  • You could use just await HttpContext.SignInAsync(...) Commented Feb 24, 2019 at 1:44
  • The WebAPI usually returns 401 Unauthorized response and doesn't do a redirect to login page, redirect has to be either done on the client side or in your MVC controller. Commented Feb 24, 2019 at 2:10
  • @CamiloTerevinto Thanks will give it a try, and where do I store the jwt token itself for further API calls, is there a builtin way of doing it? Commented Feb 24, 2019 at 12:07
  • A common way would be to put it in a Cookie, but ensure it's set as Secure Commented Feb 24, 2019 at 14:19
  • Thanks, I have been trying await HttpContext.SignInAsync(...) but when I use it to create principal and pass it with some claims. The user does not signin, and is redirected to login page. Commented Feb 24, 2019 at 15:46

1 Answer 1

6

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");
    }
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.