6

I want to use both cookie based authentication and jwt in my program, used authentication user to access mvc controller with login and JWT to access WebApi resource.

I tried using two of them First, my client can login and authenticate with the cookie using username and password. Second access resource from Application with WebApi with Token Bearer but I get an error!

In my startup.cs file I have:

public void ConfigureServices(IServiceCollection services)
        {


            services.Configure<CookiePolicyOptions>(options =>
            {
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
                options.ConsentCookie.Name = "Cookie";
            });
            services.ConfigureApplicationCookie(options =>
            {
                options.Cookie.Name = "Cookie";
                options.ClaimsIssuer = Configuration["Authentication:ClaimsIssuer"];
            });

            services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");

            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));

            services.AddIdentity<ApplicationUser, ApplicationRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultUI(UIFramework.Bootstrap4)
                .AddDefaultTokenProviders();

            services.Configure<IdentityOptions>(options =>
            {
                // Password settings.
                options.Password.RequireDigit = true;
                options.Password.RequireLowercase = true;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = false;
                options.Password.RequiredLength = 5;
                options.Password.RequiredUniqueChars = 1;

                // Lockout settings.
                options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
                options.Lockout.MaxFailedAccessAttempts = 5;
                options.Lockout.AllowedForNewUsers = true;

                // User settings.
                options.User.AllowedUserNameCharacters =
                "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
                options.User.RequireUniqueEmail = false;

                //Token
            });

            services.AddAuthentication(options =>
            {
                options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;

            })
                .AddCookie(options =>
                {
                    options.Cookie.Name = "Cookie";
                    options.ClaimsIssuer = Configuration["Authentication:ClaimsIssuer"];
                })
                .AddMicrosoftAccount(microsoftOptions =>
                 {
                     microsoftOptions.ClientId = Configuration["Authentication:Microsoft:ApplicationId"];
                     microsoftOptions.ClientSecret = Configuration["Authentication:Microsoft:Password"];
                 })
                .AddGoogle(googleOptions => 
                {
                    googleOptions.ClientId = "XXXXXXXXXXX.apps.googleusercontent.com";
                    googleOptions.ClientSecret = "g4GZ2#...GD5Gg1x";
                    googleOptions.Scope.Add("https://www.googleapis.com/auth/plus.login");
                    googleOptions.ClaimActions.MapJsonKey(ClaimTypes.Gender, "gender");
                    googleOptions.SaveTokens = true;
                    googleOptions.Events.OnCreatingTicket = ctx =>
                    {
                        List<AuthenticationToken> tokens = ctx.Properties.GetTokens()
                            as List<AuthenticationToken>;
                        tokens.Add(new AuthenticationToken()
                        {
                            Name = "TicketCreated",
                            Value = DateTime.UtcNow.ToString()
                        });
                        ctx.Properties.StoreTokens(tokens);
                        return Task.CompletedTask;
                    };
                })
                .AddJwtBearer(options =>
                {
                    options.ClaimsIssuer = Configuration["Authentication:ClaimsIssuer"];
                    options.SaveToken = true;
                    options.Authority = Configuration["Authentication:Authority"];
                    options.Audience = Configuration["Authentication:Audience"];
                    options.RequireHttpsMetadata = false;
                    options.TokenValidationParameters = new TokenValidationParameters()
                    {

                        ValidateIssuerSigningKey = true,

                        ValidateIssuer = true,
                        ValidIssuer = Configuration["Authentication:ValidIssuer"],

                        ValidateAudience = true,
                        ValidAudience = Configuration["Authentication:ValidAudience"],

                        ValidateLifetime = true,

                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Authentication:SecurityKey"]))
                    };
                });






            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
            services.AddSession();

            services.AddSingleton<IConfiguration>(Configuration);

        }

And I got a token in this controller:

[AllowAnonymous]
        [HttpPost]
        public async Task<IActionResult> GetToken(TokenLoginModel model)
        {

            if (!ModelState.IsValid) return BadRequest("Token failed to generate");
            var user = await _usermanager.FindByNameAsync(model.UserName);
            //var user = true;// (model.Password == "password" && model.Username == "username");
            if (user != null && await _usermanager.CheckPasswordAsync(user, model.Password))
            {
                var claims = new[]{
                    new Claim("ClaimsIssuer", _configuration.GetSection("Authentication:ClaimsIssuer").Value),
                new Claim(Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames.Sub,user.UserName),
                new Claim(Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString())
            };
                string SecurKey = Startup.StaticConfig.GetSection("Authentication:SecurityKey").Value;
                var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecurKey));
                var token = new JwtSecurityToken(
                    issuer: _configuration.GetSection("Authentication:ValidIssuer").Value,
                    audience: _configuration.GetSection("Authentication:Audience").Value,
                    expires: DateTime.UtcNow.AddDays(30),
                    claims: claims,
                    signingCredentials: new Microsoft.IdentityModel.Tokens.SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256)
                );
                return Ok(new
                {
                    token = new JwtSecurityTokenHandler().WriteToken(token),
                    expiration = token.ValidTo
                });
            }
            return Unauthorized();

        }

I implement control that creates token, but when I tried authorizing with that I get this error:

An unhandled exception occurred while processing the request.

HttpRequestException: Response status code does not indicate success: 404 (Not Found).
System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()

IOException: IDX20804: Unable to retrieve document from: 'https://localhost:44383/oauth2/default/.well-known/openid-configuration'.
Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(string address, CancellationToken cancel)

InvalidOperationException: IDX20803: Unable to obtain configuration from: 'https://localhost:44383/oauth2/default/.well-known/openid-configuration'.
Microsoft.IdentityModel.Protocols.ConfigurationManager<T>.GetConfigurationAsync(CancellationToken cancel)
1
  • Remove options.Authority = Configuration["Authentication:Authority"];. Commented May 12, 2019 at 21:34

1 Answer 1

14

In order to add support for JWT, we added the AddCookie and AddJwtBearer. Having websites require the token in the header would be a headache, especially for projects that aren’t purely SPA or API. So what I really wanted was support for both Cookies and JWTs.

In startup.cs you have:

    public class Startup
  {
    public Startup(IConfiguration configuration)
    {
      Configuration = configuration;
    }
    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
      services.AddDbContext<DualAuthContext>(options =>
          options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

      services.AddIdentity<ApplicationUser, IdentityRole>()
          .AddEntityFrameworkStores<DualAuthContext>()
          .AddDefaultTokenProviders();

      // Enable Dual Authentication 
      services.AddAuthentication()
        .AddCookie(cfg => cfg.SlidingExpiration = true)
        .AddJwtBearer(cfg =>
        {
          cfg.RequireHttpsMetadata = false;
          cfg.SaveToken = true;
          cfg.TokenValidationParameters = new TokenValidationParameters()
          {
            ValidIssuer = Configuration["Tokens:Issuer"],
            ValidAudience = Configuration["Tokens:Issuer"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Tokens:Key"]))
          };
        });

      // Add application services.
      services.AddTransient<IEmailSender, EmailSender>();
      services.AddMvc();
    }

And In Configure method:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, DataSeeder seeder)
{
  ...
  app.UseAuthentication();
}

After this in your controller that one you have used JWT, You should add JWT Bearer AuthenticationSchemes to Authorize attribute like this :

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
  [Route("/api/customers")]
  public class ProtectedController : Controller
  {
    public ProtectedController()
    {
    }

    public IActionResult Get()
    {
      return Ok(new[] { "One", "Two", "Three" });
    }
  }

Refrence: Two AuthorizationSchemes in ASP.NET Core 2

It's very simple and helpful to used.

Sign up to request clarification or add additional context in comments.

8 Comments

does this mean, If you specify the route/endpoint for jwt or cooikes, then it cant used vice-versa? So any endpoint must have two respectively right?
another point Authorize Role is working fine , but policy is not
You should add Policy in ConfigureServices first then it work fine
it worked for cookies based but not jwt token
Which version of .NET Core you using? this base on asp.net core 2.2 and for other version this is deferent!
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.