From b60c9006152bd033000a0663fa3f04391ec64b11 Mon Sep 17 00:00:00 2001 From: Wojtek Trocki Date: Thu, 27 Feb 2020 09:37:35 +0000 Subject: [PATCH] Fix issues with typings (#73) * fix: add yarn lock to git ignore * fix: Make onSubscriptionConnect parameters optional as in example app * fix: Use granted request from code * fix: Use peer dependencies for keycloak and graphql-tools * fix: Unit tests type mappings --- .gitignore | 1 + README.md | 16 +++++++++++++++- package.json | 14 ++++++++------ src/KeycloakContext.ts | 9 +++++++-- src/KeycloakSubscriptionHandler.ts | 2 +- test/KeycloakContext.test.ts | 12 ++++++------ test/auth.test.ts | 10 +++++----- test/hasRole.test.ts | 14 +++++++------- 8 files changed, 50 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index c183d01..3410b8f 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ dist coverage .nyc_output .vscode +yarn.lock diff --git a/README.md b/README.md index 9749817..a399de5 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,19 @@ Based on the [keycloak-connect](https://github.com/keycloak/keycloak-nodejs-conn ## Getting Started +Install library ```bash -npm install keycloak-connect-graphql +npm install --save keycloak-connect-graphql +``` + +Install required dependencies: +```bash +npm install --save graphql graphql-tools keycloak-connect +``` + +Install one of the Apollo Server libraries +```bash +npm install --save apollo-server-express ``` There are 3 steps to set up `keycloak-connect-graphql` in your application. @@ -300,6 +311,9 @@ The `examples` folder contains runnable examples that demonstrate the various wa * `subscriptions` - Shows basic subscriptions setup, requiring all subscriptions to be authenticated. * `subscriptionsAdvanced` - Shows subscriptions that use the `auth` and `hasRole` middlewares directly on subscription resolvers. +> NOTE: Examples using unrelased code that needs to be compiled before use. +Please run `npm run compile` to compile source code before running examples. + ## Setting up the Examples Prerequisites: diff --git a/package.json b/package.json index 54387ae..8edb2d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloak-connect-graphql", - "version": "0.2.4", + "version": "0.3.0", "description": "Add Keycloak authentication and authorization to your GraphQL server.", "keywords": [ "graphql", @@ -34,9 +34,6 @@ "examples:seed": "node scripts/initKeycloak.js" }, "dependencies": { - "apollo-server-express": "2.9.16", - "graphql-tools": "4.0.6", - "keycloak-connect": "8.0.1" }, "devDependencies": { "@types/express-session": "1.15.16", @@ -58,10 +55,15 @@ "subscriptions-transport-ws": "0.9.16", "ts-node": "8.6.2", "tslint": "5.20.1", - "typescript": "3.7.5" + "typescript": "3.7.5", + "apollo-server-express": "2.9.16", + "graphql-tools": "4.0.6", + "keycloak-connect": "8.0.1" }, "peerDependencies": { - "graphql": "^0.12.0 || ^0.13.0 || ^14.0.0" + "graphql": "^0.12.0 || ^0.13.0 || ^14.0.0", + "graphql-tools": ">=4.0.0", + "keycloak-connect": ">=7.0.0" }, "nyc": { "extension": [ diff --git a/src/KeycloakContext.ts b/src/KeycloakContext.ts index 9c12020..85781ae 100644 --- a/src/KeycloakContext.ts +++ b/src/KeycloakContext.ts @@ -1,5 +1,10 @@ import { AuthContextProvider } from './api' import Keycloak from 'keycloak-connect' +import { Grant } from 'keycloak-connect' + +export interface GrantedRequest extends Request { + kauth: { grant?: Grant }; +} export const CONTEXT_KEY = 'kauth' @@ -66,10 +71,10 @@ export class KeycloakContextBase implements AuthContextProvider { * */ export class KeycloakContext extends KeycloakContextBase implements AuthContextProvider { - public readonly request: Keycloak.GrantedRequest + public readonly request: GrantedRequest public readonly accessToken: Keycloak.Token | undefined - constructor ({ req }: { req: Keycloak.GrantedRequest }) { + constructor ({ req }: { req: GrantedRequest }) { const token = (req && req.kauth && req.kauth.grant) ? req.kauth.grant.access_token : undefined super(token) this.request = req diff --git a/src/KeycloakSubscriptionHandler.ts b/src/KeycloakSubscriptionHandler.ts index 6702431..5fb18ae 100644 --- a/src/KeycloakSubscriptionHandler.ts +++ b/src/KeycloakSubscriptionHandler.ts @@ -54,7 +54,7 @@ export class KeycloakSubscriptionHandler { * @param webSocket * @param context */ - public async onSubscriptionConnect(connectionParams: any, webSocket: any, context: any): Promise { + public async onSubscriptionConnect(connectionParams: any, webSocket?: any, context?: any): Promise { if (!connectionParams || typeof connectionParams !== 'object') { if (this.protect === true) { throw new Error('Access Denied - missing connection parameters for Authentication') diff --git a/test/KeycloakContext.test.ts b/test/KeycloakContext.test.ts index 3c9a3f3..6e74ad7 100644 --- a/test/KeycloakContext.test.ts +++ b/test/KeycloakContext.test.ts @@ -1,7 +1,7 @@ import test from 'ava' import Keycloak from 'keycloak-connect' -import { KeycloakContext, KeycloakContextBase, KeycloakSubscriptionContext } from '../src/KeycloakContext' +import { KeycloakContext, KeycloakContextBase, KeycloakSubscriptionContext, GrantedRequest } from '../src/KeycloakContext' test('KeycloakContextBase accessToken is the access_token in req.kauth', (t) => { const token = { @@ -47,7 +47,7 @@ test('KeycloakContext accessToken is the access_token in req.kauth', (t) => { } } } - } as Keycloak.GrantedRequest + } as GrantedRequest const provider = new KeycloakContext({ req }) const token = req.kauth.grant && req.kauth.grant.access_token ? req.kauth.grant.access_token : undefined @@ -70,7 +70,7 @@ test('KeycloakContext hasRole calls hasRole in the access_token', (t) => { } } } - } as Keycloak.GrantedRequest + } as GrantedRequest const provider = new KeycloakContext({ req }) t.truthy(provider.hasRole('')) @@ -90,7 +90,7 @@ test('KeycloakContext.isAuthenticated is true when token is defined and isExpire } } } - } as Keycloak.GrantedRequest + } as GrantedRequest const provider = new KeycloakContext({ req }) t.truthy(provider.isAuthenticated()) @@ -110,7 +110,7 @@ test('KeycloakContext.isAuthenticated is false when token is defined but isExpir } } } - } as Keycloak.GrantedRequest + } as GrantedRequest const provider = new KeycloakContext({ req }) t.false(provider.isAuthenticated()) @@ -130,7 +130,7 @@ test('KeycloakContext.hasRole is false if token is expired', (t) => { } } } - } as Keycloak.GrantedRequest + } as GrantedRequest const provider = new KeycloakContext({ req }) t.false(provider.hasRole('')) diff --git a/test/auth.test.ts b/test/auth.test.ts index ff7daec..d040877 100644 --- a/test/auth.test.ts +++ b/test/auth.test.ts @@ -6,7 +6,7 @@ import { GraphQLSchema } from 'graphql' import { VisitableSchemaType } from 'graphql-tools/dist/schemaVisitor' import { AuthDirective } from '../src/directives/schemaDirectiveVisitors' -import { KeycloakContext } from '../src/KeycloakContext' +import { KeycloakContext, GrantedRequest } from '../src/KeycloakContext' const createHasRoleDirective = () => { return new AuthDirective({ @@ -43,7 +43,7 @@ test('happy path: context.kauth.isAuthenticated() is called, then original resol } } } - } as Keycloak.GrantedRequest + } as GrantedRequest const context = { request: req, @@ -85,7 +85,7 @@ test('context.kauth.isAuthenticated() is called, even if field has no resolver', } } } - } as Keycloak.GrantedRequest + } as GrantedRequest const context = { request: req, @@ -130,7 +130,7 @@ test('resolver will throw if context.kauth is not present', async (t) => { } } } - } as Keycloak.GrantedRequest + } as GrantedRequest const context = { request: req @@ -161,7 +161,7 @@ test('resolver will throw if context.kauth present but context.kauth.isAuthentic const root = {} const args = {} - const req = {} as Keycloak.GrantedRequest + const req = {} as GrantedRequest const context = { request: req, diff --git a/test/hasRole.test.ts b/test/hasRole.test.ts index 4f56949..c9ac509 100644 --- a/test/hasRole.test.ts +++ b/test/hasRole.test.ts @@ -5,7 +5,7 @@ import Keycloak from 'keycloak-connect' import { GraphQLSchema } from 'graphql' import { VisitableSchemaType } from 'graphql-tools/dist/schemaVisitor' import { HasRoleDirective } from '../src/directives/schemaDirectiveVisitors' -import { KeycloakContext } from '../src/KeycloakContext' +import { KeycloakContext, GrantedRequest } from '../src/KeycloakContext' const createHasRoleDirective = (directiveArgs: any) => { return new HasRoleDirective({ @@ -51,7 +51,7 @@ test('context.auth.hasRole() is called', async (t) => { } } } - } as Keycloak.GrantedRequest + } as GrantedRequest const context = { request: req, @@ -98,7 +98,7 @@ test('hasRole works on fields that have no resolvers. context.auth.hasRole() is } } } - } as Keycloak.GrantedRequest + } as GrantedRequest const context = { request: req, @@ -148,7 +148,7 @@ test('visitFieldDefinition accepts an array of roles', async (t) => { } } } - } as Keycloak.GrantedRequest + } as GrantedRequest const context = { request: req, @@ -185,7 +185,7 @@ test('if there is no authentication, then an error is returned and the original const root = {} const args = {} - const req = {} as Keycloak.GrantedRequest + const req = {} as GrantedRequest const context = { request: req, kauth: new KeycloakContext({ req }) @@ -236,7 +236,7 @@ test('if token does not have the required role, then an error is returned and th } } } - } as Keycloak.GrantedRequest + } as GrantedRequest const context = { request: req, @@ -395,7 +395,7 @@ test('context.auth.hasRole() works even if request is not supplied in context', } } } - } as Keycloak.GrantedRequest + } as GrantedRequest const context = { kauth: new KeycloakContext({ req })