Apollo Server - применять аутентификацию только для определенных резолверов с помощью Passport-JWT - PullRequest
0 голосов
/ 01 февраля 2019

У меня в настоящее время есть серверная часть Node.js, на которой выполняется Express с Passport.js для аутентификации, и я пытаюсь перейти на GraphQL с Apollo Server.Моя цель состоит в том, чтобы реализовать ту же аутентификацию, которую я использую в настоящее время, но не могу понять, как оставить некоторые распознаватели открытыми, в то же время разрешив авторизацию для других.(Я много пытался исследовать этот вопрос, но пока не смог найти подходящего решения.)

Вот мой код в его нынешнем виде:

Моя стратегия JWT:

const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = JWT_SECRET;

module.exports = passport => {
  passport.use(
    new JwtStrategy(opts, async (payload, done) => {
      try {
        const user = await UserModel.findById(payload.sub);
        if (!user) {
          return done(null, false, { message: "User does not exist!" });
        }
        done(null, user);
      } catch (error) {
        done(err, false);
      }
    })
  );
}

Моя конфигурация server.js и Apollo: (В настоящее время я извлекаю токен-носитель из заголовков HTTP и передаю его своим распознавателям, используяобъект контекста):

const apollo = new ApolloServer({
  typeDefs,
  resolvers,
  context: async ({ req }) => {
    let authToken = "";

    try {
      if (req.headers.authorization) {
        authToken = req.headers.authorization.split(" ")[1];
      }
    } catch (e) {
      console.error("Could not fetch user info", e);
    }

    return {
      authToken
    };
  }
});

apollo.applyMiddleware({ app });

И, наконец, мои средства распознавания:

exports.resolvers = {
  Query: {
    hello() {
      return "Hello world!";
    },
    async getUserInfo(root, args, context) {
      try {
        const { id } = args;
        let user = await UserModel.findById(id);
        return user;
      } catch (error) {
        return "null";
      }
    },
    async events() {
      try {
        const eventsList = await EventModel.find({});
        return eventsList;
      } catch (e) {
        return [];
      }
    }
  }
};

Моя цель - оставить определенные запросынапример, первый («привет») публичный, в то время как другие ограничиваются запросами только с действительными токенами на предъявителя. Однако я не уверен, как реализовать это разрешение в средствах разрешения с использованием Passport.js и Passport-В частности, JWT (обычно это делается путем добавления промежуточного программного обеспечения к определенным конечным точкам, однако, поскольку в этом примере у меня будет только одна конечная точка (/ graphql) , эта опция ограничит все запросы аутентифицированным использованием).только то, что я не ищу.Я должен как-то выполнить авторизацию в резольверах, но не уверен, как это сделать с помощью инструментов, доступных в Passport.js.)

Любой совет очень важен!

1 Ответ

0 голосов
/ 01 февраля 2019

Я бы создал директиву схемы для авторизованного запроса по определению поля, а затем использовал бы эту директиву везде, где я хочу применить авторизацию.Пример кода:

class authDirective extends SchemaDirectiveVisitor {
    visitObject(type) {
        this.ensureFieldsWrapped(type);
        type._requiredAuthRole = this.args.requires;
    }

    visitFieldDefinition(field, details) {
        this.ensureFieldsWrapped(details.objectType);
        field._requiredAuthRole = this.args.requires;
    }

    ensureFieldsWrapped(objectType) {
        // Mark the GraphQLObjectType object to avoid re-wrapping:
        if (objectType._authFieldsWrapped) return;
        objectType._authFieldsWrapped = true;

        const fields = objectType.getFields();

        Object.keys(fields).forEach(fieldName => {
            const field = fields[fieldName];
            const {
                resolve = defaultFieldResolver
            } = field;
            field.resolve = async function (...args) {
                // your authorization code 
                return resolve.apply(this, args);
            };
        });
    }

}

И объявите это в определении типа

directive @authorization(requires: String) on OBJECT | FIELD_DEFINITION

директива схемы карты в вашей схеме

....
resolvers,
schemaDirectives: {
authorization: authDirective
}

Затем используйте ее в конечной точке API.или любой объект

Query: {
    hello { ... }
    getuserInfo():Result @authorization(requires:authToken) {...} 
    events():EventResult @authorization(requires:authToken) {...} 
  };
...