{"_id":"@dbateman/keycloak-connect-graphql","_rev":"1-35af3f45f5d4437d490507b97c3cf59f","name":"@dbateman/keycloak-connect-graphql","dist-tags":{"latest":"16.9.1"},"versions":{"16.9.0":{"name":"@dbateman/keycloak-connect-graphql","version":"16.9.0","description":"Add Keycloak authentication and authorization to your GraphQL server via schema directives.","keywords":["graphql","apollo","keycloak","authentication","express"],"author":{"name":"dbateman"},"homepage":"https://github.com/dbateman/keycloak-connect-graphql","license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/dbateman/keycloak-connect-graphql.git"},"main":"dist/index.js","types":"dist/index.d.ts","publishConfig":{"access":"public"},"scripts":{"build":"tsc --build tsconfig.build.json","build:clean":"tsc --build tsconfig.build.json --clean","watch":"tsc --watch","test":"jest","coverage":"jest --coverage","lint":"tslint '*/*/src/**/*.ts' --exclude 'src/**/*.test.ts' && tslint -c tslint_tests.json 'src/**/*.test.ts'","release:prep":"./scripts/prepareRelease.sh","release:validate":"./scripts/validateRelease.sh","release:publish":"./scripts/publishRelease.sh","examples:seed":"node scripts/initKeycloak.js"},"type":"commonjs","dependencies":{"@graphql-tools/utils":"9.2.1"},"devDependencies":{"@apollo/server":"4.5.0","@types/express-session":"1.17.3","@types/graphql":"14.2.3","@types/jest":"29.5.0","@types/keycloak-connect":"4.5.4","@types/node":"18.15.3","cors":"2.8.5","graphql":"16.6.0","graphql-subscriptions":"2.0.0","jest":"29.5.0","keycloak-connect":"12.0.1","keycloak-request-token":"0.1.0","subscriptions-transport-ws":"0.9.18","ts-jest":"29.0.5","ts-node":"9.1.1","tslint":"5.20.1","typescript":"4.9.5"},"peerDependencies":{"graphql":"^14.0.0 || ^15.0.0 || ^16.0.0","keycloak-connect":">=9.0.0"},"gitHead":"e8f14c50a41fe3a36ed8fabcb9d2501dcfe09b2d","bugs":{"url":"https://github.com/dbateman/keycloak-connect-graphql/issues"},"_id":"@dbateman/keycloak-connect-graphql@16.9.0","_nodeVersion":"16.15.0","_npmVersion":"9.6.0","dist":{"integrity":"sha512-fGiHKHxaKA2Yw3zD6E5sNIqFwyPS6UxVAKI15M2V1oZhLHzo65oZO4ZIqaSz8LzhtZK2p4ucBq/xaRPGt8nSYw==","shasum":"b5052656472892b09f521b178a25c4c064a0b407","tarball":"https://registry.npmjs.org/@dbateman/keycloak-connect-graphql/-/keycloak-connect-graphql-16.9.0.tgz","fileCount":68,"unpackedSize":332452,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC+zIBwfUj+r6P2TB7kkwGc6cHE+v9EvzrdY2MFJE4fbgIhAJ9pfNXy5oKoee9zjULfIEXzwrsywip7gHnbecubO19U"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkGATpACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmrn6Q//YlW+M5PClkVnK5z0ayPZK8pBv5FN5Jrdboyf/OVWUKr2w0Xk\r\nnRJ/Eh61m+th10GlVoPDTz+2/dsFPMOn+FFyPdofiKVvkeZBCkOtouiUvlA/\r\njgm5bL3PleMi07MY6rHsdccSBV9wkSnypcnk7mRkJlqyc2ab/Ri0BKy/VgCZ\r\nDZrGvdhY7GY/NKeiRJqb7SsnEO1etpZieLIx8kIdERCtBFEIaxqM3AavDl1B\r\nfcNKQYBptvTcOjI+FX5Xdai16S4rv2BnU2EvObza+GSuFbnVAXK3zqlZItha\r\n7iNR3e5bI+1jeL0g7vgK1PsXvHVZU5sAFmys0s2YkOXYY5rqcUQ2HH23VEOa\r\nDGnyrclxeFn1EtMiMgprrWjTfgdR3rMJRqbkoq6wVHcgWUTvaNqcxjCW+/fR\r\nWJ8A3kFlhVLEIbhn52tHW69h5GH5cW2xAxqsuw41Z0nhmCq0TavyBcDaosa7\r\n2v0crZ1QziQmL/TkrV1Z5Z1ffulxk9O4fEZlTQkpmjA/3ZIcYrEh6e3bU9RW\r\nigWCmNJIr/+uRg/D/siN4hh/YRvkyBJCZpzmAM5TgZnGL61R58lZw5nTtj81\r\nsplo/J3huJeMNmI98UwbjNVwR288WgD+vimLlrevfeqRgdSzyVbnH+9gbwpD\r\nCG4j9nN7H99YkpSCNeAs9veOavoVyAQfCKM=\r\n=y40D\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"dbateman","email":"damien.bateman@gmail.com"},"directories":{},"maintainers":[{"name":"dbateman","email":"damien.bateman@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/keycloak-connect-graphql_16.9.0_1679295720784_0.17572645748418658"},"_hasShrinkwrap":false},"16.9.1":{"name":"@dbateman/keycloak-connect-graphql","version":"16.9.1","description":"Add Keycloak authentication and authorization to your GraphQL server via schema directives.","keywords":["graphql","apollo","keycloak","authentication","express"],"author":{"name":"dbateman"},"homepage":"https://github.com/dbateman/keycloak-connect-graphql","license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/dbateman/keycloak-connect-graphql.git"},"main":"dist/index.js","types":"dist/index.d.ts","publishConfig":{"access":"public"},"scripts":{"build":"tsc --build tsconfig.build.json","build:clean":"tsc --build tsconfig.build.json --clean","watch":"tsc --watch","test":"jest","coverage":"jest --coverage","lint":"tslint '*/*/src/**/*.ts' --exclude 'src/**/*.test.ts' && tslint -c tslint_tests.json 'src/**/*.test.ts'","release:prep":"./scripts/prepareRelease.sh","release:validate":"./scripts/validateRelease.sh","release:publish":"./scripts/publishRelease.sh","examples:seed":"node scripts/initKeycloak.js"},"type":"commonjs","dependencies":{"@graphql-tools/utils":"9.2.1"},"devDependencies":{"@apollo/server":"4.5.0","@types/express-session":"1.17.3","@types/graphql":"14.2.3","@types/jest":"29.5.0","@types/keycloak-connect":"4.5.4","@types/node":"18.15.3","cors":"2.8.5","graphql":"16.6.0","graphql-subscriptions":"2.0.0","jest":"29.5.0","keycloak-connect":"12.0.1","keycloak-request-token":"0.1.0","subscriptions-transport-ws":"0.9.18","ts-jest":"29.0.5","ts-node":"9.1.1","tslint":"5.20.1","typescript":"4.9.5"},"peerDependencies":{"graphql":"^14.0.0 || ^15.0.0 || ^16.0.0","keycloak-connect":">=9.0.0"},"gitHead":"d379259ab6cdb4d8d03ec0790b33f1b2384f4be3","bugs":{"url":"https://github.com/dbateman/keycloak-connect-graphql/issues"},"_id":"@dbateman/keycloak-connect-graphql@16.9.1","_nodeVersion":"16.15.0","_npmVersion":"9.6.0","dist":{"integrity":"sha512-bCpZhnqHNetgGgch3y6m2xHy2dZgPLxhDfQgZpsgTx3zQn7nuPymLGfAPJCgAsJJdFR8a4UmvAT/Pj+YaOjAhA==","shasum":"9cecadedb099a4a3a6d38bf477d464dac1a95913","tarball":"https://registry.npmjs.org/@dbateman/keycloak-connect-graphql/-/keycloak-connect-graphql-16.9.1.tgz","fileCount":39,"unpackedSize":88236,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDE9xnPALYfv0celtc+PBmDetjbbDvW0DUanuYp3ilb5AIgMcg9VwO8pxbIs49tXwMRbxeuUeRC8OKma3stRIQt5bg="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkGAn2ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqS8Q/+MF/I+aTQWzjCzx9zUUDjfSS4wPciaX82aPiIr0Y+Sub0G6hA\r\npLFjfaM6z3mj5CmBNQw7IDEAeznjNa8zEekTmrq23UwZbR2kqcvnzAMcH0Y5\r\nzQISqDjROUqQydLgLPQEwzfE615EN+c/91bIjEfxHNc0Qhk0Y7CrAjODNtiV\r\n7R4XC2XTO9t1AtWrfZEzEgjjm3icb4dIeULBYFsHwbwc7iMeJMzln/Gxhnj7\r\n74YuLjnNs4OqOc23/S9tXvL3sab6vljgAtHcvKXntMTg/d+W0sf6y7DJAzF2\r\nRm5RYkU+Sv3F4u5ByCO7pqCmdqUVYSA9eSL86FPAzuSbWQmMcNLfIvbDauJ1\r\nKOhz2ka+yrHTjQUtnt/Z0GHaFB9WhTEFySdgiQgkutaXyksFjnmATl4YllOp\r\nuDwx/hgzyFc6wcPyaAoByW5awrc23cFgVbTCJmZKsl/tM56vcjJAUZz1r8XR\r\nuXrUPQbXBoNSCMR9evu++rCxXxfXjrUZSV0S21PjTk+aKwfcqcbrAsPhGJC0\r\ndYwjykEJFOpw7xBFEc9ngRXCWlCNIFROOzIjShUqzPqyVUuL626OiebfuvPx\r\nKvwXzfuRwZtYwRNSU9WaYTz9PRyTdUzr69TYnXdLaYlGsGI0wiKp6sItOFK2\r\njic0dyUD8TAzD/iQH4g8U1t/PJsd2XS/HLg=\r\n=aqPx\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"dbateman","email":"damien.bateman@gmail.com"},"directories":{},"maintainers":[{"name":"dbateman","email":"damien.bateman@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/keycloak-connect-graphql_16.9.1_1679297014438_0.7230225417528451"},"_hasShrinkwrap":false}},"time":{"created":"2023-03-20T07:02:00.689Z","16.9.0":"2023-03-20T07:02:01.001Z","modified":"2023-03-20T07:23:34.819Z","16.9.1":"2023-03-20T07:23:34.672Z"},"maintainers":[{"name":"dbateman","email":"damien.bateman@gmail.com"}],"description":"Add Keycloak authentication and authorization to your GraphQL server via schema directives.","homepage":"https://github.com/dbateman/keycloak-connect-graphql","keywords":["graphql","apollo","keycloak","authentication","express"],"repository":{"type":"git","url":"git+https://github.com/dbateman/keycloak-connect-graphql.git"},"author":{"name":"dbateman"},"bugs":{"url":"https://github.com/dbateman/keycloak-connect-graphql/issues"},"license":"Apache-2.0","readme":"# @dbateman/keycloak-connect-graphql\n\n![GitHub](https://img.shields.io/github/license/aerogear/keycloak-connect-graphql.svg)\n\n## Updated For graphql ^16.0.0 and graphql-tools ^9.0.0 \nThis is a fork of the [original @Aerogear project](https://github.com/aerogear/keycloak-connect-graphql) (now sadly unmaintained) and has been updated to work with `Apollo Server 4` (other graphql servers are untested but there's a good chance they will work if built upon the same foundation), `graphql v16` and `graphql-tools v9`.\n\n## Apollo Server 4 Example\nIt's easy to get up and running with the `@auth`, `@hasRole` and `@hasPermission` custom directives provided by this library. Here's an example instantiation of an Apollo Server 4 instance (but can also be adapted to Apollo Server 3 simply enough, but it has some differences around context creation etc):\n\n```bash\nnpm i @dbateman/keycloak-connect-graphql\n```\n\nInstall required dependencies:\n```bash\nnpm i graphql keycloak-connect\n```\n\nInstall Apollo Server (or your preference of graphql server based on express)\n```bash\nnpm i @apollo/server \n```\n\nNow you need to define your schema, resolvers and start your apollo server.\n\n```typescript\nimport Keycloak from \"keycloak-connect\";\nimport {applyDirectiveTransformers, KeycloakContext} from \"@dbateman/keycloak-connect-graphql\";\nimport {makeExecutableSchema} from \"@graphql-tools/schema\";\nimport {ApolloServerPluginDrainHttpServer} from \"@apollo/server/plugin/drainHttpServer\";\nimport {expressMiddleware} from \"@apollo/server/express4\";\nimport express from \"express\";\nimport cors from \"cors\";\nimport http from \"http\";\n\nconst app = express();\nconst keycloak = new Keycloak({}, {/*...your keycloak connect config here...*/});\nconst httpServer = http.createServer(app);\n\n// Type your context for use in your resolvers etc.\ninterface IRequestContext {\n    // Must be called kauth as it is looked for by name in the\n    // directive resolvers.\n    kauth: KeycloakContext;\n}\n\n//\n// Define your schema somewhere.\n//\nconst schema = `#graphql\n    type Query {\n        doSomething: Boolean @hasRole(role: \"staff\")\n    }\n`;\n\n//\n// Define your resolvers.\n//\nconst resolvers = {\n    Query: {\n        doSomething: async (_: unknown, __: unknown, context: IRequestContext) => {\n            //\n            // You can access the keycloak context in your resolvers\n            // via context.kauth if you need more authorisation logic\n            // than is offered by the @hasRole et al directives:\n            //\n            //   context.kauth.hasRole(\"roleName\") etc\n            //\n            return true;\n        }\n    }\n}\n\n//\n// Instantiate your apollo server 4 instance.\n//\nconst apollo = new ApolloServer<IRequestContext>({\n    schema: applyDirectiveTransformers(makeExecutableSchema({typeDefs: schema, resolvers})),\n    plugins: [ApolloServerPluginDrainHttpServer({httpServer})]\n});\nawait apollo.start();\n\n//\n// Configure your express app to use the apollo server to handle\n// requests to /graphql (or wherever you prefer).\n//\napp.use(\n    '/graphql',\n    express.json(),\n    keycloak.middleware(),\n    expressMiddleware(\n        apollo,\n        {\n            context: async ({req}: any) => {\n                const context: IRequestContext = {\n                    request: req,\n                    kauth: new KeycloakContext({req})\n                }\n\n                // Do whatever else you need to here to set the context\n                // for each request.\n                \n                return context;\n            }\n        }\n    )\n);\n\n(async () => {\n    await httpServer.listen(PORT, () => {});\n});\n```\n\n---\n## Original Description (With Incompatible Stuff Removed)\nA comprehensive solution for adding [keycloak](https://www.keycloak.org/) authentication and authorisation to your Express based GraphQL server. \n\nBased on the [keycloak-connect](https://github.com/keycloak/keycloak-nodejs-connect) middleware for Express. Provides useful authentication/authorization features within your GraphQL application.\n\n## Features\n\n🔒  Auth at the **GraphQL layer**. Authentication and Role Based Access Control (RBAC) on individual Queries, Mutations and fields.\n\n⚡️  Auth on Subscriptions. Authentication and RBAC on incoming websocket connections for subscriptions.\n\n🔑  Access to token/user information in resolver context via `context.kauth` (for regular resolvers and subscriptions)\n\n📝  Declarative `@auth`, `@hasRole` and `@hasPermission` directives that can be applied directly in your Schema.\n\n⚙️  `auth`, `hasRole` and `hasPermission` middleware resolver functions that can be used directly in code. (Alternative to directives)\n\n## Using @auth, @hasRole and @hasPermission directives (Apollo Server only)\n\nIn Apollo Server, the `@auth`, `@hasRole` and `@hasPermission` directives can be used directly on the schema.\nThis declarative approach means auth logic is never mixed with business logic.\n\n```js\nconst Keycloak = require('keycloak-connect')\nconst { KeycloakContext, KeycloakTypeDefs, KeycloakSchemaDirectives } = require('keycloak-connect-graphql')\n\nconst typeDefs = gql`\n  type Article {\n    id: ID!\n    title: String!\n    content: String!\n  }\n\n  type Query {\n    listArticles: [Article]! @auth\n  }\n\n  type Mutation {\n    publishArticle(title: String!, content: String!): Article! @hasRole(role: \"editor\")\n    unpublishArticle(title: String!):Boolean @hasPermission(resources: [\"Article:publish\",\"Article:delete\"])\n  }\n`\n\nconst resolvers = {\n  Query: {\n    listArticles: (obj, args, context, info) => {\n      return Database.listArticles()\n    }\n  },\n  mutation: {\n    publishArticle: (object, args, context, info) => {\n      const user = context.kauth.accessToken.content // get the user details from the access token\n      return Database.createArticle(args.title, args.content, user)\n    },\n\tunpublishArticle: (object, args, context, info) => {\n\t  const user = context.kauth.accessToken.content\n      return Database.deleteArticle(args.title, user)\n    }\n  }\n}\n```\n\nIn this example a number of things are happening:\n\n1. `@auth` is applied to the `listArticles` Query. This means a user must be authenticated for this Query.\n2. `@hasRole(role: \"editor\")` is applied to the `publishArticle` Mutation. This means the keycloak user must have the editor *client role* in keycloak\n3. `@hasPermission(resources: [\"Article:publish\",\"Article:delete\"])` is applied to `unpublishArticle` Mutation. This means keycloak user must have all permissions given in resources array.\n4. The `publishArticle` resolver demonstrates how `context.kauth` can be used to get the keycloak user details\n\n### `auth`,`hasRole` and `hasPermission` middlewares.\n\n`keycloak-connect-graphql` also exports the `auth` ,`hasRole` and `hasPermission` logic directly. They can be thought of as middlewares that wrap your business logic resolvers. This is useful if you don't have a clear way to use schema directives (e.g. when using `graphql-express`).\n\n```js\nconst { auth, hasRole } = require('keycloak-connect-graphql')\n\nconst resolvers = {\n  Query: {\n    listArticles: auth(listArticlesResolver)\n  },\n  mutation: {\n    publishArticle: hasRole('editor')(publishArticleResolver),\n    unpublishArticle: hasPermission(['Article:publish','Article:delete'])(unpublishArticleResolver)\n  }\n}\n```\n\n### hasRole Usage and Options\n\n**`@hasRole` directive**\n\nThe syntax for the `@hasRole` schema directive is `@hasRole(role: \"rolename\")` or `@hasRole(role: [\"array\", \"of\", \"roles\"])`\n\n**`hasRole`**\n\n* The usage for the exported `hasRole` function is `hasRole('rolename')` or `hasRole(['array', 'of', 'roles'])`\n\nBoth the `@hasRole` schema directive and the exported `hasRole` function work exactly the same.\n\n* If a single string is provided, it returns true if the keycloak user has a **client role** with that name.\n* If an array of strings is provided, it returns true if the keycloak user has **at least one** client role that matches.\n\nBy default, hasRole checks for keycloak client roles.\n\n* Example: `hasRole('admin')` will check the logged in user has the client role named admin.\n\nIt also is possible to check for realm roles and application roles.\n* `hasRole('realm:admin')` will check the logged in user has the admin realm role\n* `hasRole('some-other-app:admin')` will check the loged in user has the admin realm role in a different application\n\n### hasPermission Usage and Options\n\n**`@hasPermission` directive**\n\nThe syntax for the `@hasPermission` schema directive is `@hasPermission(resources: \"resource:scope\")` or  `@hasPermission(resources: \"resource\")` because a scope is  optional or for multiple resources `@hasPermission(resources: [\"array\", \"of\", \"resources\"])`, use colon to separate name of the resource and optionally its scope.\n\n**`hasPermission`**\n\n* The usage for the exported `hasPermission` function is `hasPremission('resource:scope')` or `hasPermission(['array', 'of', 'resources'])`, use colon to separate name of the resource and optionally its scope.\n\nBoth the `@hasPermission` schema directive and the exported `hasPermission` function work exactly the same.\n\n* If a single string is provided, it returns true if the keycloak user has a permission for requested resource and its scope, if the scope is provided.\n* If an array of strings is provided, it returns true if the keycloak user has **all** requested permissions.\n\n## Apollo Server Express 3+ Support\n\n`apollo-server-express@^3.x` no longer supports the `SchemaDirectiveVisitor` class and therefor prevents\nyou from using the visitors of this library. They have adopted schema \n[transformers functions](https://www.apollographql.com/docs/apollo-server/schema/creating-directives/) that define behavior\non the schema fields with the directives.\n\nRemediating this is actually rather simple and gives you the option of adding a bit more authentication logic if needed,\nbut will require some understanding of the inner workings of this library.\n\nTo make things easy, this is an example implementation of what the transformers may look like. (Note the validation of roles and permissions\ngiven to their respective directives):\n\n```typescript\nimport { defaultFieldResolver, GraphQLSchema } from 'graphql';\nimport { getDirective, MapperKind, mapSchema } from '@graphql-tools/utils';\nimport { auth, hasPermission, hasRole } from 'keycloak-connect-graphql';\n\nconst authDirectiveTransformer = (schema: GraphQLSchema, directiveName: string = 'auth') => {\n  return mapSchema(schema, {\n    [MapperKind.OBJECT_FIELD]: (fieldConfig) => {\n      const authDirective = getDirective(schema, fieldConfig, directiveName)?.[0];\n      if (authDirective) {\n        const { resolve = defaultFieldResolver } = fieldConfig;\n        fieldConfig.resolve = auth(resolve);\n      }\n      return fieldConfig;\n    }\n  });\n};\n\nexport const permissionDirectiveTransformer = (schema: GraphQLSchema, directiveName: string = 'hasPermission') => {\n  return mapSchema(schema, {\n    [MapperKind.OBJECT_FIELD]: (fieldConfig) => {\n      const permissionDirective = getDirective(schema, fieldConfig, directiveName)?.[0];\n      if (permissionDirective) {\n        const { resolve = defaultFieldResolver } = fieldConfig;\n        const keys = Object.keys(permissionDirective);\n        let resources;\n        if (keys.length === 1 && keys[0] === 'resources') {\n          resources = permissionDirective[keys[0]];\n          if (typeof resources === 'string') resources = [resources];\n          if (Array.isArray(resources)) {\n            resources = resources.map((val: any) => String(val));\n          } else {\n            throw new Error('invalid hasRole args. role must be a String or an Array of Strings');\n          }\n        } else {\n          throw Error(\"invalid hasRole args. must contain only a 'role argument\");\n        }\n        fieldConfig.resolve = hasPermission(resources)(resolve);\n      }\n      return fieldConfig;\n    }\n  });\n};\n\nexport const roleDirectiveTransformer = (schema: GraphQLSchema, directiveName: string = 'hasRole') => {\n  return mapSchema(schema, {\n    [MapperKind.OBJECT_FIELD]: (fieldConfig) => {\n      const roleDirective = getDirective(schema, fieldConfig, directiveName)?.[0];\n      if (roleDirective) {\n        const { resolve = defaultFieldResolver } = fieldConfig;\n        const keys = Object.keys(roleDirective);\n        let role;\n        if (keys.length === 1 && keys[0] === 'role') {\n          role = roleDirective[keys[0]];\n          if (typeof role === 'string') role = [role];\n          if (Array.isArray(role)) {\n            role = role.map((val: any) => String(val));\n          } else {\n            throw new Error('invalid hasRole args. role must be a String or an Array of Strings');\n          }\n        } else {\n          throw Error(\"invalid hasRole args. must contain only a 'role argument\");\n        }\n        fieldConfig.resolve = hasRole(role)(resolve);\n      }\n      return fieldConfig;\n    }\n  });\n};\n\nexport const applyDirectiveTransformers = (schema: GraphQLSchema) => {\n  return authDirectiveTransformer(roleDirectiveTransformer(permissionDirectiveTransformer(schema)));\n};\n```\n\nWith your transformers defined, apply them on the schema and continue configuring your server instance:\n```typescript\n...\nlet schema = makeExecutableSchema({\n  typeDefs,\n  resolvers\n});\n\nschema = applyDirectiveTransformers(schema);\n\n// Now just passing the schema in the options, configurting the context with Keycloak as before.\nconst server = new ApolloServer({\n  schema,\n  context: ({ req }) => {\n    return {\n      kauth: new KeycloakContext({ req }, keycloak)\n    };\n  }\n});\n...\n```\n\n### Error Codes\n\nLibrary will return specific GraphQL errors to the client that can\nbe differenciated by using error codes.\n\nExample response from GraphQL Server could look as follows:\n\n```json\n{\n   \"errors\":[\n      {\n        \"message\":\"User is not authorized. Must have one of the following roles: [admin]\",\n        \"code\": \"FORBIDDEN\"\n      }\n   ]\n}\n```\n\nPossible error codes: \n\n- `UNAUTHENTICATED`: returned when user is not authenticated to access API because it requires login\n- `FORBIDDEN`: returned when user do not have permission to perform operation \n\n\n","readmeFilename":"README.md"}