Mercurius Auth Plugin

6.0.0 · active · verified Wed Apr 22

Mercurius Auth is a Fastify plugin designed to add configurable authentication and authorization support to GraphQL APIs built with Mercurius. It is currently at stable version 6.0.0, with major updates often aligning with new Fastify or Mercurius versions. The plugin allows defining auth directives directly within the GraphQL schema to apply custom policies against protected fields, supporting both normal and gateway modes. Alternatively, it can operate in an 'External Policy' mode, offering programmatic control over authorization. Key differentiators include its tight integration with the Fastify and Mercurius ecosystems, its ability to build an auth context, and its GraphQL spec compliance, including features like schema filtering and replacement. Development appears active, with regular updates and dependency bumps.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates `mercurius-auth` in 'Directive mode' using Fastify and Mercurius. It defines a custom `@auth` directive with role-based authorization, extracting the user role from request headers in `authContext` and enforcing policies in `applyPolicy`.

import Fastify from 'fastify';
import mercurius from 'mercurius';
import mercuriusAuth from 'mercurius-auth';

const app = Fastify();

const schema = `
  directive @auth(
    requires: Role = ADMIN,
  ) on OBJECT | FIELD_DEFINITION

  enum Role {
    ADMIN
    REVIEWER
    USER
    UNKNOWN
  }

  type Query {
    add(x: Int, y: Int): Int @auth(requires: USER)
    adminData: String @auth(requires: ADMIN)
  }
`;

const resolvers = {
  Query: {
    add: async (_, { x, y }) => x + y,
    adminData: async () => 'Secret admin information'
  }
};

app.register(mercurius, {
  schema,
  resolvers
});

app.register(mercuriusAuth, {
  authContext (context) {
    // Simulate loading user identity from request headers
    const identity = context.reply.request.headers['x-user'] || 'UNKNOWN';
    return {
      identity: identity.toUpperCase() // Ensure consistency
    };
  },
  async applyPolicy (authDirectiveAST, parent, args, context, info) {
    const requiredRole = authDirectiveAST.requires;
    const userRole = context.auth.identity; // Assuming identity is the role for simplicity

    if (requiredRole === 'ADMIN' && userRole !== 'ADMIN') {
      return false;
    }
    if (requiredRole === 'USER' && (userRole !== 'ADMIN' && userRole !== 'USER')) {
        return false;
    }
    return true; // Policy passes
  },
  authDirective: 'auth'
});

app.listen({ port: 3000 }, (err) => {
  if (err) {
    app.log.error(err);
    process.exit(1);
  }
  app.log.info(`Server listening on port 3000`);
});

// Example usage (e.g., with curl):
// curl -H "x-user: user" http://localhost:3000/graphql -X POST -H "Content-Type: application/json" -d '{"query":"query { add(x: 5, y: 3) }"}'
// curl -H "x-user: admin" http://localhost:3000/graphql -X POST -H "Content-Type: application/json" -d '{"query":"query { adminData }"}'
// curl -H "x-user: user" http://localhost:3000/graphql -X POST -H "Content-Type: application/json" -d '{"query":"query { adminData }"}' // Should fail

view raw JSON →