I am trying to get a basic Rails, Graphql, Apollo-Client setup working but having trouble with 422 errors \'invalid auth token\' on the rails side.
Does my use of apoll
To clarify some of the above answers, since one of the users noted that passing in the uri as a first argument to createNetworkInterface works but logs a deprecation message in the console: you have to pass in options like "credentials" and "headers" in as properties of an options object like so:
const networkInterface = createNetworkInterface({
uri: '/graphql',
opts: {
credentials: 'same-origin',
headers: {
'X-CSRF-TOKEN': $('meta[name=csrf-token]').attr('content'),
},
},
});
See the second example under Fetch in the docs: http://dev.apollodata.com/core/network.html
I was experimenting with Apollo on Rails the other day, here's the configuration that worked for me:
var RailsNetworkInterface = apollo.createNetworkInterface('/graphql', {
credentials: 'same-origin',
headers: {
'X-CSRF-Token': $("meta[name=csrf-token]").attr("content"),
}
});
var client = new apollo.ApolloClient({networkInterface: RailsNetworkInterface})
client.query({ query: gql`{ event(id: 7) { name } }`})
But, it seems like yours would work just as well!
Can you confirm that the proper token is being sent to the server? For example, if you open the network tab of Chrome devtools, then make a query, can you see the X-CSRF-Token
in the "Request Headers" section?
Also, it's possible that Rails is updating the CSRF token in the DOM, but the NetworkInterface keeps the old, stale CSRF token. Can you confirm that the token in the "Request Headers" section matches the current value in the <meta>
tag? (I was surprised that I didn't encounter this problem myself, using Turbolinks Classic.)
Since Apollo 2.0 and according to
https://github.com/apollographql/apollo-client/blob/master/Upgrade.md
Just do this way
const csrfToken = document.querySelector('meta[name=csrf-token]').getAttribute('content');
const client = new ApolloClient({
link: new HttpLink({
credentials: 'same-origin',
headers: {
'X-CSRF-Token': csrfToken
}
}),
cache: new InMemoryCache()
});
Using apollo-boost, it's a little different.
import ApolloClient from 'apollo-boost'
const client = new ApolloClient({
fetchOptions: {
credentials: 'same-origin',
},
request: (operation) => {
const csrfToken = document.querySelector('meta[name=csrf-token]').getAttribute('content')
operation.setContext({
headers: { "X-CSRF-Token": csrfToken }
})
},
})
This worked for me (I'm using react_on_rails):
import { ApolloClient, createNetworkInterface } from 'react-apollo';
import ReactOnRails from 'react-on-rails';
const networkInterface = createNetworkInterface({
uri: '/graphql',
opts: {
credentials: 'same-origin'
}
});
networkInterface.use([{
applyMiddleware(req, next) {
if (!req.options.headers) {
req.options.headers = {
"X-CSRF-Token": ReactOnRails.authenticityToken()
}
}
next();
}
}]);
const client = new ApolloClient({
networkInterface
});
export default client
I was missing opts: { credentials: 'same-origin' } , resulting in the same error: Can't verify CSRF token authenticity. Completed 422 Unprocessable Entity
Was running into a 422 error myself on the /graphql
endpoint when firing Apollo query client side (React in my case).
There are 2 major things that could be happening.
1.) Make triple, quadruple, quintuple... sure that you have not made any typos. the Apollo client config can get quite terse, so just be super prudent about the token setup code you write.
// Get Token from Meta Tags on Application.html.erb
const getToken = () => document.querySelector('meta[name="csrf-token"]').getAttribute('content');
const token = getToken();
// MiddleWare Operationt that sets the CSRF token on requests to protect from forgery
const setTokenForOperation = async operation =>
operation.setContext({
headers: {
'X-CSRF-Token': token,
},
});
// Link With CSRF Token
const createLinkWithToken = () =>
new ApolloLink(
(operation, forward) =>
new Observable(observer => {
let handle;
Promise.resolve(operation)
.then(setTokenForOperation)
.then(() => {
handle = forward(operation).subscribe({
next: observer.next.bind(observer),
error: observer.error.bind(observer),
complete: observer.complete.bind(observer),
});
})
.catch(observer.error.bind(observer));
return () => {
if (handle) handle.unsubscribe();
};
})
);
... other apollo-link middleware stuff like error logging etc
// Tell Apollo client about the endpoint for making queries: (HTTP LINK) - this was default endpoint added by graphql install
const createHttpLink = () =>
new HttpLink({
uri: '/graphql',
credentials: 'include',
});
// and finally put it all together
export const createClient = (cache, requestLink) =>
new ApolloClient({
link: ApolloLink.from([createErrorLink(), createLinkWithToken(), createHttpLink()]),
cache,
});
2.) Protect from Forgery Session fail on 3rd party authentication strategies (ie Devise + CanCan or w/e )
If your apollo middleware patterns are kosher, then it could be that your authentication strategy might be impeding the request.
for example I am using devise + cancan, which was also causing some seeming random 422s which was easily solved with some research (see this article https://blog.bigbinary.com/2016/04/06/rails-5-default-protect-from-forgery-prepend-false.html)
long story short on this part, you may need to have application controller (especially if you are upgrading from a legacy version) load and authorize your current user/resource AFTER the protect from forgery setup (more deets on the article referenced)
class ApplicationController < ActionController::Base
protect_from_forgery prepend: true, with: :exception
...
before_action .. whatever else you have going on hereafter
Hope this helps.
Cheers