3

I am trying to login using ClaimsPrincipal and then fetch a JWT in .net core 2.0. With my current code, I get the error from the result of the SignInAsync function: "No IAuthenticationSignInHandler is configured to handle sign in for the scheme: Bearer"

Here is the controller I am currently using:

[Route("Login/{username}")]
public async Task<IActionResult> Login(string username)
{
    var userClaims = new List<Claim>
    {
        new Claim(ClaimTypes.Name, username)
    };
    var principal = new ClaimsPrincipal(new ClaimsIdentity(userClaims));
    var sign = HttpContext.SignInAsync(principal);
    await sign;
    var res = await HttpContext.AuthenticateAsync();
    var token = await HttpContext.GetTokenAsync("access_token");
    return Json(token);
}

The login portion was tested and works well with cookies. However when I use the following code with JwtBearerDefaults.AuthenticationScheme in my startup.cs:

services.AddAuthentication(config => {
    config.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    config.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(config =>
{
    config.TokenValidationParameters = Token.tokenValidationParameters;
    config.RequireHttpsMetadata = false;
    config.SaveToken = true;
});

I get the error from the result of the SignInAsync function: "No IAuthenticationSignInHandler is configured to handle sign in for the scheme: Bearer"

My Token class was created with the help of a code I found online (at JWT on .NET Core 2.0) and is defined as follows:

public static class Token
{

    public static TokenValidationParameters tokenValidationParameters {
        get
        {

            return new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = GetSignInKey(),
                ValidateIssuer = true,
                ValidIssuer = GetIssuer(),
                ValidateAudience = true,
                ValidAudience = GetAudience(),
                ValidateLifetime = true,
                ClockSkew = TimeSpan.Zero
            };
        }
    }

    static private SymmetricSecurityKey GetSignInKey()
    {
        const string secretKey = "very_long_very_secret_secret";
        var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));

        return signingKey;
    }

    static private string GetIssuer()
    {
        return "issuer";
    }

    static private string GetAudience()
    {
        return "audience";
    }

}
Neville Nazerane
  • 6,622
  • 3
  • 46
  • 79

2 Answers2

5

If I understand it correctly from looking at the source code for JwtBearerHandler, it does not implement IAuthenticationSignInHandler, which is why you are getting this error. Call to SignInAsync is designed to persist authentication information, such as created auth cookie which, for instance, is exactly what CookieAuthenticationHandler does. But for JWT there is no single well-known place to store the token, hence no reason to call SignInAsync at all. Instead of that, grab the token and pass it back to the browser. Assuming you are redirecting, you can tuck it into a query string. Assuming browser application is an SPA (i.e. Angular-based) and you need tokens for AJAX calls, you should store token in the SPA and send it with every API request. There are some good tutorials on how to use JWT with SPAs of different types, such as this: https://medium.com/beautiful-angular/angular-2-and-jwt-authentication-d30c21a2f24f

Keep in mind that JwtBearerHandler expects to find Authentication header with Bearer in it, so if your AJAX calls are placing token in query string, you will need to supply JwtBearerEvents.OnMessageReceived implementation that will take token from query string and put it in the header.

iTolik
  • 109
  • 2
  • what is the need to sent the token from the client and pass it back? do you know how to implement the IAuthenticationSignInHandler? – Neville Nazerane Sep 04 '17 at 00:40
  • Token is only passed back from the server to the client once, immediately after login. Token contains signed user identity. Server is stateless, hence browser client must send token to the server with each API call, which means that your AJAX call should include Authentication header containing Bearer notation. – iTolik Sep 04 '17 at 04:09
  • 1
    User enters name/password into your client, they are passed to the server and validated. If everything checks out, token (in JWT format, and it's signed by the server, so server will be able to validate it later) is generated and sent back. Client stores token and uses it in further API calls. That's the flow. It looks like in your Login action you are already generating JTW. That's perfect, because now all you need to do is return it. You can attach it to query string if you are doing redirect, or you can put it in response header, or as JSON if login was on AJAX. – iTolik Sep 04 '17 at 04:09
  • In any case, it is my opinion that you do not need to implement IAuthenticationSignInHandler for this at all. – iTolik Sep 04 '17 at 04:09
  • I was aware of how the flow works. I have worked with token in the client several times. In fact I was mainly looking for a way to generate the token in the server. The way above was not really working. I still have no idea what the `HttpContext.GetTokenAsync` actually does. – Neville Nazerane Sep 09 '17 at 04:52
  • Token generation I use is close to what you have marked as correct answer. Except for I create token a bit differently: `var token = new JwtSecurityToken( issuer: Options.Issuer, audience: Options.ValidAudience, claims: claims, expires: DateTime.UtcNow.AddMinutes(10),` – iTolik Sep 10 '17 at 05:56
  • ` signingCredentials: new SigningCredentials( new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Options.IssuerSigningKey)), SecurityAlgorithms.HmacSha256) ); var tokenString = new JwtSecurityTokenHandler().WriteToken(token);` – iTolik Sep 10 '17 at 05:57
  • Not sure you need to worry about GetTokenAsync at all. You are generating new token and returning it, so you are done. No need for SignInAsync either. If you call `services.AddAuthentication(options => { //set options here }).AddJwtBearer(options => { { //more options for Bearer auth here }` Then token will be retrieved from Authentication header and claims identity created for you. – iTolik Sep 10 '17 at 06:06
1

A signed token can be created using the JwtSecurityTokenHandler.

var handler = new JwtSecurityTokenHandler();
var jwt = handler.CreateJwtSecurityToken(new SecurityTokenDescriptor
{
     Expires = DateTime.UtcNow.Add(Expiary),
     Subject = new ClaimsIdentity(claims, "local"),
     SigningCredentials = new SigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256)
});
return handler.WriteToken(jwt);
Neville Nazerane
  • 6,622
  • 3
  • 46
  • 79