使用Rails,Graphql,Apollo客户端的无效令牌

时间:2017-03-10 17:08:59

标签: ruby-on-rails graphql authenticity-token apollo-client

我正在尝试使用基本的Rails,Graphql,Apollo-Client设置,但在导轨端遇到422错误“无效的身份验证令牌”时出现问题。

我对apollo的使用看起来不对吗?

这是一个带有graphql gem和apollo客户端的Rails 5应用程序。

const csrfToken = document.getElementsByName('csrf-token')[0].content
const client = new ApolloClient({
  networkInterface: createNetworkInterface({
    uri: '/graphql',
    credentials: 'same-origin',
    headers: {
      'X-CSRF-Token': csrfToken
    }
  }),
});
client.query({
query: gql`
    query Camillo2 {
      user {
        id
        email
        created_at
      }
    }
  `,
})
.then(data => console.log(data))
.catch(error => console.error(error));  

Rails日志:

Started POST "/graphql" for ::1 at 2017-03-10 08:51:58 -0800
Processing by GraphqlController#create as */*
Parameters: {"query"=>"query Camillo2 {\n  user {\n    id\n    email\n    created_at\n    __typename\n  }\n}\n", "operationName"=>"Camillo2", "graphql"=>{"query"=>"query Camillo2 {\n  user {\n    id\n    email\n    created_at\n    __typename\n  }\n}\n", "operationName"=>"Camillo2"}}
Can't verify CSRF token authenticity.
Completed 422 Unprocessable Entity in 2ms (ActiveRecord: 0.0ms)

7 个答案:

答案 0 :(得分:7)

自Apollo 2.0以来根据

https://github.com/apollographql/apollo-client/blob/master/Upgrade.md

就这样做

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()
});

答案 1 :(得分:1)

前几天我在Rails上试验过Apollo,这是适合我的配置:

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 } }`})

但是,看起来你的工作也一样好!

您能否确认正确的令牌被发送到服务器?例如,如果您打开Chrome devtools的网络标签,然后进行查询,您是否可以在“请求标题”部分中看到X-CSRF-Token

此外,Rails可能正在更新DOM中的CSRF令牌,但NetworkInterface会保留旧的陈旧CSRF令牌。您能确认“请求标头”部分中的令牌是否与<meta>标记中的当前值相匹配? (我很惊讶我自己没有遇到过这个问题,使用Turbolinks Classic。)

答案 2 :(得分:1)

为了澄清上面的一些答案,因为其中一个用户注意到将uri作为createNetworkInterface的第一个参数传递但在控制台中记录了一个弃用消息:你必须传递&#34;凭证等选项#34;和#34;标题&#34;作为选项对象的属性,如:

const networkInterface = createNetworkInterface({
  uri: '/graphql',
  opts: {
    credentials: 'same-origin',
    headers: {
      'X-CSRF-TOKEN': $('meta[name=csrf-token]').attr('content'),
    },
  },
});

请参阅文档中的抓取下的第二个示例:http://dev.apollodata.com/core/network.html

答案 3 :(得分:1)

使用apollo-boost,它有点不同。

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 }
    })
  },
})

答案 4 :(得分:0)

这对我有用(我使用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

我错过了选择:{凭据:&#39;同源&#39; } ,导致相同的错误:无法验证CSRF令牌的真实性。 完成了422个不可处理的实体

答案 5 :(得分:0)

这个对我来说很好。

const csrfToken = document.querySelector('meta[name=csrf-token]').getAttribute('content');
const link = createHttpLink({
  uri: '/graphql',
  credentials: 'same-origin',
  headers: {
    'X-CSRF-Token': csrfToken
  }
});

const client = new ApolloClient({
  cache: new InMemoryCache(),
  link,
});

答案 6 :(得分:0)

在触发Apollo查询客户端时,我自己在/graphql端点上遇到422错误(在我的情况下为响应)。

可能会发生2件主要事情。

1。)制作三重,四重,五重...确保您没有打错任何字。 Apollo客户端配置可能会变得非常简洁,因此请谨慎使用您编写的令牌设置代码。

// 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。)防止伪造会话在第三方身份验证策略(例如Devise + CanCan或w / e)上失败

如果您的阿波罗中间件模式是犹太洁食,则可能是您的身份验证策略可能阻止了该请求。

例如,我使用的是devise + cancan,这也导致了一些看似随机的422s,通过一些研究很容易解决(请参见本文https://blog.bigbinary.com/2016/04/06/rails-5-default-protect-from-forgery-prepend-false.html

长话短说,在这一部分上,您可能需要安装应用程序控制器(尤其是从旧版本升级),并在防止伪造设置之后授权当前的用户/资源(有关参考文献的更多信息,请参阅参考资料)

class ApplicationController < ActionController::Base
protect_from_forgery prepend: true, with: :exception

... 
before_action .. whatever else you have going on hereafter

希望这会有所帮助。

欢呼