# graphql-directive-auth [![Version][version-badge]][package] [![downloads][downloads-badge]][npmtrends] [![PRs Welcome][prs-badge]][prs] [![MIT License][license-badge]][build] # Introduction The `graphql-directive-auth` was created to help with common authentication tasks that is faced in almost every API. # Table of Contents - [graphql-directive-auth](#graphql-directive-auth) - [Introduction](#introduction) - [Table of Contents](#table-of-contents) - [Installation](#installation) - [Usage](#usage) - [Default](#default) - [What `default` means, and what I **need to do**?](#what-default-means-and-what-i-need-to-do) - [Example:](#example) - [Custom behaviour of authentication functions](#custom-behaviour-of-authentication-functions) - [Custom check role function](#custom-check-role-function) - [How to create your own function](#how-to-create-your-own-function) - [Directive Parameters](#directive-parameters) - [Contributing](#contributing) - [LICENSE](#license) # 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**? - `@isAuthenticated` - Just after you set environment variables, you need to have a valid JWT token and send it by `Authorization` in the HTTP headers. That's all, the directive will check your token and throw an error if the token is invalid or expired. - `@hasRole` - Checks roles of an authenticated user. To use it correctly, inside your JWT token you should have the `role` property with the correct role. If the user role doesn't match with the provided role, then directive will throw an error. > `@hasRole` before checking role is doing authentication to get roles from JWT token. ### Example: ```js 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: ```js 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: ```js 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: ```js 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: ```js 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 - Function accepts two parameters, one is the context and the second is the value from the directive - To reject an access to the particular field, you need to throw an Error that will be caught by the directive and returned if required. - Function doesn't need to return anything special # Directive Parameters - '@isAuthenticated' - checks if user is authenticated - '@hasRole(role: "user, admin")' - checks if user is authenticated and has the specified roles > if you use [`graphql-import`](https://github.com/prismagraphql/graphql-import) then you need to add this definition on top of the schema: ```graphql 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> [npm]: https://www.npmjs.com/ [node]: https://nodejs.org [build-badge]: https://img.shields.io/travis/graphql-community/graphql-directive-auth.svg?style=flat-square [build]: https://travis-ci.org/graphql-community/graphql-directive-auth [coverage-badge]: https://img.shields.io/codecov/c/github/graphql-community/graphql-directive-auth.svg?style=flat-square [coverage]: https://codecov.io/github/graphql-community/graphql-directive-auth [version-badge]: https://img.shields.io/npm/v/graphql-directive-auth.svg?style=flat-square [package]: https://www.npmjs.com/package/graphql-directive-auth [downloads-badge]: https://img.shields.io/npm/dm/graphql-directive-auth.svg?style=flat-square [npmtrends]: http://www.npmtrends.com/graphql-directive-auth [license-badge]: https://img.shields.io/npm/l/graphql-directive-auth.svg?style=flat-square [license]: https://github.com/graphql-community/graphql-directive-auth/blob/master/LICENSE [prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square [prs]: http://makeapullrequest.com [donate-badge]: https://img.shields.io/badge/$-support-green.svg?style=flat-square [coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square [coc]: https://github.com/graphql-community/graphql-directive-auth/blob/master/CODE_OF_CONDUCT.md