/graphql-auth-directive

GraphQL @auth directive that protects resources from unauthenticated and unauthorized access

Primary LanguageTypeScriptMIT LicenseMIT

graphql-auth-directive

ci codeql codecov Snyk Codacy semantic-release: angular FOSSA

GraphQL @auth directive that protects resources from unauthenticated and unauthorized access.

Inspired by TypeGraphQL and GraphQL Tools.

⚠️ Under development

👋 Any help is appreciated

Installation

npm install graphql-auth-directive
# or
yarn add graphql-auth-directive

Usage

Note: See examples directory

  1. Add @auth directive to GraphQL schema:

    Note: on OBJECT | FIELD | FIELD_DEFINITION

    type Example @auth {
     unprotected: String!
     adminProtected: String! @auth(roles: [ADMIN])
    }
    
    type Query {
     unprotected: String!
     protected: String! @auth
     adminRoleProtected: String! @auth(roles: [ADMIN])
     adminOrModeratorRolesProtected: String! @auth(roles: [ADMIN, MODERATOR])
     viewPermissionProtected: String! @auth(permissions: [VIEW])
     viewOrEditPermissionsProtected: String! @auth(permissions: [VIEW, EDIT])
     roleAndPermissionProtected: String! @auth(roles: [ADMIN], permissions: [VIEW])
     # ...
    }
    
    type Mutation {
     # Same as Query
    }
    
    type Subscription {
     # Same as Query
    }
  2. Build auth directive and create GraphQL schema:

    import { buildAuthDirective, defaultAuthFn } from 'graphql-auth-directive';
    import { makeExecutableSchema } from '@graphql-tools/schema';
    import { ApolloServer } from 'apollo-server';
    import { typeDefs } from './typeDefs';
    import { resolvers } from './resolvers';
    
    // Build auth directive
    const authDirective = buildAuthDirective({
      auth: defaultAuthFn // Auth procedure (default or custom)
      // ... More options
    });
    
    // Build schema
    let schema = makeExecutableSchema({
      typeDefs: [authDirective.typeDefs, typeDefs], // TypeDefs
      resolvers // Resolvers
    });
    schema = authDirective.transformer(schema); // Transform schema
    
    // Build and start server
    const server = new ApolloServer({
      schema,
      context: () => {
        /* ... */
      }
    });
    server
      .listen()
      .then((serverInfo) => console.info(`Server started at ${serverInfo.url}`));

    Warning: defaultAuthFn requires a context of the following type:

    {
      user?: {
        roles: string[] | number[];
        permissions: string[] | number[];
      };
      /* ... */
    }

    If defaultAuthFn does not match your configuration or you want more control, you can fully customize the auth procedure. See Custom Auth procedure for more information.

Options

Name Type Default Value Description
name string auth Directive name.
auth Auth<TContext, TRole, TPermission> Auth function (AuthFn<TContext, TRole, TPermission>) or class (AuthFnClass<TContext, TRole, TPermission>).
authMode 'ERROR' | 'NULL' ERROR Auth mode if access is not granted. ERROR throws an error. NULL returns null.
roles { enumName?: string, default?: TRole | TRole[] } { enumName: undefined, default: undefined } Roles configuration. enumName is the enum name for roles array type, default is String. default is the default value, default to [].
permissions { enumName?: string, default?: TPermission | TPermission[] } { enumName: undefined, default: undefined } Permissions configuration. enumName is the enum name for permissions array type, default is String. default is the default value, default to [].
authenticationError ClassTypeEmptyConstructor<Error> AuthenticationError Authentication error class. An error class must extends Error.
authorizationError ClassTypeEmptyConstructor<Error> AuthorizationError Authorization error class. An error class must extends Error.
container ContainerType IOCContainer Dependency injection container.

Custom Auth procedure

Warning: auth must return boolean or Promise<boolean>, where true indicates that access has been granted and false that is denied

You can fully customize the auth procedure by providing a function or class when building the directive.

import { buildAuthDirective } from 'graphql-auth-directive';
import { myAuth } from './myAuth';

// Build auth directive
const authDirective = buildAuthDirective({
  auth: myAuth // Custom auth procedure
});

AuthFn function

import type { AuthFn } from 'graphql-auth-directive';
import type { Context } from './Context'; // Your context type
import type { Roles, Permissions } from './User'; // Your roles and permissions enum

export const myAuthFn: AuthFn<Context, Roles, Permissions> = (
  { context: { user } }, // Context
  { roles, permissions } // @auth(roles: [...], permissions: [...])
) : boolean | Promise<boolean> => {
  /* ... */
};

Note: Class based auth can leverage Dependency Injection (DI) mechanism. To enable DI mechanism register your OCI Container when building the directive: buildAuthDirective({ ..., container: MyContainer });

import type { AuthFnClass } from 'graphql-auth-directive';
import type { Context } from './Context'; // Your context type
import type { Roles, Permissions } from './User'; // Your roles and permissions enum

export class MyAuthFnClass implements AuthFnClass<Context, UserRoles, Permissions> {
  public auth(
    { context: { user } }, // Context
    { roles, permissions } // @auth(roles: [...], permissions: [...])
  ): boolean | Promise<boolean> {
    /* ... */
  }
}

Integrations

See typegraphql example for more information.

Why another GraphQL Auth Directive?

Similar libraries are unmaintained and use an old (and deprecated) version of graphql-tools. Moreover this library tries to be as most modular as possible, giving the user the ability to configure the directive as much as possible.

Similar libraries:

  1. graphql-auth-user-directives

  2. graphql-directive-auth

  3. graphql-auth-directives

  4. gql-auth-directives

Contributing

I would love to see your contribution ❤️

See CONTRIBUTING guidelines.

License

This project is licensed under the MIT License.
See LICENSE file for details.

FOSSA