{"id":13566781,"url":"https://github.com/aerogear/keycloak-connect-graphql","last_synced_at":"2025-04-04T00:32:13.486Z","repository":{"id":37789913,"uuid":"195026069","full_name":"aerogear/keycloak-connect-graphql","owner":"aerogear","description":"Add Keyloak Authentication and Authorization to your GraphQL server.","archived":true,"fork":false,"pushed_at":"2023-04-17T14:15:45.000Z","size":187,"stargazers_count":158,"open_issues_count":17,"forks_count":23,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-02T15:11:59.987Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aerogear.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-07-03T09:49:44.000Z","updated_at":"2025-01-13T03:05:52.000Z","dependencies_parsed_at":"2024-01-07T21:06:45.279Z","dependency_job_id":"9f656157-ed7a-4922-8ece-d69d3bf88680","html_url":"https://github.com/aerogear/keycloak-connect-graphql","commit_stats":{"total_commits":145,"total_committers":12,"mean_commits":"12.083333333333334","dds":0.6620689655172414,"last_synced_commit":"272415fd4c478e7a8ab1213f3b8c9bfecb129d93"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aerogear%2Fkeycloak-connect-graphql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aerogear%2Fkeycloak-connect-graphql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aerogear%2Fkeycloak-connect-graphql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aerogear%2Fkeycloak-connect-graphql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aerogear","download_url":"https://codeload.github.com/aerogear/keycloak-connect-graphql/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247103290,"owners_count":20884023,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T13:02:16.691Z","updated_at":"2025-04-04T00:32:12.973Z","avatar_url":"https://github.com/aerogear.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","Integrations"],"sub_categories":[],"readme":"# keycloak-connect-graphql\n\n[![CircleCI](https://img.shields.io/circleci/build/github/aerogear/keycloak-connect-graphql.svg)](https://circleci.com/gh/aerogear/keycloak-connect-graphql)\n[![Coverage Status](https://coveralls.io/repos/github/aerogear/keycloak-connect-graphql/badge.svg)](https://coveralls.io/github/aerogear/keycloak-connect-graphql)\n![npm](https://img.shields.io/npm/v/keycloak-connect-graphql.svg)\n![GitHub](https://img.shields.io/github/license/aerogear/keycloak-connect-graphql.svg)\n\nA comprehensive solution for adding [keycloak](https://www.keycloak.org/) Authentication and Authorization 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## Getting Started\n\nInstall library \n```bash\nnpm install --save keycloak-connect-graphql\n```\n\nInstall required dependencies:\n```bash\nnpm install --save  graphql keycloak-connect\n```\n\nInstall one of the Apollo Server libraries \n```bash\nnpm install --save apollo-server-express \n```\n\nThere are 3 steps to set up `keycloak-connect-graphql` in your application.\n\n1. Add the `KeycloakTypeDefs` along with your own type defs.\n2. Add the `KeycloakSchemaDirectives` (Apollo Server)\n3. Add the `KeycloakContext` to `context.kauth`\n\nThe example below shows a typical setup with comments beside each of the 3 steps mentioned.\n\n```javascript\nconst { ApolloServer, gql } = require('apollo-server-express')\nconst Keycloak = require('keycloak-connect')\n\nconst { KeycloakContext, KeycloakTypeDefs, KeycloakSchemaDirectives } = require('keycloak-connect-graphql')\n\nconst { typeDefs, resolvers } = require('./schema')\n\nconst app = express()\nconst keycloak = new Keycloak()\n\napp.use(graphqlPath, keycloak.middleware())\n\nconst server = new ApolloServer({\n  typeDefs: [KeycloakTypeDefs, typeDefs], // 1. Add the Keycloak Type Defs\n  schemaDirectives: KeycloakSchemaDirectives, // 2. Add the KeycloakSchemaDirectives\n  resolvers,\n  context: ({ req }) =\u003e {\n    return {\n      kauth: new KeycloakContext({ req }, keycloak) // 3. add the KeycloakContext to `kauth`\n    }\n  }\n})\n\nserver.applyMiddleware({ app })\n\napp.listen({ 4000 }, () =\u003e\n  console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`)\n) \n```\n\nIn this example `keycloak.middleware()` is used on the GraphQL endpoint. This allows for **Authentication and Authorization at the GraphQL Layer**. `keycloak.middleware` parses user token information if found, but will not block unauthenticated requests. This approach gives us the flexibility to implement authentication on individual Queries, Mutations and Fields.\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) =\u003e {\n      return Database.listArticles()\n    }\n  },\n  mutation: {\n    publishArticle: (object, args, context, info) =\u003e {\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) =\u003e {\n\t  const user = context.kauth.accessToken.content\n      return Database.deleteArticle(args.title, user)\n    }\n  }\n}\n\nconst keycloak = new Keycloak()\n\nconst server = new ApolloServer({\n  typeDefs: [KeycloakTypeDefs, typeDefs], // 1. Add the Keycloak Type Defs\n  schemaDirectives: KeycloakSchemaDirectives, // 2. Add the KeycloakSchemaDirectives\n  resolvers,\n  context: ({ req }) =\u003e {\n    return {\n      kauth: new KeycloakContext({ req }, keycloak) // 3. add the KeycloakContext to `kauth`\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') =\u003e {\n  return mapSchema(schema, {\n    [MapperKind.OBJECT_FIELD]: (fieldConfig) =\u003e {\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') =\u003e {\n  return mapSchema(schema, {\n    [MapperKind.OBJECT_FIELD]: (fieldConfig) =\u003e {\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 \u0026\u0026 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) =\u003e 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') =\u003e {\n  return mapSchema(schema, {\n    [MapperKind.OBJECT_FIELD]: (fieldConfig) =\u003e {\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 \u0026\u0026 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) =\u003e 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) =\u003e {\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 }) =\u003e {\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## Authentication and Authorization on Subscriptions\n\nThe `KeycloakSubscriptionHandler` provides a way to validate incoming websocket connections to `SubscriptionServer` from [`subscriptions-transport-ws`](https://www.npmjs.com/package/subscriptions-transport-ws) for subscriptions and add the keycloak user token to the `context` in subscription resolvers.\n\nUsing `onSubscriptionConnect` inside the `onConnect` function, we can parse and validate the keycloak user token from the `connectionParams`. The example below shows the typical setup that will **ensure all subscriptions must be authenticated**.\n\n```js\nconst { KeycloakSubscriptionHandler } = require('keycloak-connect-graphql')\n\n// Apollo Server Setup Goes Here. (See Getting Started Section)\n\nconst httpServer = app.listen({ port }, () =\u003e {\n   console.log(`🚀 Server ready at http://localhost:${port}${server.graphqlPath}`)\n\n   const keycloakSubscriptionHandler = new KeycloakSubscriptionHandler({ keycloak })\n   new SubscriptionServer({\n     execute,\n     subscribe,\n     schema: server.schema,\n     onConnect: async (connectionParams, websocket, connectionContext) =\u003e {\n       const token = await keycloakSubscriptionHandler.onSubscriptionConnect(connectionParams)\n       return {\n         kauth: new KeycloakSubscriptionContext(token)\n       }\n     }\n   }, {\n     server: httpServer,\n     path: '/graphql'\n   })\n})\n```\n\nIn this example, `keycloakSubscriptionHandler.onSubscriptionConnect` parses the connectionParams into a Keycloak Access Token. The value returned from `onConnect` becomes the `context` in subscription resolvers. By returning `{ kauth: new KeycloakSubscriptionContext }` we will have access to the keycloak user token in our subscription resolvers.\n\nBy default, `onSubscriptionConnect` throws an Authentication `Error` and the subscription is cancelled if invalid `connectionParams` or an expired/invalid keycloak token is supplied. This is an easy way to force authentication on all subscriptions.\n\nFor more information, please read the generic apollo documentation on [Authentication Over Websockets.](https://www.apollographql.com/docs/apollo-server/features/subscriptions/#authentication-over-websocket)\n\n### Advanced Authentication and Authorization on Subscriptions\n\nThe `auth` and `hasRole` middlewares can be used on individual subscriptions. Use the same code to from the [Authentication and Authorization on Subscriptions](#authentication-and-authorization-on-subscriptions) example but intialise the `KeycloakSubscriptionHandler` with `protect:false`.\n\n```js\nconst keycloakSubscriptionHandler = new KeycloakSubscriptionHandler({ keycloak, protect: false })\n```\n\nWhen `protect` is false, an error will not be thrown during the initial websocket connection attempt if the client is not authenticated. Instead, the `auth`,`hasRole` and `hasPermission` middlewares can be used on the individual subscription resolvers.\n\n```js\nconst { auth, hasRole } = require('keycloak-connect-graphql')\n\nconst typeDefs = gql`\n  type Message {\n    content: String!\n    author: String\n  }\n\n  type Comment {\n    content: String!\n    author: String\n  }\n\n  type Subscription {\n    commentAdded: Comment!\n    messageAdded: Message! @auth\n    alertAdded: String @hasRole(role: \"admin\") \n  }\n`\n\nconst resolvers = {\n  Subscription: {\n    commentAdded: {\n      subscribe: () =\u003e pubsub.asyncIterator(COMMENT_ADDED)\n    },\n    messageAdded: {\n      subscribe: auth(() =\u003e pubsub.asyncIterator(COMMENT_ADDED))\n    },\n    alertAdded: hasRole('admin')(() =\u003e pubsub.asyncIterator(ALERT_ADDED)),\n    alertRemoved: hasPermission('alert:remove')(() =\u003e pubsub.asyncIterator(ALERT_REMOVED))\n  }\n}\n```\n\nIn this hypothetical application we have three subscription type that have varying levels of Authentication/Authorization\n\n* commentAdded - Unauthenticated users can subscribe.\n* messageAdded - Only authenticated users can subscribe.\n* alertAdded - Only authenticated user with the `admin` client role can subscribe\n* alertRemoved - Only authenticated user with the permission on resource `alert` and scope `remove`  can subscribe\n\n### Client Authentication over Websocket\n\nThe GraphQL client should provide the following `connectionParams` when attempting a websocket connection.\n\n```json\n{\n  \"Authorization\": \"Bearer \u003ckeycloak token value\u003e\"\n}\n```\n\nThe example code shows how it could be done on the client side using Apollo Client.\n\n```js\nimport Keycloak from 'keycloak-js'\nimport { WebSocketLink } from 'apollo-link-ws'\n\n\nvar keycloak = Keycloak({\n    url: 'http://keycloak-server/auth',\n    realm: 'myrealm',\n    clientId: 'myapp'\n})\n\nconst wsLink = new WebSocketLink({\n  uri: 'ws://localhost:5000/',\n  options: {\n    reconnect: true,\n    connectionParams: {\n        Authorization: `Bearer ${keycloak.token}`\n    }\n})\n```\n\nSee the Apollo Client documentation for [Authentication Params Over Websocket](https://www.apollographql.com/docs/react/advanced/subscriptions/#authentication-over-websocket).\n\nSee the Keycloak Documentation for the [Keycloak JavaScript Adapter](https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter)\n\n## Usage with Apollo Federation\n`keycloak-connect-graphql` can be used with [Apollo Federation](https://www.apollographql.com/docs/apollo-server/federation/introduction/) for your distributed GraphQL service.\n\nThere are 4 steps to set up `keycloak-connect-graphql` in your distributed application using [Apollo Federation](https://www.apollographql.com/docs/apollo-server/federation/introduction/). The **first 3 steps** you should do in **every service** and for the step 3 you should also do to `gateway` service, then the step 4 just in a `gateway` service. \n1. Add the `KeycloakTypeDefs` along with your own type defs.\n2. Add the `KeycloakSchemaDirectives` (Apollo Server)\n3. Add the `KeycloakContext` to context.kauth\n4. Setup the gateway to pass `Authorization` token to all services.  \n\nFor the first 3 steps, you could see example at [Getting Started](#getting-started) section. So, The example below shows how to setup the `gateway` service.\n\n```javascript\nconst { ApolloGateway, RemoteGraphQLDataSource  } = require(\"@apollo/gateway\");\n\nconst gateway = new ApolloGateway({\n  serviceList: [\n    { name: \"accounts\", url: \"http://localhost:4001/graphql\" },\n    { name: \"reviews\", url: \"http://localhost:4002/graphql\" },\n    { name: \"products\", url: \"http://localhost:4003/graphql\" },\n    { name: \"inventory\", url: \"http://localhost:4004/graphql\" }\n    // other services might be entry\n  ],\n  buildService({ name, url }) {\n    return new RemoteGraphQLDataSource({\n      url,\n      willSendRequest({ request, context }) {\n        // 4. Setup the gateway to pass `Authorization` token to all services.\n        // Passing Keycloak Access Token to services.\n        if (context.kauth \u0026\u0026 context.kauth.accessToken) {\n          request.http.headers.set('Authorization', 'bearer '+ context.kauth.accessToken.token);\n        }\n      }\n    })\n  },\n\n  // Experimental: Enabling this enables the query plan view in Playground.\n  __exposeQueryPlanExperimental: false,\n});\n```\n\nSee the example project for [Apollo Federation with Keycloak](https://github.com/ilmimris/apollofederation-keycloak-demo).\n\n\u003e Apollo Federation does not currently support GraphQL subscription operations.\n\n## Examples\n\nThe `examples` folder contains runnable examples that demonstrate the various ways to use this library.\n\n* `examples/basic.js` - Shows the basic setup needed to use the library. Uses `keycloak.connect()` to require authentication on the entire GraphQL API.\n* `examples/advancedAuth` - Shows how to use the `@auth` and `@hasRole` schema directives to apply auth at the GraphQL layer.\n* `examples/authMiddlewares` - Shows usage of the `auth` and `hasRole` middlewares.\n* `examples/resourceBasedAuht` - Shows how to use `@hasPermission` middleware.\n* `subscriptions` - Shows basic subscriptions setup, requiring all subscriptions to be authenticated.\n* `subscriptionsAdvanced` - Shows subscriptions that use the `auth` and `hasRole` middlewares directly on subscription resolvers\n* `subscriptionsResourceBasedAuth.js` - Shows subscriptions that use the `auth` and `hasPermission` middlewares directly on subscription resolvers\n\n\u003e NOTE: Examples using unrelased code that needs to be compiled before use.\nPlease run `npm run compile` to compile source code before running examples.\n\n## Setting up the Examples\n\nPrerequisites:\n\n* Docker and docker-compose installed\n* Node.js and NPM installed\n\nStart by cloning this repo.\n\n```\ngit clone https://github.com/aerogear/keycloak-connect-graphql/\n```\n\nThen start a Keycloak server using `docker-compose`.\n\n```\ncd examples/config \u0026\u0026 docker-compose up\n```\n\nNow in a separate terminal, seed the keycloak server with a sample configuration.\n\n```\n$ npm run examples:seed\n\ncreating role admin\ncreating role developer\ncreating client role admin for client keycloak-connect-graphql-bearer\ncreating client role developer for client keycloak-connect-graphql-bearer\ncreating client role admin for client keycloak-connect-graphql-public\ncreating client role developer for client keycloak-connect-graphql-public\ncreating user developer with password developer\nassigning client and realm roles called \"developer\" to user developer\ncreating user admin with password admin\nassigning client and realm roles called \"admin\" to user admin\ndone\n```\n\nThis creates a sample realm called `keycloak-connect-graphql` with some clients, roles and users that we can use in the examples.\nNow we are ready to start and explore the examples.\n\nThe Keycloak console is accessible at [localhost:8080](http://localhost:8080) and the admin login is `admin/admin`. You can make any configuration changes you wish and `npm run examples:seed` will always recreate the example realm from scratch.\n\n## Running the Basic Example\n\nThe basic example shows:\n\n* The setup of the keycloak express middleware\n* How to add **Role Based Access Control** using the `@hasRole` schema directive.\n\nIn `examples/basic.js` the GraphQL schema for the server is defined:\n\n```js\nconst typeDefs = gql`\n  type Query {\n    hello: String @hasRole(role: \"developer\")\n  }\n`\n```\n\nThe `@hasRole` directive means only users with the `developer` role are authorized to perform the `hello` query. Start the server to try it out.\n\n```\n$ node examples/basic.js\n🚀 Server ready at http://localhost:4000/graphql\n```\n\nOpen the URL and you will see the Keycloak login screen. First login with `developer/developer` as the username/password.\n\nNow you should see the GraphQL Playground.\n\nNOTE: The login page is shown because the Keycloak middleware is enforcing authentication on the `/graphql` endpoint using a `public` client configuration. A public client is being used so we can access the GraphQL Playground in the browser. In production, your GraphQL API would use a `bearer` client configuration and instead you would receive an `Access Denied` message.\n\nOn the right side of the GraphQL Playground you will see a message:\n\n```\n{\n  \"error\": \"Failed to fetch. Please check your connection\"\n}\n```\n\nAlthough the browser has authenticated with the Keycloak server, the GraphQL playround isn't sending the keycloak `Authorization` header along with its requests to the GraphQL server. In the bottom left corner of the playground there is a field called **HTTP Headers** which will be added to requests sent by the playground.\n\nUse `scripts/getToken.js` to get a valid header for the `developer` user.\n\n```\nnode scripts/getToken.js developer developer # username password\n\n{\"Authorization\":\"Bearer \u003ctoken string\u003e\"}\n```\n\nCopy the entire JSON object, then paste it into the HTTP Headers field in the playground. The error message should disappear.\n\nNow try the following query:\n\n```\nquery {\n  hello\n}\n```\n\nYou should see the result.\n\n```\n{\n  \"data\": {\n    \"hello\": \"Hello developer\"\n  }\n}\n```\n\nThe `hasRole` directive checked that the user had the appropriate role and then the GraphQL resolver successfully executed. Let's change the role. Change the code in `examples/basic.js` to the code below and then restart the server.\n\n```js\nconst typeDefs = gql`\n  type Query {\n    hello: String @hasRole(role: \"admin\")\n  }\n`\n```\n\nNow run the query in the playground again. You should see an error.\n\n```\n{\n  \"errors\": [\n    {\n      \"message\": \"User is not authorized. Must have one of the following roles: [admin]\",\n      \"locations\": [\n        {\n          \"line\": 2,\n          \"column\": 3\n        }\n      ],\n      \"path\": [\n        \"hello\"\n      ],\n      \"extensions\": \u003comitted\u003e\n    }\n  ],\n  \"data\": {\n    \"hello\": null\n  }\n}\n```\n\nThis time an error comes back saying the user does not have the right role. That's the full example! The process of running and trying the other examples is very similar. Feel free to try them or to look at the code!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faerogear%2Fkeycloak-connect-graphql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faerogear%2Fkeycloak-connect-graphql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faerogear%2Fkeycloak-connect-graphql/lists"}