graphql-community / graphql-directive-auth

GraphQL directive for handling auth
MIT License
136 stars 12 forks source link
auth authentication authorization graphql graphql-directive graphql-server

graphql-directive-auth

Version downloads PRs Welcome MIT License

Introduction

The graphql-directive-auth was created to help with common authentication tasks that is faced in almost every API.

Table of Contents

Installation

yarn add graphql-directive-auth

Usage

We are able to use directives in two different way:

Default

To use the default directive behaviour, you need to set APP_SECRET environment variable, and that's all.

What default means, and what do I need to do?

@hasRole before checking role is doing authentication to get roles from JWT token.

Example:

import { AuthDirective } from 'graphql-directive-auth';
// or
const AuthDirective = require('graphql-directive-auth').AuthDirective;

// set environment variable, but in better way ;)
process.env.APP_SECRET = 'your_secret_key';

const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
  schemaDirectives: {
    // to use @hasRole and @isAuthenticated directives
    ...AuthDirective(),
    // custom name for @isAuthenticated
    auth: AuthDirective().isAuthenticated,
    // custom name for @hasRole
    role: AuthDirective().hasRole,
  },
});

Custom behaviour of authentication functions

If you need custom Authentication you can pass your authentication function to the main AuthDirective functions. Your authentication function should return an object which will be available via context.auth.

Authentication function signature:

context => {
  // your logic here

  // you should return an object
  // this object will be passed inside your resolver
  // it is available inside context via auth property
  return {
    user: {
      id: 'your_user_id',
    },
  };
};

usage:

import { AuthDirective } from 'graphql-directive-auth';
// or
const AuthDirectives = require('graphql-directive-auth').AuthDirective;

const customAuth = AuthDirectives({
  authenticateFunc: authenticateCustomFunc,
  checkRoleFunc: checkRoleCustomFunc
});

const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
  schemaDirectives: {
    // to use @hasRole and @isAuthenticated directives
    ...customAuth,
    // custom name for @isAuthenticated
    auth: customAuth().isAuthenticated,
    // custom name for @hasRole
    role: customAuth().hasRole,
  },

resolver:

export default {
  Query: {
    me() (root, args, ctx){
      const userId = ctx.auth.user.id; // your_user_id
    },
  },
};

Custom check role function

Same as with the authenticate function, you can add your own logic to checking roles. Here is an example of implementation:

import { AuthenticationError } from 'apollo-server';
import jwt from 'jsonwebtoken';
import { jwtSecret } from '../config';

export default (ctx, value) => {
  const authorization =
    ctx.request && ctx.request.headers && ctx.request.headers.authorization;

  if (!authorization) {
    throw new AuthenticationError('Unauthorized access!');
  }

  const token = authorization.replace('Bearer ', '');

  const decodedToken = jwt.verify(token, jwtSecret);

  const mandatoryRoles = value.split(',').map((s) => s.trim());

  if (decodedToken && decodedToken.user && decodedToken.user.roles) {
    const { roles } = decodedToken.user;
    const rolesIntersection = roles.filter((role) =>
      mandatoryRoles.includes(role),
    );

    if (rolesIntersection.length === 0) {
      throw new AuthenticationError('Invalid role!');
    }

    return rolesIntersection;
  }

  throw new AuthenticationError('Invalid token!');
};

How to create your own function

Directive Parameters

if you use graphql-import then you need to add this definition on top of the schema:

directive @isAuthenticated on FIELD | FIELD_DEFINITION
directive @hasRole(role: String) on FIELD | FIELD_DEFINITION

Contributing

I would love to see your contribution. ❤️

For local development (and testing), all you have to do is to run yarn and then yarn dev. This will start the Apollo server and you are ready to contribute :tada:

Run yarn test (try --watch flag) for unit tests (we are using Jest)

LICENSE

The MIT License (MIT) 2018 - Luke Czyszczonik - mailto:lukasz.czyszczonik@gmail.com