Anti-Forgery Tokens and ASP.NET Core APIs

Monday, February 6, 2017

In modern web programming, you can never have too many tokens. There are access tokens, refresh tokens, anti-XSRF tokens, and more. It’s the last type of token that I’ve gotten a lot of questions about recently. Specifically, does one need to protect against cross site requests forgeries when building an API based app? And if so, how does one create a token in an ASP.NET Core application?

Do I Need an XSRF Token?

In any application where the browser can implicitly authenticate the user, you’ll need to protect against cross-site request forgeries. Implicit authentication happens when the browser sends authentication information automatically, which is the case when using cookies for authentication, but also for applications using Windows authentication.

Generally, APIs don’t use cookies for authentication. Instead, APIs typically use bearer tokens, and custom JavaScript code running in the browser must send the token along by explicitly adding the token to a request.

However, there are also APIs living inside the same server process as a web application and using the same cookie as the application for authentication. This is the type of scenario where you must use anti forgery tokens to prevent an XSRF.

XSRF Tokens and ASP.NET Core APIs

There is no additional work required to validate an anti-forgery token in an API request, because the [ValidateAntiForgeryToken] attribute in ASP.NET Core will look for tokens in a posted form input, or in an HTTP header. But, there is some additional work required to give the client a token. This is where the IAntiforgery service comes in.

[Route("api/[controller]")]
public class XsrfTokenController : Controller
{
    private readonly IAntiforgery _antiforgery;

    public XsrfTokenController(IAntiforgery antiforgery)
    {
        _antiforgery = antiforgery;
    }

    [HttpGet]
    public IActionResult Get()
    {
        var tokens = _antiforgery.GetAndStoreTokens(HttpContext);

        return new ObjectResult(new {
            token = tokens.RequestToken,
            tokenName = tokens.HeaderName
        });
    }
}

In the above code, we can inject the IAntiforgery service for an application and provide an endpoint a client can call to fetch the token and token name it needs to use in a request. The GetAndStoreTokens method will not only return a data structure with token information, it will also issue the anti-forgery cookie the framework will use in one-half of the validation algorithm. We can use a new ObjectResult to serialize the token information back to the client.

Note: if you want to change the header name, you can change the AntiForgeryOptions during startup of the application [1].

With the endpoint in place, you’ll need to fetch and store the token from JavaScript on the client. Here is a bit of Typescript code using Axios to fetch the token, then configure Axios to send the token with every HTTP request.

import axios, { AxiosResponse } from "axios";
import { IGolfer, IMatchSet } from "models"
import { errorHandler } from "./error";

const XSRF_TOKEN_KEY = "xsrfToken";
const XSRF_TOKEN_NAME_KEY = "xsrfTokenName";

function reportError(message: string, response: AxiosResponse) {
    const formattedMessage = `${message} : Status ${response.status} ${response.statusText}`
    errorHandler.reportMessage(formattedMessage);
}

function setToken({token, tokenName}: { token: string, tokenName: string }) {
    window.sessionStorage.setItem(XSRF_TOKEN_KEY, token);
    window.sessionStorage.setItem(XSRF_TOKEN_NAME_KEY, tokenName);
    axios.defaults.headers.common[tokenName] = token;
}

function initializeXsrfToken() {
    let token = window.sessionStorage.getItem(XSRF_TOKEN_KEY);
    let tokenName = window.sessionStorage.getItem(XSRF_TOKEN_NAME_KEY);

    if (!token || !tokenName) {
        axios.get("/api/xsrfToken")
            .then(r => setToken(r.data))
            .catch(r => reportError("Could not fetch XSRFTOKEN", r));
    } else {
        setToken({ token: token, tokenName: tokenName });
    }
}

 

Summary

In this post we … well, forget it. No one reads these anyway.

[1] Tip: Using the name TolkeinToken can bring to life many literary references when discussing the application amongst team members.


Comments
gravatar Nick DeVore Monday, February 6, 2017
I read the summary. Well, I would if it were there ;)
gravatar Dave van Herten Monday, February 6, 2017
Best summary. Approve of the tip.
gravatar Jim Schneider Monday, February 6, 2017
I read the summaries.... :-) Great post Scott.
gravatar Muhammad Rehan Saeed Tuesday, February 7, 2017
So in summary (See what I did there), if you have your API and website as seperate apps, then you don't need anti-forgery tokens. Correct?
gravatar Emilio Tuesday, February 7, 2017
Hello Scott. What about using the [ValidateAntiForgeryToken] attribute in a Web API 2 project? AFAIK the attribute is only available in the System.Web.Mvc namespace.
gravatar Scott Tuesday, February 7, 2017
@Emilio: I believe you have to roll your own in WebAPI 2.
Tony Wednesday, February 8, 2017
I actually went to the summary first when reading the article.
gravatar Ray Thursday, February 9, 2017
I tend to read the articles till the end if they are well written and accurate. BTW this one was both. Enjoy!
Your Comment

My Pluralsight Courses

K.Scott Allen OdeToCode by K. Scott Allen
What JavaScript Developers Should Know About ECMAScript 2015
The Podcast!