Thursday, 18 October 2018

MVC renew ` __RequestVerificationToken` token when logging in via angularjs

I have a MVC website with some AngularJS components.

When I make a post request using angularjs, I always include the __RequestVerificationToken token from a hidden input on the page.

My problem is this:

User starts an anonymous session i.e not logged in.

The user logs in using angularjs component, which sends a post request. My MVC controller validates the credentials and the '__RequestVerificationToken'. Once the user is logged in, it returns a the new token.

The angularjs controller then takes the new token and updates the hidden input to be used for any future requests.

However the next request I make using angularjs fails the validation because the var tokenCookie = filterContext.HttpContext.Request.Cookies.Get(AntiForgeryConfig.CookieName); (see below code sample) is still the old token from the anonymous session.

Although the "X-XSRF-Token" (see below code sample) is coming through as the new one.

How do I also update/renew the http cookie (tokenCookie) containing the token to the new one?

I have posted copy of my code below.

My action filter:

public sealed class WebApiValidateAntiForgeryTokenAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
            var headers = filterContext.HttpContext.Request.Headers;

            var tokenCookie = filterContext.HttpContext.Request.Cookies.Get(AntiForgeryConfig.CookieName);

            var tokenHeader = string.Empty;
            if (headers.AllKeys.Contains("X-XSRF-Token"))
            {
                tokenHeader = headers.GetValues("X-XSRF-Token").FirstOrDefault();
            }

           AntiForgery.Validate(tokenCookie != null ? tokenCookie.Value : null, tokenHeader);

        base.OnActionExecuting(filterContext);
    }
}

My login controller:

[WebApiValidateAntiForgeryTokenAttribute]
[HttpPost]
public ActionResult login(string email, string password)
{
    if (string.IsNullOrWhiteSpace(email) || string.IsNullOrWhiteSpace(password)) return new HttpUnauthorizedResult();

    var rq = HttpContext.Request;
    var r = validateLogin(email, password, true); // my login handling
    if (r.Success)
    {
        Response.StatusCode = (int)HttpStatusCode.OK;

        // within an action construct AJAX response and pass updated token to client
        return Json(new
        {
            __RequestVerificationToken = UpdateRequestVerificationToken(Request)
        });
    }
    else
    {
        return new HttpUnauthorizedResult();
    }
}

/// <summary>
/// resets AntiForgery validation token and update a cookie
/// The new antiforgery cookie is set as the results and sent
/// back to client with Ajax
/// </summary>
/// <param name="Request">request from current context</param>
/// <returns>string - a form token to pass to AJAX response</returns>
private string UpdateRequestVerificationToken(HttpRequestBase Request)
{
    string formToken;
    string cookieToken;
    const string __RequestVerificationToken = "__RequestVerificationToken";
    AntiForgery.GetTokens(Request.Form[__RequestVerificationToken], out cookieToken, out formToken);
    if (Request.Cookies.AllKeys.Contains(__RequestVerificationToken))
    {
        HttpCookie cookie = Request.Cookies[__RequestVerificationToken];
        cookie.HttpOnly = true;
        cookie.Name = __RequestVerificationToken;
        cookie.Value = cookieToken;
        Response.Cookies.Add(cookie);
    }
    return formToken;
}

My angularjs Login handling:

login(email, password) {
    return new Promise((resolve, reject) => {
        return this.AccountRequest.login(email, password)
            .then(response => {
                const newToken = response.data['__RequestVerificationToken'];
                const oldTokenElement = angular.element('input[name="__RequestVerificationToken"]');
                oldTokenElement.val(newToken); // confirmed the new token has been updated in the hidden element

                resolve(this.refresh.bind(this));
            });
    })
}

Every time I make a post request using angularjs:

post(uri, queryParams = {}, data = null) {

    this.$http.defaults.headers.common['X-XSRF-Token'] = angular.element('input[name="__RequestVerificationToken"]').attr('value');
    this.$http.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest'; // Needed to ensure we get back http error instead of a webpage
    const config = this.makeConfig('POST', uri, queryParams, data);

    return this.$http(config);
}

makeConfig(method, uri, params = {}, data = null) {
    return {
        method,
        data,
        params,
        url: uri.toString(),
    };
}



from MVC renew ` __RequestVerificationToken` token when logging in via angularjs

No comments:

Post a Comment