0
votes

I am trying out Web API's in .net core. I have added an API controller to my existing .net core MVC web application.

I am using JWT Tokens for authorization. I am doing this through the startup class.

The API works just fine.

But as expected the authorization is being applied on the entire MVC application.

Is there a way I can enable authentication through bearer tokens only for the API controllers and not the web application controllers?

Startup.cs:

using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Project.Data;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

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

        public IConfiguration Configuration { get; }
        public bool ValidateAudience { get; private set; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));

            services.AddIdentity<IdentityUser,IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

            services.Configure<CookiePolicyOptions>(options =>
            {
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;

            });

            services.ConfigureApplicationCookie(options =>
            {
                options.Cookie.HttpOnly = true;
                options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
                options.LoginPath = "/Identity/Account/Login"; 
                options.LogoutPath = "/Identity/Account/Logout"; 
                options.AccessDeniedPath = "/Identity/Account/AccessDenied"; 
                options.SlidingExpiration = true;
            });

            services.AddCors();

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

            services.AddAuthentication(option =>
            {
                option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                option.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options =>
            {
                options.SaveToken = true;
                options.RequireHttpsMetadata = true;
                options.TokenValidationParameters = new TokenValidationParameters()
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidAudience = Configuration["Jwt:Site"],
                    ValidIssuer = Configuration["Jwt:Site"],
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:SigningKey"]))
                };
            });

        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();

            app.UseAuthentication();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{Controller=Startup}/{action=Login}/{id?}");
            });
        }
    }
}

appsettings.json:

{
  "ConnectionStrings": {
    "DefaultConnection": " Data Source=Server; Database = db;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Jwt": {
    "Site": "www.signinkey.com",
    "SigningKey": "ConstantSigningKey",
    "ExpiryInMinutes": "30"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*"
}

API controller:

namespace Project.Controllers
{
    [Route("api/[controller]/[action]")]
    public class AuthController : BaseController
    {
        protected IConfiguration _configuration;

        public AuthController(UserManager<IdentityUser> userManager, IConfiguration configuration)
        {
            _userManager = userManager;
            _configuration = configuration;
        }

        public string GenerateToken(int size = 32)
        {
            var randomNumber = new byte[size];
            using (var rng = RandomNumberGenerator.Create())
            {
                rng.GetBytes(randomNumber);
                return Convert.ToBase64String(randomNumber);
            }
        }

        [HttpGet("")]
        [Route("modelList")]
        [Authorize]
        public IEnumerable<ModelList> SupervisorList(string username)
        {
             return db.modelList.Select(x => x).ToList();
        }

        [Route("register")]
        [HttpPost]
        public async Task<ActionResult> Register([FromBody] InputModel reg)
        {
            var user = new IdentityUser { UserName = reg.Email, Email = reg.Email, SecurityStamp = Guid.NewGuid().ToString() };
            var result = await _userManager.CreateAsync(user, reg.Password);
            if (result.Succeeded)
            {
                await _userManager.AddToRoleAsync(user, "Admin");
            }
            return Ok(new { Username = user.UserName });

        }

        [Route("login")]
        [HttpPost]
        public async Task<ActionResult> Login([FromBody] InputModel login)
        {
            var user = await _userManager.FindByNameAsync(login.Email);
            if (user != null && await _userManager.CheckPasswordAsync(user, login.Password))
            {

                var claim = new[]
                {
                    new Claim(JwtRegisteredClaimNames.Sub, user.UserName)
                };
                var signinKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SigningKey"]));

                int expiryInMinutes = Convert.ToInt32(_configuration["Jwt:ExpiryInMinutes"]);

                var token = new JwtSecurityToken(
                    issuer: _configuration["Jwt:Site"],
                    audience: _configuration["Jwt:Site"],
                    expires: DateTime.UtcNow.AddMinutes(expiryInMinutes),
                    signingCredentials: new SigningCredentials(signinKey, SecurityAlgorithms.HmacSha256)
                    );


                return Ok(
                    new
                    {
                        token = new JwtSecurityTokenHandler().WriteToken(token),
                        expiration = token.ValidTo
                    });


            }
            return Unauthorized();
        }
    }
}

As the JWT Token authentication is enabled in the startup class; I am getting an 401 unauthorized code when I try to access the non api controller actions as well.

What I am trying is:

  1. Add an API controller to an existing .net core mvc web application.
  2. Use JWT token authentication only for the API methods and not the web application methods.

    • Is the above possible? Is it a good practice? How can it be achieved?

Need Direction. Thank you:)

2

2 Answers

1
votes

Detailed Articles

Using Multiple Authentication/Authorization Providers in ASP.NET Core

Yes you can here are few steps

  1. Setup multiple authentication schemes in Startup.cs
  2. Use scheme as per controller
  3. Specify the Policy for web application and used that in Web app controllers [Authorize(Policy = "WebApp")]
  4. In Web API controllers just use the JWT authentication scheme

    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

Startup code

        .AddCookie(options =>
        {
            // You cookie auth setup 
        })
        .AddJwtBearer(options =>
        {
          // Your JWt setup
        })

policies Setup

services.AddAuthorization(options =>
        {
            options.AddPolicy("WebApp",
                              policy => policy.Requirements.Add(new WebAppRequirement()));
        });
1
votes

You can write your own authorization attribute in .NET. Like [CustomAuthorization] instead of built-in attributer. For learning phase of course.