diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e326835..01f8844 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,7 +4,7 @@ specifiers: '@nestjs/common': ^8.0.0 '@nestjs/core': ^8.0.0 '@nestjs/graphql': ^8.0.0 - '@types/express': 4.16.1 + '@types/express': ^4.17.13 '@types/node': ^13.13.5 '@typescript-eslint/eslint-plugin': ^4.22.0 '@typescript-eslint/parser': ^4.22.0 @@ -29,7 +29,7 @@ devDependencies: '@nestjs/common': 8.0.5_7de7ece315067f012f2001b6a7f20971 '@nestjs/core': 8.0.5_1b001a5d86d3fbb71f818ebd3972ff15 '@nestjs/graphql': 8.0.2_ca28c3c4316f91f336ba4c5d137586f0 - '@types/express': 4.16.1 + '@types/express': 4.17.13 '@types/node': 13.13.52 '@typescript-eslint/eslint-plugin': 4.29.0_48ea228fa0647506aa803d17f48b59f7 '@typescript-eslint/parser': 4.29.0_eslint@7.32.0+typescript@4.3.5 @@ -89,7 +89,7 @@ packages: peerDependencies: graphql: 0.13.1 - 15 dependencies: - '@types/express': 4.16.1 + '@types/express': 4.17.13 '@types/fs-capacitor': 2.0.0 '@types/koa': 2.13.4 busboy: 0.3.1 @@ -569,7 +569,7 @@ packages: resolution: {integrity: sha512-h7BcvPUogWbKCzBR2lY4oqaZbO3jXZksexYJVFvkrFeLgbZjQkU4x8pRq6eg2MHXQhY0McQdqmmsxRWlVAHooA==} dependencies: '@types/connect': 3.4.35 - '@types/express': 4.16.1 + '@types/express': 4.17.13 '@types/keygrip': 1.0.2 '@types/node': 13.13.52 dev: true @@ -582,11 +582,12 @@ packages: '@types/range-parser': 1.2.4 dev: true - /@types/express/4.16.1: - resolution: {integrity: sha512-V0clmJow23WeyblmACoxbHBu2JKlE5TiIme6Lem14FnPW9gsttyHtk6wq7njcdIWH1njAaFgR8gW09lgY98gQg==} + /@types/express/4.17.13: + resolution: {integrity: sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==} dependencies: '@types/body-parser': 1.19.1 '@types/express-serve-static-core': 4.17.24 + '@types/qs': 6.9.7 '@types/serve-static': 1.13.10 dev: true @@ -1310,8 +1311,8 @@ packages: fsevents: 2.3.2 dev: true - /chromedriver/99.0.0: - resolution: {integrity: sha512-pyB+5LuyZdb7EBPL3i5D5yucZUD+SlkdiUtmpjaEnLd9zAXp+SvD/hP5xF4l/ZmWvUo/1ZLxAI1YBdhazGTpgA==} + /chromedriver/100.0.0: + resolution: {integrity: sha512-oLfB0IgFEGY9qYpFQO/BNSXbPw7bgfJUN5VX8Okps9W2qNT4IqKh5hDwKWtpUIQNI6K3ToWe2/J5NdpurTY02g==} engines: {node: '>=10'} hasBin: true requiresBuild: true @@ -2729,7 +2730,7 @@ packages: dependencies: jwk-to-pem: 2.0.5 optionalDependencies: - chromedriver: 99.0.0 + chromedriver: 100.0.0 transitivePeerDependencies: - debug - supports-color diff --git a/src/guards/auth.guard.ts b/src/guards/auth.guard.ts index bddb41b..6e76a8e 100644 --- a/src/guards/auth.guard.ts +++ b/src/guards/auth.guard.ts @@ -84,7 +84,7 @@ export class AuthGuard implements CanActivate { this.logger.verbose(`User JWT: ${jwt}`); - const keycloak = useKeycloak( + const keycloak = await useKeycloak( request, jwt, this.singleTenant, diff --git a/src/guards/resource.guard.ts b/src/guards/resource.guard.ts index 913269d..7e27150 100644 --- a/src/guards/resource.guard.ts +++ b/src/guards/resource.guard.ts @@ -111,7 +111,7 @@ export class ResourceGuard implements CanActivate { const user = request.user?.preferred_username ?? 'user'; const enforcerFn = createEnforcerContext(request, response, enforcerOpts); - const keycloak = useKeycloak( + const keycloak = await useKeycloak( request, request.accessTokenJWT, this.singleTenant, diff --git a/src/guards/role.guard.ts b/src/guards/role.guard.ts index 2bba56f..d21717f 100644 --- a/src/guards/role.guard.ts +++ b/src/guards/role.guard.ts @@ -70,7 +70,7 @@ export class RoleGuard implements CanActivate { } // Create grant - const keycloak = useKeycloak( + const keycloak = await useKeycloak( request, request.accessTokenJWT, this.singleTenant, diff --git a/src/interface/keycloak-connect-options.interface.ts b/src/interface/keycloak-connect-options.interface.ts index 44edc77..1090124 100644 --- a/src/interface/keycloak-connect-options.interface.ts +++ b/src/interface/keycloak-connect-options.interface.ts @@ -9,8 +9,8 @@ export type KeycloakConnectOptions = string | KeycloakConnectConfig; * Multi tenant configuration. */ export interface MultiTenantOptions { - realmResolver: (request: any) => string; - realmSecretResolver?: (realm: string) => string; + realmResolver: (request: any) => Promise | string; + realmSecretResolver?: (realm: string) => Promise | string; } /** diff --git a/src/services/keycloak-multitenant.service.ts b/src/services/keycloak-multitenant.service.ts index 9d0bcae..12dec98 100644 --- a/src/services/keycloak-multitenant.service.ts +++ b/src/services/keycloak-multitenant.service.ts @@ -27,7 +27,7 @@ export class KeycloakMultiTenantService { * @param realm the realm to retrieve from * @returns the multi tenant keycloak instance */ - get(realm: string): KeycloakConnect.Keycloak { + async get(realm: string): Promise { if (this.instances.has(realm)) { return this.instances.get(realm); } else { @@ -36,11 +36,24 @@ export class KeycloakMultiTenantService { 'Keycloak configuration is a configuration path. This should not happen after module load.', ); } + if ( + this.keycloakOpts.multiTenant === null || + this.keycloakOpts.multiTenant === undefined + ) { + throw new Error( + 'Multi tenant is not defined yet multi tenant service is being called.', + ); + } // Resolve realm secret - const realmSecret = this.keycloakOpts.multiTenant?.realmSecretResolver?.( + const resolvedRealmSecret = this.keycloakOpts.multiTenant.realmSecretResolver( realm, ); + const realmSecret = + resolvedRealmSecret || resolvedRealmSecret instanceof Promise + ? await resolvedRealmSecret + : resolvedRealmSecret; + // Override secret // Order of priority: resolved realm secret > default global secret const secret = realmSecret || this.keycloakOpts.secret; diff --git a/src/util.ts b/src/util.ts index 074258f..29a8eaa 100644 --- a/src/util.ts +++ b/src/util.ts @@ -7,20 +7,22 @@ type GqlContextType = 'graphql' | ContextType; // Confusing and all, but I needed to extract this fn to avoid more repeating code // TODO: Rework in 2.0 -export const useKeycloak = ( +export const useKeycloak = async ( request: any, jwt: string, singleTenant: KeycloakConnect.Keycloak, multiTenant: KeycloakMultiTenantService, opts: KeycloakConnectConfig, -): KeycloakConnect.Keycloak => { +): Promise => { if (opts.multiTenant && opts.multiTenant.realmResolver) { const resolvedRealm = opts.multiTenant.realmResolver(request); - return multiTenant.get(resolvedRealm); + const realm = + resolvedRealm instanceof Promise ? await resolvedRealm : resolvedRealm; + return await multiTenant.get(realm); } else if (!opts.realm) { const payload = parseToken(jwt); const issuerRealm = payload.iss.split('/').pop(); - return multiTenant.get(issuerRealm); + return await multiTenant.get(issuerRealm); } return singleTenant; };