12

I have a project in MVC using .NET Framework v4.7 with some WebApi on it. What I need to know is how to use a middleware between then to authorize a JWT for HTTP requests and MVC Action requests.

I've searched everywhere looking for a solution sample, but I couldn't find anything.

If anyone could help, I would be grateful.

Sorry for the English.

Mauro Bilotti
  • 5,628
  • 4
  • 44
  • 65
Pedro Lopes
  • 131
  • 1
  • 5

2 Answers2

8

If I grasped your problem statement, I think OWIN might be an option: you decouple your application from underlying hosting and get an extensible pipeline that you can inject middleware into (pretty much like .net core works out of the box).

Even better - it comes with JWT support out of the box (well, you need to install a few nuget packages - see below). Then you simply enable it on your IAppBuilder and roll with standard [Authorize] attributes.

To demo this setup, I've put together a working GitHub repo here to illustrate WebApi middleware.

Apart from Microsoft.AspNet.WebApi.Owin, Microsoft.Owin.Host.SystemWeb and Microsoft.Owin.Security.Jwt nuget packages, it's pretty much a stock standard asp.net WebApi project with the following files changed:

/Startup.cs

using System.Text;
using System.Web.Http;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Jwt;
using Owin;

namespace OWIN.WebApi
{
    public class Startup
    {
        public void Configuration(IAppBuilder appBuilder)
        {
            HttpConfiguration config = new HttpConfiguration();
            WebApiConfig.Register(config); // bootstrap your existing WebApi config 

            appBuilder.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
            {
                AuthenticationMode = AuthenticationMode.Active,
                TokenValidationParameters = new TokenValidationParameters()
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateIssuerSigningKey = true, // I guess you don't even have to sign the token
                    ValidIssuer = "http://localhost",
                    ValidAudience = "http://localhost",
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("jwt_signing_secret_key"))
                }
            });
            appBuilder.UseWebApi(config); // instruct OWIN to take over
        }
    }
}

/Controllers/ProtectedValuesController.cs

using System.Collections.Generic;
using System.Web.Http;

namespace OWIN.WebApi.Controllers
{
    [Authorize]
    public class ProtectedValuesController : ApiController
    {
        // GET api/values
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }
    }
}

/Controllers/ObtainJwtController.cs

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using System.Web.Http;
using Microsoft.IdentityModel.Tokens;
using Claim = System.Security.Claims.Claim;

namespace OWIN.WebApi.Controllers
{
    // this class is literally just a test harness to help me generate a valid token for popping into Postman.
    public class ObtainJwtController: ApiController
    {
        private string CraftJwt()
        {
            string key = "jwt_signing_secret_key"; //Secret key which will be used later during validation    
            var issuer = "http://localhost";

            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
            var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

            var permClaims = new List<Claim>
            {
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                new Claim("valid", "1"),
                new Claim("userid", "1"),
                new Claim("name", "test")
            };

            var token = new JwtSecurityToken(issuer,
                issuer,
                permClaims,
                expires: DateTime.Now.AddDays(1),
                signingCredentials: credentials);
            return new JwtSecurityTokenHandler().WriteToken(token);
        }

        public string Get()
        {
            return $"Bearer {CraftJwt()}";
        }
    }
}

This appears to work for MVC too

I have added a few extra nuget packages to do with ASP.NET Identity, which seems to have enabled me to successfully protect the following controller:

/Controllers/Home.cs

using System.Web.Mvc;

namespace OWIN.WebApi.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Title = "Home Page";

            return View();
        }

        [Authorize]
        public ActionResult Protected()
        {
            return View();
        }
    }
}

Hopefully that gives you some options to explore

timur
  • 14,239
  • 2
  • 11
  • 32
  • 1
    please, make sure you mean a classic asp.net mvc, not a netcore app – anatol Mar 04 '20 at 07:04
  • @anatol it is exclusively a full .net framework solution as far as I’m aware – timur Mar 04 '20 at 07:07
  • that doesn't work with `[Authorize]` attribute in mvc controllers – anatol Mar 04 '20 at 07:09
  • @anatol okay, let me have another look – timur Mar 04 '20 at 07:09
  • 1
    there only cookie auth could be used, as it seems – anatol Mar 04 '20 at 07:10
  • Right. It seems, [this SO answer](https://stackoverflow.com/a/22278642/12339804) confirms your theory - MVC does not play nice with OWIN. Then you might be forced to take that second option and implement the handler – timur Mar 04 '20 at 10:56
  • are you sure about handler? seams it relates to web api only – anatol Mar 04 '20 at 14:12
  • I have just tested my test example with protected MVC action - seems to work as expected. will update github shortly – timur Mar 05 '20 at 02:23
  • but how could you provide the token for mvc requests and how do you able to achieve correct validation? – anatol Mar 05 '20 at 07:27
  • I was just using postman with Authorization Bearer token header for both endpoints. I think I saw it somewhere that one could store jwt in stock standard asp Auth cookie and get that passed along. Not sure if I would be able to quickly find the source for you, but it seems very doable – timur Mar 05 '20 at 07:51
-3

See what I understand from your question You want to use JWT Token for your WebApi and normal process for your MVC, as for later View generated from server and its not dependent on JWT Token.

I have seen this problem. In one of my application I am hosting both MVC views and webapi, whereas MVC view rely on cookie based authentication and authorization. and WebApi call depend upon JWT token for authorization and authentication.

Sorry this solution is from .net core. you need to configure both Cookie and JWT authentication like this

services.AddCookie()
.AddJwtBearer()

Here default would be your cookie authentication. Now to enable jwt token for your webapi you need to decorate webapi with following attribute

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

Hope that's answer your question.

thatsalok
  • 135
  • 1
  • 8