【问题标题】:How to use AMAZON_COGNITO_USER_POOLS with apollo-client如何将 AMAZON_COGNITO_USER_POOLS 与 apollo-client 一起使用
【发布时间】:2021-01-11 06:39:58
【问题描述】:

我正在尝试用较新的 @aws-amplify 包填充我的 jwtToken,但事实证明这有点困难。

当尝试运行 Query 时,我会收到以下错误:Uncaught (in promise) No current user

我可以从源代码中看到,如果我将身份验证类型设置为AMAZON_COGNITO_USER_POOLS,那么我必须使用jwt token

        case AUTH_TYPE.AMAZON_COGNITO_USER_POOLS:
        case AUTH_TYPE.OPENID_CONNECT:
            const { jwtToken = '' } = auth;
            promise = headerBasedAuth({ header: 'Authorization', value: jwtToken }, operation, forward);

所以这导致我尝试生成我的 JWT 令牌,这就是我的知识失败的地方。我知道 jwtToken: async () => (await Auth.currentSession()).getIdToken().getJwtToken(), 按照上面代码中的要求返回了一个承诺......所以我不明白为什么这会失败?

_app.js (next.js)

import Amplify from '@aws-amplify/core'
import { Auth } from '@aws-amplify/auth'
import { ApolloProvider } from '@apollo/react-hooks'
import { ApolloLink } from 'apollo-link'
import { createAuthLink } from 'aws-appsync-auth-link'
import { InMemoryCache, ApolloClient } from '@apollo/client'
import { createHttpLink } from 'apollo-link-http'

import awsExports from '../aws-exports'

Amplify.configure(awsExports)
Auth.configure(awsExports)

const url = awsExports.aws_appsync_graphqlEndpoint
const region = awsExports.aws_appsync_region

const auth = {
  type: awsExports.aws_appsync_authenticationType,
  jwtToken: async () => (await Auth.currentSession()).getIdToken().getJwtToken(),
}
const link = ApolloLink.from([createAuthLink({ url, region, auth }), createHttpLink({ uri: url })])
const client = new ApolloClient({
  link,
  cache: new InMemoryCache(),
})

const MyApp = function ({ Component, pageProps, router }) {
  return (
        .....
          <ApolloProvider client={client}>
        .....
  )
}

export default MyApp

【问题讨论】:

    标签: javascript reactjs amazon-web-services next.js apollo-client


    【解决方案1】:
    jwtToken: async () => (await Auth.currentSession()).getIdToken().getJwtToken()
    

    这是一个部分正确的实现,当用户已经登录时可以正常工作。

    当用户尚未登录时,Auth.currentSession() 失败并显示 Uncaught (in promise) No current user。

    以下将显示操作中的错误

     Amplify.configure(awsExports);
    
     Auth.signOut()
        .then(_ => auth.jwtToken())
        .then(console.log)
        .catch(console.error);
    

    以下是获取令牌的示例(替换代码中的用户名和密码)

    Amplify.configure(awsExports);
    
    Auth.signIn(<username>, <password>)
        .then(_ => auth.jwtToken())
        .then(console.log)
        .catch(console.error);
    

    此问题的解决方案是确保您在用户登录时请求令牌或确保优雅地处理错误。

    更新:

    如果有任何公共查询,我会说在 AppSync GraphQL 端点的 Cognito 身份验证之外添加基于 api-key 的身份验证(AppSync 设置中的Additional authorization providers)。在下面的示例中,id 和 publicProperty 可以通过 somePublicQuery 使用配置的 API 密钥访问

    type Query {
      somePublicQuery:[SomeModel!]!
      @aws_api_key
    }
    
    type SomeModel {
      id: ID! @aws_api_key
      privateProperty: String!
      publicProperty: String! @aws_api_key
    }
    

    如果我参考问题中指出的示例,那么这就是客户端会发生的变化。

    headerBasedAuth 可以采用一组标头,一个用于 api-key,一个用于 Cognito 令牌。

    const headerBasedAuth = async ( authHeaders: Array<Headers> = [], operation, forward) => {
      const origContext = operation.getContext();
      let headers = {
        ...origContext.headers,
        [USER_AGENT_HEADER]: USER_AGENT,
      };
    
      for ( let authHeader of authHeaders) { // Handle the array of auth headers
        let { header, value } = authHeader;
        if (header && value) {
          const headerValue = typeof value === 'function' ? await value.call(undefined) : await value;
    
          headers = {
              ...{ [header]: headerValue },
              ...headers
          };
        }
      }
    
      operation.setContext({
          ...origContext,
          headers,
      });
    
      return forward(operation);
    
    };
    

    在authLink 函数中,你会使用类似这样的东西,而不是 switch 语句

    const { apiKey = '', jwtToken = '' } = auth;
    promise = headerBasedAuth([{ header: 'X-Api-Key', value: apiKey }, { header: 'Authorization', value: jwtToken }], operation, forward);
    

    最后auth 对象看起来像这样

    const auth = {
      apiKey: awsExports.aws_appsync_apiKey, //Add apiKey to your aws-exports
      jwtToken: async () => {
          try {
            return (await Auth.currentSession()).getIdToken().getJwtToken()
          } catch (e) {
            console.error(e);
            return ""; // In case you don't get the token, hopefully that is a public api and that should work with the API Key alone.
          }
        }
    }
    

    【讨论】:

    • 谢谢 GSSWain,我想,对于公开的查询,我确保我的角色在我的架构中更新为 role: public?
    • 再次非常感谢@GSSWain!我很感激,并期待明天检查这个:)如果你感兴趣的话,我还有另一个关于 RDS MySQL db 和应用同步的一般性问题:D stackoverflow.com/questions/64176243/…
    • 这很有帮助,非常感谢!
    猜你喜欢
    • 2018-09-05
    • 2020-03-31
    • 2018-07-04
    • 2017-12-22
    • 1970-01-01
    • 2021-06-25
    • 2018-11-19
    • 2021-04-27
    • 1970-01-01
    相关资源
    最近更新 更多