7

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

我对阿波罗的使用看起来不对吗?

这是一个带有 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));  

导轨日志:

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)
4

7 回答 7

8

自 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()
});
于 2018-01-12T10:47:19.827 回答
4

使用 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 }
    })
  },
})
于 2018-04-30T14:23:52.633 回答
2

这个对我来说很好。

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,
});
于 2018-06-26T14:45:09.797 回答
1

前几天我在用 Apollo on Rails 进行试验,这是对我有用的配置:

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。)

于 2017-03-10T17:46:38.187 回答
1

为了澄清上述一些答案,因为其中一个用户注意到将 uri 作为第一个参数传递给 createNetworkInterface 有效,但在控制台中记录了一条弃用消息:您必须在作为选项对象的属性,如下所示:

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

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

于 2017-07-06T06:22:40.603 回答
0

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

有两件大事可能会发生。

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.) 防止伪造会话在第 3 方身份验证策略上失败(即设计 + CanCan 或 w/e)

如果您的 apollo 中间件模式是 kosher 的,那么您的身份验证策略可能会阻碍请求。

例如,我正在使用 devise + cancan,这也导致了一些看似随机的 422,通过一些研究很容易解决(参见这篇文章https://blog.bigbinary.com/2016/04/06/rails-5-default-防止伪造-prepend-false.html )

在这部分长话短说,您可能需要让应用程序控制器(特别是如果您从旧版本升级)加载并授权您当前的用户/资源在防止伪造设置之后(参考文章的更多细节)

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

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

希望这可以帮助。

干杯

于 2019-12-22T15:59:50.800 回答
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

我错过了opts: { credentials: 'same-origin' },导致同样的错误:无法验证 CSRF 令牌的真实性。已完成 422 无法处理的实体

于 2017-03-21T05:56:36.660 回答