如何通过Apollo Client将AWS Amplify(Cognito)异步获取的JWT访问令牌发送至Apollo服务器进行后端验证
如何用Apollo Client将AWS Amplify(Cognito)的JWT令牌发送到Apollo服务器做后端验证?
我之前刚好遇到这个问题,折腾了一阵终于搞定了,现在把完整的解决方案分享给大家,希望能帮到有同样需求的开发者~
核心思路
我们需要让Apollo Client在每次发起GraphQL请求时,异步从Amplify(Cognito)获取JWT访问令牌,然后将令牌添加到请求头中,传给后端Apollo服务器完成合法性验证。
具体实现步骤
1. 确保Amplify Auth初始化完成
首先要保证你已经正确配置并初始化了Amplify Auth,这是获取令牌的基础:
import Amplify from 'aws-amplify'; import awsconfig from './aws-exports'; // 初始化Amplify,使用你的AWS配置文件 Amplify.configure(awsconfig);
2. 自定义Apollo Link处理令牌与请求头
Apollo Client的setContext支持异步函数,我们可以利用这一点来动态获取令牌并添加到请求头:
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client'; import { setContext } from '@apollo/client/link/context'; import { Auth } from 'aws-amplify'; // 创建基础HTTP链接,指向你的Apollo服务器地址 const httpLink = createHttpLink({ uri: 'https://your-apollo-server-url.com/graphql', }); // 创建认证链接,负责获取令牌并设置请求头 const authLink = setContext(async (_, { headers }) => { try { // 从Amplify异步获取当前用户的会话信息 const session = await Auth.currentSession(); // 提取JWT访问令牌 const accessToken = session.getAccessToken().getJwtToken(); // 返回包含令牌的请求头,格式遵循Bearer认证规范 return { headers: { ...headers, authorization: accessToken ? `Bearer ${accessToken}` : '', }, }; } catch (error) { // 如果获取令牌失败(比如用户未登录),返回原始请求头 return { headers }; } }); // 组合认证链接与HTTP链接,初始化Apollo Client const client = new ApolloClient({ link: authLink.concat(httpLink), cache: new InMemoryCache(), });
3. 后端Apollo Server验证令牌
后端需要验证令牌的合法性,这里以Node.js为例,使用jsonwebtoken和jwks-rsa库来验证Cognito签发的令牌:
const jwt = require('jsonwebtoken'); const jwksClient = require('jwks-rsa'); const { ApolloServer } = require('apollo-server'); // 配置Cognito的JWKS地址,用于获取公钥 const jwksClientInstance = jwksClient({ jwksUri: 'https://cognito-idp.<你的AWS区域>.amazonaws.com/<你的用户池ID>/.well-known/jwks.json', }); // 获取Cognito的公钥用于验证签名 function getSigningKey(header, callback) { jwksClientInstance.getSigningKey(header.kid, (err, key) => { const signingKey = key.getPublicKey(); callback(null, signingKey); }); } // 初始化Apollo Server,在context中验证令牌 const server = new ApolloServer({ typeDefs: /* 你的GraphQL类型定义 */, resolvers: /* 你的解析器 */, context: async ({ req }) => { const authHeader = req.headers.authorization; if (authHeader) { const token = authHeader.split(' ')[1]; // 验证令牌合法性 return new Promise((resolve, reject) => { jwt.verify(token, getSigningKey, { audience: '<你的Cognito客户端ID>', issuer: `https://cognito-idp.<你的AWS区域>.amazonaws.com/<你的用户池ID>`, }, (err, decodedToken) => { if (err) { reject(new Error('Invalid token')); } else { // 把解码后的用户信息传到解析器中 resolve({ user: decodedToken }); } }); }); } return {}; }, }); server.listen().then(({ url }) => { console.log(`🚀 Server ready at ${url}`); });
关于未选用AppSync的说明
截至2021年9月11日,AppSync对GraphQL规范的实现程度无法满足我的业务需求,因此最终选择了自行搭建Apollo服务器配合Amplify Auth的方案。
内容的提问来源于stack exchange,提问作者Sigex
相关产品推荐
相关产品推荐

