Skip to content

Commit

Permalink
Support async for multi tenant resolvers, fixes #125
Browse files Browse the repository at this point in the history
  • Loading branch information
ferrerojosh committed Apr 20, 2022
1 parent 252b8a0 commit 628c18e
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 20 deletions.
19 changes: 10 additions & 9 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/guards/auth.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/guards/resource.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/guards/role.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export class RoleGuard implements CanActivate {
}

// Create grant
const keycloak = useKeycloak(
const keycloak = await useKeycloak(
request,
request.accessTokenJWT,
this.singleTenant,
Expand Down
4 changes: 2 additions & 2 deletions src/interface/keycloak-connect-options.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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> | string;
realmSecretResolver?: (realm: string) => Promise<string> | string;
}

/**
Expand Down
17 changes: 15 additions & 2 deletions src/services/keycloak-multitenant.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<KeycloakConnect.Keycloak> {
if (this.instances.has(realm)) {
return this.instances.get(realm);
} else {
Expand All @@ -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;
Expand Down
10 changes: 6 additions & 4 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<KeycloakConnect.Keycloak> => {
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;
};
Expand Down

0 comments on commit 628c18e

Please sign in to comment.