graphql + apollo-server-express,如何处理快递authMiddleware中的auth错误?

sli*_*wp2 5 graphql apollo-server

我无法弄清楚如何在authMiddleware函数中处理auth错误。

这是我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;
Run Code Online (Sandbox Code Playgroud)

但是用apollo-server-expressgraphql系统。在error传递到next功能不做工精细。这意味着express使用graphql工具堆栈时,错误处理方式似乎不再起作用。

errorauthMiddleware不会通过以下快递错误处理中间件

app.use((err, req, res) => {
  console.log('error handler: ', err);
});
Run Code Online (Sandbox Code Playgroud)

如果我使用return res.status(401).json({code: 1001, msg: 'Authorization failed'})或身份验证失败时throw new Error('xxx')进入catch。该请求将永远在此处停止,这意味着永远不会停止graphqlExpressHandler。为了让请求下降到graphqlExpressHandler,我只能为错误做的事情是使用console.log打印它们。

而且没有办法使用express-jwt unless方法或credentialsRequired属性。因为当使用graphql时,只有一个名为'/ graphql'的路由。因此,除非/ graphql route

解决此问题的一种方法是:auth以传统方式处理静态api 并对其进行处理。使graphqlAPI进行数据查询。

Ant*_*ony 0

迟到的回答,但可能会帮助面临同样问题的人。

我们是这样解决的:

graphql + apollo-server-express 仅公开路由/graphql,因此简单而好的方法是将身份验证端点公开为 graphql 突变,并在传递给实例的函数authMiddleware中进行令牌验证(您所做的)。contextApolloServer

例子:

  1. 定义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
  }
};
Run Code Online (Sandbox Code Playgroud)
  1. 定义验证凭证并颁发令牌的令牌突变解析器。
// 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.');
  }
}
Run Code Online (Sandbox Code Playgroud)
  1. 定义验证授权标头中的令牌的上下文函数(您的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');
});
Run Code Online (Sandbox Code Playgroud)

您的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;
Run Code Online (Sandbox Code Playgroud)

文档中的身份验证部分apollo-server提供了这种实现方式的详细说明。