Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

storing JWT token in cookie in MVC 5

I wanted to make JWT auth in my MVC app. I make Authorization web service in Web API which returns token correctly. After that Im trying to store token in cookie.

 [HttpPost]
    public async Task<ActionResult> Login(LoginDto loginDto)
    {
        var token = await loginService.GetToken(loginDto);

        if (!string.IsNullOrEmpty(token))
        {
            var cookie = new System.Web.HttpCookie("token", token)
            {
                HttpOnly = true
            };
            Response.Cookies.Add(cookie);
            return RedirectToAction("Index", "Product");
        }
        return View("LoginFailed");
    }

But now I wanted to add this token to headers for every request. So I decided action filters would be best for achieve this.

public class CustomActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var token = filterContext.HttpContext.Request.Cookies.Get("token");

        if (token != null)
            filterContext.HttpContext.Request.Headers.Add("Authorization", $"Bearer {token}");

        base.OnActionExecuting(filterContext);
    }
}

Startup

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        AutofacConfig.Configure();
        AreaRegistration.RegisterAllAreas();
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

        ConfigureOAuth(app);
    }

    public void ConfigureOAuth(IAppBuilder app)
    {
        var issuer = System.Configuration.ConfigurationManager.AppSettings["issuer"];
        var audience = System.Configuration.ConfigurationManager.AppSettings["appId"];
        var secret = TextEncodings.Base64Url.Decode(System.Configuration.ConfigurationManager.AppSettings["secret"]);

        app.UseJwtBearerAuthentication(
            new JwtBearerAuthenticationOptions
            {
                AuthenticationMode = AuthenticationMode.Active,
                AllowedAudiences = new[] { audience },
                IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
                {
                    new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
                },

            });

    }
}

And then I just marked controller which authorize attribute. It works fine when I called it with POSTMAN.

But action filters in MVC are fired always after authorization filter. So I have questions:

  1. How to add token from cookie to every request ? Is it good practice? If not what I should do?
  2. How about csrf attacks and others ? Is AntiForgeryTokenAttr will do the work? What about ajax calls then?

Additional Information

This is how login service looks like. Its just making call to auth endpoint.

 public class LoginService : ILoginService
{
    public async Task<string> GetToken(LoginDto loginDto)
    {
        var tokenIssuer = ConfigurationManager.AppSettings["issuer"];
        using (var httpClient = new HttpClient {BaseAddress = new Uri($"{tokenIssuer}/oauth2/token")})
        {
            using (var response = await httpClient.PostAsync(httpClient.BaseAddress, new FormUrlEncodedContent(
                new List<KeyValuePair<string, string>>
                {
                    new KeyValuePair<string, string>("username", loginDto.Username),
                    new KeyValuePair<string, string>("password", loginDto.Password),
                    new KeyValuePair<string, string>("grant_type", "password"),
                    new KeyValuePair<string, string>("client_id", ConfigurationManager.AppSettings["appId"])
                })))
            {
                var contents = await response.Content.ReadAsStringAsync();

                if (response.StatusCode == HttpStatusCode.OK)
                {
                    var deserializedResponse =
                        new JavaScriptSerializer().Deserialize<Dictionary<string, string>>(contents);

                    var token = deserializedResponse["access_token"];

                    return token;
                }
            }
            return null;
        }
    }
}
like image 849
TjDillashaw Avatar asked Jul 09 '17 07:07

TjDillashaw


1 Answers

I found a solution. I just make custom OAuthBearerAuthenticationProvider provider and inside this class Im retrieve token from cookie and then assign this to context.Token

public class MvcJwtAuthProvider : OAuthBearerAuthenticationProvider
{
    public override Task RequestToken(OAuthRequestTokenContext context)
    {
        var token = context.Request.Cookies.SingleOrDefault(x => x.Key == "token").Value;

        context.Token = token;
        return base.RequestToken(context);
    }
}

And then inside startup.cs

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        AutofacConfig.Configure();
        AreaRegistration.RegisterAllAreas();
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

        ConfigureOAuth(app);
    }

    public void ConfigureOAuth(IAppBuilder app)
    {
        var issuer = System.Configuration.ConfigurationManager.AppSettings["issuer"];
        var audience = System.Configuration.ConfigurationManager.AppSettings["appId"];
        var secret = TextEncodings.Base64Url.Decode(System.Configuration.ConfigurationManager.AppSettings["secret"]);

        app.UseJwtBearerAuthentication(
            new JwtBearerAuthenticationOptions
            {
                AuthenticationMode = AuthenticationMode.Active,
                AllowedAudiences = new[] { audience },
                IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
                {
                    new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
                },
                Provider = new MvcJwtAuthProvider() // override custom auth

            });

    }
}
like image 86
TjDillashaw Avatar answered Sep 21 '22 15:09

TjDillashaw