如何在阿波罗客户端上使用AMAZON_COGNITO_USER_POOLS

时间:2020-09-24 22:52:01

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

我正在尝试用较新的@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

1 个答案:

答案 0 :(得分: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端点(AppSync设置中的Additional authorization providers)的Cognito身份验证之外,还要添加基于api-key的身份验证。在下面的示例中,idpublicProperty可以使用已配置的API密钥通过somePublicQuery进行访问

type Query {
  somePublicQuery:[SomeModel!]!
  @aws_api_key
}

type SomeModel {
  id: ID! @aws_api_key
  privateProperty: String!
  publicProperty: String! @aws_api_key
}

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

headerBasedAuth可以使用一组标头数组,一个标头用于api键,一个标头用于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.
      }
    }
}