7
votes

I implemented a JWT Bearer Token- Authentication in my Web-API that runs with .net core 2.0. Now I created another Website that speaks with my Web-API. Retrieving the token works, I'm adding it into cookies, when I'm debugging I can see my cookie (name is "identity") with the correct value.

In the projects template there is the controller HomeController with the actions. I'm using the action Contact for my purposes and annotate it with the AuthorizeAttribute:

[Authorize]
public IActionResult Contact()
{
    ViewData["Message"] = "Your contact page.";

    return View();
}

Now I'm navigating (as an anonymous user) to /home/contact - perfect: it redirects me to /home/login where I need to login.

When I'm trying to login I get following error-message:

No IAuthenticationSignInHandler is configured to handle sign in for the scheme: Bearer

I guess the token configuration is wrong- I guess there are a few more things I'm doing wrong here.

First, here is my Startup.cs (I didn't delete anything since there are dependencies on the order):

public void ConfigureServices(IServiceCollection services)
{
    services.AddDistributedMemoryCache();

    services.AddSession(options =>
    {
        options.IdleTimeout = TimeSpan.FromMinutes(30);
        options.Cookie.HttpOnly = true;
    });

    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
    {

        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("mysupersecret_secretkey!123")),
            ValidateIssuer = true,
            ValidIssuer = "ExampleIssuer",
            ValidateAudience = true,
            ValidAudience = "ExampleAudience",
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero,
            SaveSigninToken = true
        };
        options.Events = new JwtBearerEvents
        {
            OnTokenValidated = context =>
            {
                JwtSecurityToken accessToken = context.SecurityToken as JwtSecurityToken;
                if (accessToken != null)
                {
                    ClaimsIdentity identity = context.Result.Principal.Identity as ClaimsIdentity;
                    identity?.AddClaim(new Claim("access_token", accessToken.RawData));
                }

                return Task.CompletedTask;
            }
        };
     })
    .AddCookie(
           o =>
           {
               o.Cookie.Name = "beareridentity";
               o.LoginPath = new PathString("/Home/Login/");
               o.AccessDeniedPath = new PathString("/Home/Login/");
           });

    services.AddMvc();

    services.AddTransient<IAccountService, AccountService>();
    services.AddTransient(typeof(ISession), serviceProvider =>
    {
        var httpContextAccessor = serviceProvider.GetService<IHttpContextAccessor>();
        return httpContextAccessor.HttpContext.Session;
    });
}


public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseSession();
    app.UseStaticFiles();
    app.UseAuthentication();


    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

and here is my login-action:

[HttpPost]
public async Task<IActionResult> Login(LoginData data)
{
    var loginresult = (await _accountService.GetLoginToken(data.Username, data.Password));

    if (!loginresult.Success)
        return RedirectToAction("Login");

    Response.Cookies.Append("identity", loginresult.Token, new CookieOptions { Expires = DateTimeOffset.Now.Add

    int id = await _getIdFromToken(loginresult);

    ApplicationUser user;
    await _signin(user = await _accountService.GetUserAsync(id));
    _session.SetData("actualuser", user);

    return RedirectToAction("Index");
}

private async Task _signin(ApplicationUser c)
{
    var claims = new List<Claim>
    {
        new Claim(ClaimTypes.MobilePhone, c.Phone??""),
        new Claim(ClaimTypes.Name, c.UserName)
    };
    var userIdentity = new ClaimsIdentity();
    userIdentity.AddClaims(claims);
    ClaimsPrincipal userPrincipal = new ClaimsPrincipal(userIdentity);

    try
    {
        await HttpContext.SignInAsync(
            JwtBearerDefaults.AuthenticationScheme,
            userPrincipal,
            new Microsoft.AspNetCore.Authentication.AuthenticationProperties
                {
                    ExpiresUtc = DateTime.UtcNow.AddMinutes(20),
                    IsPersistent = true,
                    AllowRefresh = true,
                    IssuedUtc = DateTimeOffset.Now
                });
    }
    catch (Exception e)
    {
        throw;
    }
}
3
Have you had a read of this: JWT Token Authentication with Cookies in ASP.NET Core? I have not tried it but it looks like that is what you are trying to do.Frank Fajardo
@FrankFajardo thanks for this link. I implemented it into my application and it works for now. AYCS there is only dummy-data, so I need to switch now that dummy data to productive data (that comes from my service/api).Matthias Burger
so I assume that answered your question. What does AYCS mean?Frank Fajardo
In a way it answered my question yes. I didn't solve it yet but the link helped, yes. 'As you can see' :DMatthias Burger
@FrankFajardo with your help (your link) I finally got it - thank you! You mind posting it in a short answer? (Creating that cookie and the login-method were the problems)Matthias Burger

3 Answers

2
votes

Here is a blog post on how you can use cookies as a delivery mechanism for your JWTs on ASP.NET Core 2.0, which is what you are trying to do: JWT Token Authentication with Cookies in ASP.NET Core

I have not tried it, but it could guide you on understanding where you could be doing it wrong.

0
votes

The default scheme doesn't match any handler (in this case the cookie). If you can, try fitting this into your code (this might be .net core 2 specific):

services.AddAuthentication("YourSchemeNameHere")
.AddCookie("YourSchemeNameHere", options => 
{
    ...
});

If that doesn't work, change the AuthenticationScheme property in the options of the cookie to match the DefaultAuthenticateScheme in the authentication options.

0
votes

I had a similar problem. Please check the Web.config and check if you have a node like or check if there is a node for authentication, maybe this cause that, or maybe the configuration is not correct.

I am developing a mvc 5 app and my web config is like this

  <modules>
  <remove name="FormsAuthentication" />
  <remove name="ApplicationInsightsWebTracking" />
  <add name="ApplicationInsightsWebTracking" 
   type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, 
   Microsoft.AI.Web" preCondition="managedHandler" />
   </modules>
<authentication mode="None" />

I am using the pre built asp.net authentication modules with their db