graphql + apollo-server-express,如何处理 express authMiddleware 中的身份验证错误?
Posted
技术标签:
【中文标题】graphql + apollo-server-express,如何处理 express authMiddleware 中的身份验证错误?【英文标题】:graphql + apollo-server-express, how to handle auth error in express authMiddleware? 【发布时间】:2019-01-09 16:58:12 【问题描述】:我无法弄清楚如何在我的 authMiddleware 函数中处理身份验证错误。
这是我的authMiddleware
函数,带有传统的快速错误处理方式。
const jwt = require('jsonwebtoken');
const appConfig = require('../config');
function authMiddleware(req, res, next)
let token;
const parts = req.headers.authorization.split(' ');
if (parts.length === 2)
const schema = parts[0];
const credentials = parts[1];
if (/^Bearer$/i.test(schema))
token = credentials;
else
// throw new Error();
next(new Error('credentials_bad_scheme: Format is Authorization: Bearer [token]'));
try
const user = jwt.verify(token, appConfig.JWT_SCERET);
req.user = user;
catch (error)
// console.log(error);
next(error);
next();
exports.authMiddleware = authMiddleware;
但是使用apollo-server-express
和graphql
系统。传递给next
函数的error
不能正常工作。这意味着当使用graphql
工具堆栈时,express
错误处理方式似乎不再起作用。
authMiddleware
中的 error
不会通过下面的 express 错误处理中间件
app.use((err, req, res) =>
console.log('error handler: ', err);
);
如果我在身份验证失败时在catch
中使用return res.status(401).json(code: 1001, msg: 'Authorization failed')
或throw new Error('xxx')
。该请求将永远停止在这里,这意味着永远不会下降到graphqlExpressHandler
。为了让请求下降到graphqlExpressHandler
,我只能对错误做的就是使用console.log
打印它们。
并且没有办法使用express-jwt
unless
方法或credentialsRequired
属性。 因为在使用 graphql 时,只有一个名为 '/graphql' 的路由。所以,你不能,除非 /graphql route
解决这个问题的一种方法是:为auth
制作restful api 并以传统方式处理它。制作graphql
api进行数据查询。
【问题讨论】:
你想通了吗?我也面临这个问题。 您可以将问题中提出的身份验证逻辑抽象为一个单独的函数,该函数只需要一个标题。然后在您的“正常”快速路由中,您使用中间件来运行身份验证代码,但在 Apollo 中,您使用上下文创建来调用您的函数。 遇到了同样的问题,使用 Express 中间件进行身份验证,API 的其余部分在 Apollo 服务器中运行。因此,常规的 Apollo 错误处理不会捕获服务器外部的错误。 :// 【参考方案1】:迟到的答案,但可能对面临同样问题的人有所帮助。
我们是这样解决的:
graphql + apollo-server-express 仅公开 /graphql
路由,因此简单而好的方法是将身份验证端点公开为 graphql 突变,并在 @987654324 中进行令牌验证(您的 authMiddleware
所做的) @函数传递给ApolloServer
实例。
示例:
-
定义
token
突变。
// graphql.ts
import gql from 'apollo-server-express';
import AuthnHandler from './handlers/authn_handler';
export const typeDefs = gql`
type Mutation
token(username: String, password: String): String
`
const authnHandler = new AuthnHandler();
export const resolvers =
Mutation:
token: authnHandler.tokenResolver
;
-
定义验证凭据和颁发令牌的令牌变异解析器。
// handlers/authn_handler.ts
import AuthenticationError from 'apollo-server-express';
export default class AuthnHandler
public async tokenResolver(parent: any, args: any, context: any, info: any): Promise<any>
const username = args.username;
const password = args.password;
// pseudo-code here, replace with your token issuing implementation.
// if credentials are valid, return Promise.resolve(token);
// else throw new AuthenticationError('Invalid credentials.');
-
定义验证授权标头中令牌的上下文函数(您的
authMiddleware
函数所做的事情)。
// server.ts
import express from 'express';
import ApolloServer, ApolloServerExpressConfig from 'apollo-server-express';
import typeDefs, resolvers from './graphql';
import authMiddleware from './auth_middleware';
const expressApp = express();
const apolloServer = new ApolloServer(
typeDefs,
resolvers,
context: authMiddleware
as ApolloServerExpressConfig);
apolloServer.applyMiddleware( app: expressApp );
expressApp.listen(3000, () =>
console.log('server listening on port 3000');
);
您的authMiddleware
函数签名会根据context
函数要求更改,在这种情况下,它会在成功时返回请求对象本身。
// auth_middleware.ts
const jwt = require('jsonwebtoken');
const appConfig = require('../config');
function authMiddleware( req )
let token;
const parts = req.headers.authorization.split(' ');
if (parts.length === 2)
const schema = parts[0];
const credentials = parts[1];
if (/^Bearer$/i.test(schema))
token = credentials;
else
throw new Error();
try
const user = jwt.verify(token, appConfig.JWT_SCERET);
req.user = user;
catch (error)
throw new Error();
return req ;
exports.authMiddleware = authMiddleware;
apollo-server
文档中的authentication section 提供了这种实现方式的详细说明。
【讨论】:
以上是关于graphql + apollo-server-express,如何处理 express authMiddleware 中的身份验证错误?的主要内容,如果未能解决你的问题,请参考以下文章
GraphQl 和 insomnia 桌面客户端无法使用 graphql.org/swapi-graphql
使用 Express-GraphQL 的 GraphQL 订阅
Graphql 突变查询不适用于 express-graphql
[GraphQL] Query GraphQL Interface Types in GraphQL Playground