Apollo + Next.js, authentication and CSRF protection
At my previous job, we were working on a project for a client where we wanted to try out writing a backend GraphQL server and connect to it from a Apollo and React frontend. I also read about this cool thing called universal rendering, and wanted to try out Next.js. We ran into some difficulties setting up and understanding how authentication should work with this set up, so I thought I’d write our journey of discovery down here, might it help others in a similar situation!
The specification I got for authentication from the back end developer who build the GraphQL server (in Java) was the following: A login
mutation is available, which takes a username and password, that will return a JSESSIONID
token, which is used in the back end for sessions. On the /graphql
endpoint though — where the mutation is being sent to — a valid CSRF token should be sent along with every request.
We had a small discussion about if the CSRF token was actually needed, and decided it would be, because of the project’s nature (should be secure!) and because Spring Boot’s documentation specified it.
I started writing my front end code, utilising Next.js’ server side rendering, and thinking about how getting the data from the GraphQL server should work on the server side. I found this article very helpful, and the example that comes with it. I also found that Next.js has a great number of well written examples on his Github page. Most if not all of what I ended up using was taken straight from these examples, from the with-apollo-auth example in particular. I just tweaked it a little bit.
In these examples, a HOC called withData
is used to create components with data coming from the GraphQL server. The HOC determines if the component passed in is being rendered server side or client side, and sets up an Apollo client via the initApollo
method accordingly. It passes cookies along to this function, for the purpose of passing along authentication tokens. Perfect! The cookies are gotten from a parseCookies
method, which again checks if we’re on the server or client (if context.req
and context.req.headers
are set, we have a request object coming from a browser, so we’re on the server. If we would be on the client side, these objects would not exist.)
In the initApollo
function, we use create
to create an Apollo client. Here, we’re creating an httpLink
object, that specifies what Apollo will use for every request to the GraphQL endpoint. The documentation is here. We create a link, pass in our uri
— the URL of our /graphQL
endpoint — and set credentials
to include
. This last bit will specify that Apollo will include the headers it gets in the response from the server. In our case, this was the JSESSIONID
token. We also create an authLink
object that will hold the header data, and here we can specify extra stuff like an X-XSRF-TOKEN
header, which Spring Boot will pick up as a CSRF token (in the Next.js example they set a Bearer token here, which is also a good idea). This token is gotten from the getToken()
method which was passed in by our withData
HOC.
In our case, we had a /csrf
endpoint, that when receiving a GET
request, would return a Set-Cookie
header, which would set the XSRF-TOKEN
as a cookie. Whether called from the client or server, these cookies would be set and gotten from the parseCookies
method in withData
, and be passed on to the initApollo
function that could then set these values as headers in a Apollo Link object. My create
method in the end looked like this:
function create(initialState, { getToken }) {
const httpLink = createHttpLink({
uri: gql_url + "/graphql",
credentials: "include"
});const authLink = setContext((_, { headers }) => {
const token = getToken()["XSRF-TOKEN"];
return {
headers: {
...headers,
"X-XSRF-TOKEN": token
}
};
});return new ApolloClient({
connectToDevTools: process.browser,
ssrMode: !process.browser, // Disables forceFetch on the server (so queries are only run once)
link: authLink.concat(httpLink),
cache: new InMemoryCache().restore(initialState || {})
});
}
At first, these examples can be pretty confusing, at least they were to me. But once you learn more about how it works, you’ll see that Apollo handles a lot of stuff for you, and once you have your Link object set up correctly, it will all work very smoothly!