@clemax/nest-keycloak-connect 2.1.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/constants.d.ts +69 -0
  2. package/dist/constants.js +76 -0
  3. package/dist/decorators/access-token.decorator.d.ts +5 -0
  4. package/dist/decorators/access-token.decorator.js +13 -0
  5. package/dist/decorators/enforcer-options.decorator.d.ts +8 -0
  6. package/dist/decorators/enforcer-options.decorator.js +12 -0
  7. package/dist/decorators/keycloak-user.decorator.d.ts +5 -0
  8. package/dist/decorators/keycloak-user.decorator.js +13 -0
  9. package/dist/decorators/public.decorator.d.ts +7 -0
  10. package/dist/decorators/public.decorator.js +12 -0
  11. package/dist/decorators/resource.decorator.d.ts +6 -0
  12. package/dist/decorators/resource.decorator.js +11 -0
  13. package/dist/decorators/roles.decorator.d.ts +10 -0
  14. package/dist/decorators/roles.decorator.js +15 -0
  15. package/dist/decorators/scopes.decorator.d.ts +19 -0
  16. package/dist/decorators/scopes.decorator.js +28 -0
  17. package/dist/guards/auth.guard.d.ts +19 -0
  18. package/dist/guards/auth.guard.js +172 -0
  19. package/dist/guards/resource.guard.d.ts +18 -0
  20. package/dist/guards/resource.guard.js +175 -0
  21. package/dist/guards/role.guard.d.ts +17 -0
  22. package/dist/guards/role.guard.js +139 -0
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.js +18 -0
  25. package/dist/interface/keycloak-connect-module-async-options.interface.d.ts +9 -0
  26. package/dist/interface/keycloak-connect-module-async-options.interface.js +2 -0
  27. package/dist/interface/keycloak-connect-options-factory.interface.d.ts +4 -0
  28. package/dist/interface/keycloak-connect-options-factory.interface.js +2 -0
  29. package/dist/interface/keycloak-connect-options.interface.d.ts +170 -0
  30. package/dist/interface/keycloak-connect-options.interface.js +3 -0
  31. package/dist/internal.util.d.ts +8 -0
  32. package/dist/internal.util.js +69 -0
  33. package/dist/keycloak-connect.module.d.ts +32 -0
  34. package/dist/keycloak-connect.module.js +126 -0
  35. package/dist/keycloak-connect.providers.d.ts +7 -0
  36. package/dist/keycloak-connect.providers.js +81 -0
  37. package/dist/services/keycloak-multitenant.service.d.ts +24 -0
  38. package/dist/services/keycloak-multitenant.service.js +174 -0
  39. package/dist/util.d.ts +1 -0
  40. package/dist/util.js +8 -0
  41. package/package.json +13 -11
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
22
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
23
+ };
24
+ var __importStar = (this && this.__importStar) || function (mod) {
25
+ if (mod && mod.__esModule) return mod;
26
+ var result = {};
27
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
28
+ __setModuleDefault(result, mod);
29
+ return result;
30
+ };
31
+ var __metadata = (this && this.__metadata) || function (k, v) {
32
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
33
+ };
34
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
35
+ return function (target, key) { decorator(target, key, paramIndex); }
36
+ };
37
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
38
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
39
+ return new (P || (P = Promise))(function (resolve, reject) {
40
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
41
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
42
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
43
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
44
+ });
45
+ };
46
+ var ResourceGuard_1;
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.ResourceGuard = void 0;
49
+ const common_1 = require("@nestjs/common");
50
+ const core_1 = require("@nestjs/core");
51
+ const KeycloakConnect = __importStar(require("keycloak-connect"));
52
+ const constants_1 = require("../constants");
53
+ const enforcer_options_decorator_1 = require("../decorators/enforcer-options.decorator");
54
+ const public_decorator_1 = require("../decorators/public.decorator");
55
+ const resource_decorator_1 = require("../decorators/resource.decorator");
56
+ const scopes_decorator_1 = require("../decorators/scopes.decorator");
57
+ const internal_util_1 = require("../internal.util");
58
+ const keycloak_multitenant_service_1 = require("../services/keycloak-multitenant.service");
59
+ /**
60
+ * This adds a resource guard, which is policy enforcement by default is permissive.
61
+ * Only controllers annotated with `@Resource` and methods with `@Scopes`
62
+ * are handled by this guard.
63
+ */
64
+ let ResourceGuard = ResourceGuard_1 = class ResourceGuard {
65
+ constructor(singleTenant, keycloakOpts, multiTenant) {
66
+ this.singleTenant = singleTenant;
67
+ this.keycloakOpts = keycloakOpts;
68
+ this.multiTenant = multiTenant;
69
+ this.logger = new common_1.Logger(ResourceGuard_1.name);
70
+ this.reflector = new core_1.Reflector();
71
+ }
72
+ canActivate(context) {
73
+ return __awaiter(this, void 0, void 0, function* () {
74
+ var _a, _b, _c, _d;
75
+ const defaultEnforcerOpts = {
76
+ claims: (request) => {
77
+ const httpUri = request.url;
78
+ const userAgent = request.headers['user-agent'];
79
+ this.logger.verbose(`Enforcing claims, http.uri: ${httpUri}, user.agent: ${userAgent}`);
80
+ return {
81
+ 'http.uri': [httpUri],
82
+ 'user.agent': [userAgent],
83
+ };
84
+ },
85
+ };
86
+ const resource = this.reflector.get(resource_decorator_1.META_RESOURCE, context.getClass());
87
+ const explicitScopes = (_a = this.reflector.get(scopes_decorator_1.META_SCOPES, context.getHandler())) !== null && _a !== void 0 ? _a : [];
88
+ const conditionalScopes = this.reflector.get(scopes_decorator_1.META_CONDITIONAL_SCOPES, context.getHandler());
89
+ const isPublic = this.reflector.getAllAndOverride(public_decorator_1.META_PUBLIC, [
90
+ context.getClass(),
91
+ context.getHandler(),
92
+ ]);
93
+ const enforcerOpts = (_b = this.reflector.getAllAndOverride(enforcer_options_decorator_1.META_ENFORCER_OPTIONS, [context.getClass(), context.getHandler()])) !== null && _b !== void 0 ? _b : defaultEnforcerOpts;
94
+ // Default to permissive
95
+ const policyEnforcementMode = this.keycloakOpts.policyEnforcement || constants_1.PolicyEnforcementMode.PERMISSIVE;
96
+ const shouldAllow = policyEnforcementMode === constants_1.PolicyEnforcementMode.PERMISSIVE;
97
+ // Extract request/response
98
+ const cookieKey = this.keycloakOpts.cookieKey || constants_1.KEYCLOAK_COOKIE_DEFAULT;
99
+ const [request, response] = (0, internal_util_1.extractRequestAndAttachCookie)(context, cookieKey);
100
+ // if is not an HTTP request ignore this guard
101
+ if (!request) {
102
+ return true;
103
+ }
104
+ if (!request.user && isPublic) {
105
+ this.logger.verbose(`Route has no user, and is public, allowed`);
106
+ return true;
107
+ }
108
+ const keycloak = yield (0, internal_util_1.useKeycloak)(request, request.accessToken, this.singleTenant, this.multiTenant, this.keycloakOpts);
109
+ const grant = yield keycloak.grantManager.createGrant({
110
+ access_token: request.accessToken,
111
+ });
112
+ // No resource given, check policy enforcement mode
113
+ if (!resource) {
114
+ if (shouldAllow) {
115
+ this.logger.verbose(`Controller has no @Resource defined, request allowed due to policy enforcement`);
116
+ }
117
+ else {
118
+ this.logger.verbose(`Controller has no @Resource defined, request denied due to policy enforcement`);
119
+ }
120
+ return shouldAllow;
121
+ }
122
+ // Build the required scopes
123
+ const conditionalScopesResult = conditionalScopes != null || conditionalScopes != undefined
124
+ ? conditionalScopes(request, grant.access_token)
125
+ : [];
126
+ const scopes = [...explicitScopes, ...conditionalScopesResult];
127
+ // Attach resolved scopes
128
+ request.scopes = scopes;
129
+ // No scopes given, check policy enforcement mode
130
+ if (!scopes || scopes.length === 0) {
131
+ if (shouldAllow) {
132
+ this.logger.verbose(`Route has no @Scope/@ConditionalScopes defined, request allowed due to policy enforcement`);
133
+ }
134
+ else {
135
+ this.logger.verbose(`Route has no @Scope/@ConditionalScopes defined, request denied due to policy enforcement`);
136
+ }
137
+ return shouldAllow;
138
+ }
139
+ this.logger.verbose(`Protecting resource [ ${resource} ] with scopes: [ ${scopes} ]`);
140
+ const user = (_d = (_c = request.user) === null || _c === void 0 ? void 0 : _c.preferred_username) !== null && _d !== void 0 ? _d : 'user';
141
+ const enforcerFn = createEnforcerContext(request, response, enforcerOpts);
142
+ // Build permissions
143
+ const permissions = scopes.map((scope) => `${resource}:${scope}`);
144
+ const isAllowed = yield enforcerFn(keycloak, permissions);
145
+ // If statement for verbose logging only
146
+ if (!isAllowed) {
147
+ this.logger.verbose(`Resource [ ${resource} ] denied to [ ${user} ]`);
148
+ }
149
+ else {
150
+ this.logger.verbose(`Resource [ ${resource} ] granted to [ ${user} ]`);
151
+ }
152
+ return isAllowed;
153
+ });
154
+ }
155
+ };
156
+ exports.ResourceGuard = ResourceGuard;
157
+ exports.ResourceGuard = ResourceGuard = ResourceGuard_1 = __decorate([
158
+ (0, common_1.Injectable)(),
159
+ __param(0, (0, common_1.Inject)(constants_1.KEYCLOAK_INSTANCE)),
160
+ __param(1, (0, common_1.Inject)(constants_1.KEYCLOAK_CONNECT_OPTIONS)),
161
+ __param(2, (0, common_1.Inject)(constants_1.KEYCLOAK_MULTITENANT_SERVICE)),
162
+ __metadata("design:paramtypes", [Object, Object, keycloak_multitenant_service_1.KeycloakMultiTenantService])
163
+ ], ResourceGuard);
164
+ const createEnforcerContext = (request, response, options) => (keycloak, permissions) =>
165
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
166
+ new Promise((resolve, _) =>
167
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
168
+ keycloak.enforcer(permissions, options)(request, response, (_) => {
169
+ if (request.resourceDenied) {
170
+ resolve(false);
171
+ }
172
+ else {
173
+ resolve(true);
174
+ }
175
+ }));
@@ -0,0 +1,17 @@
1
+ import { CanActivate, ExecutionContext } from '@nestjs/common';
2
+ import * as KeycloakConnect from 'keycloak-connect';
3
+ import { KeycloakConnectConfig } from '../interface/keycloak-connect-options.interface';
4
+ import { KeycloakMultiTenantService } from '../services/keycloak-multitenant.service';
5
+ /**
6
+ * A permissive type of role guard. Roles are set via `@Roles` decorator.
7
+ * @since 1.1.0
8
+ */
9
+ export declare class RoleGuard implements CanActivate {
10
+ private singleTenant;
11
+ private keycloakOpts;
12
+ private multiTenant;
13
+ private readonly logger;
14
+ private readonly reflector;
15
+ constructor(singleTenant: KeycloakConnect.Keycloak, keycloakOpts: KeycloakConnectConfig, multiTenant: KeycloakMultiTenantService);
16
+ canActivate(context: ExecutionContext): Promise<boolean>;
17
+ }
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
22
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
23
+ };
24
+ var __importStar = (this && this.__importStar) || function (mod) {
25
+ if (mod && mod.__esModule) return mod;
26
+ var result = {};
27
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
28
+ __setModuleDefault(result, mod);
29
+ return result;
30
+ };
31
+ var __metadata = (this && this.__metadata) || function (k, v) {
32
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
33
+ };
34
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
35
+ return function (target, key) { decorator(target, key, paramIndex); }
36
+ };
37
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
38
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
39
+ return new (P || (P = Promise))(function (resolve, reject) {
40
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
41
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
42
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
43
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
44
+ });
45
+ };
46
+ var RoleGuard_1;
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.RoleGuard = void 0;
49
+ const common_1 = require("@nestjs/common");
50
+ const core_1 = require("@nestjs/core");
51
+ const KeycloakConnect = __importStar(require("keycloak-connect"));
52
+ const constants_1 = require("../constants");
53
+ const roles_decorator_1 = require("../decorators/roles.decorator");
54
+ const internal_util_1 = require("../internal.util");
55
+ const keycloak_multitenant_service_1 = require("../services/keycloak-multitenant.service");
56
+ /**
57
+ * A permissive type of role guard. Roles are set via `@Roles` decorator.
58
+ * @since 1.1.0
59
+ */
60
+ let RoleGuard = RoleGuard_1 = class RoleGuard {
61
+ constructor(singleTenant, keycloakOpts, multiTenant) {
62
+ this.singleTenant = singleTenant;
63
+ this.keycloakOpts = keycloakOpts;
64
+ this.multiTenant = multiTenant;
65
+ this.logger = new common_1.Logger(RoleGuard_1.name);
66
+ this.reflector = new core_1.Reflector();
67
+ }
68
+ canActivate(context) {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ const roleMerge = this.keycloakOpts.roleMerge
71
+ ? this.keycloakOpts.roleMerge
72
+ : constants_1.RoleMerge.OVERRIDE;
73
+ const roles = [];
74
+ const matchingMode = this.reflector.getAllAndOverride(roles_decorator_1.META_ROLE_MATCHING_MODE, [context.getClass(), context.getHandler()]);
75
+ if (roleMerge == constants_1.RoleMerge.ALL) {
76
+ const mergedRoles = this.reflector.getAllAndMerge(roles_decorator_1.META_ROLES, [
77
+ context.getClass(),
78
+ context.getHandler(),
79
+ ]);
80
+ if (mergedRoles) {
81
+ roles.push(...mergedRoles);
82
+ }
83
+ }
84
+ else if (roleMerge == constants_1.RoleMerge.OVERRIDE) {
85
+ const resultRoles = this.reflector.getAllAndOverride(roles_decorator_1.META_ROLES, [context.getClass(), context.getHandler()]);
86
+ if (resultRoles) {
87
+ roles.push(...resultRoles);
88
+ }
89
+ }
90
+ else {
91
+ throw Error(`Unknown role merge: ${roleMerge}`);
92
+ }
93
+ if (roles.length === 0) {
94
+ return true;
95
+ }
96
+ const roleMatchingMode = matchingMode !== null && matchingMode !== void 0 ? matchingMode : constants_1.RoleMatch.ANY;
97
+ this.logger.verbose(`Using matching mode: ${roleMatchingMode}`, { roles });
98
+ // Extract request
99
+ const cookieKey = this.keycloakOpts.cookieKey || constants_1.KEYCLOAK_COOKIE_DEFAULT;
100
+ const [request] = (0, internal_util_1.extractRequestAndAttachCookie)(context, cookieKey);
101
+ const { accessToken } = request;
102
+ // if is not an HTTP request ignore this guard
103
+ if (!request) {
104
+ return true;
105
+ }
106
+ if (!accessToken) {
107
+ // No access token attached, auth guard should have attached the necessary token
108
+ this.logger.warn('No access token found in request, are you sure AuthGuard is first in the chain?');
109
+ return false;
110
+ }
111
+ // Create grant
112
+ const keycloak = yield (0, internal_util_1.useKeycloak)(request, request.accessToken, this.singleTenant, this.multiTenant, this.keycloakOpts);
113
+ const grant = yield keycloak.grantManager.createGrant({
114
+ access_token: accessToken,
115
+ });
116
+ // Grab access token from grant
117
+ const grantAccessToken = grant.access_token;
118
+ // For verbose logging, we store it instead of returning it immediately
119
+ const granted = roleMatchingMode === constants_1.RoleMatch.ANY
120
+ ? roles.some((r) => grantAccessToken.hasRole(r))
121
+ : roles.every((r) => grantAccessToken.hasRole(r));
122
+ if (granted) {
123
+ this.logger.verbose(`Resource granted due to role(s)`);
124
+ }
125
+ else {
126
+ this.logger.verbose(`Resource denied due to mismatched role(s)`);
127
+ }
128
+ return granted;
129
+ });
130
+ }
131
+ };
132
+ exports.RoleGuard = RoleGuard;
133
+ exports.RoleGuard = RoleGuard = RoleGuard_1 = __decorate([
134
+ (0, common_1.Injectable)(),
135
+ __param(0, (0, common_1.Inject)(constants_1.KEYCLOAK_INSTANCE)),
136
+ __param(1, (0, common_1.Inject)(constants_1.KEYCLOAK_CONNECT_OPTIONS)),
137
+ __param(2, (0, common_1.Inject)(constants_1.KEYCLOAK_MULTITENANT_SERVICE)),
138
+ __metadata("design:paramtypes", [Object, Object, keycloak_multitenant_service_1.KeycloakMultiTenantService])
139
+ ], RoleGuard);
@@ -0,0 +1 @@
1
+ export * from './keycloak-connect.module';
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ // public-api
18
+ __exportStar(require("./keycloak-connect.module"), exports);
@@ -0,0 +1,9 @@
1
+ import { ModuleMetadata, Type } from '@nestjs/common/interfaces';
2
+ import { KeycloakConnectOptionsFactory } from './keycloak-connect-options-factory.interface';
3
+ import { KeycloakConnectOptions } from './keycloak-connect-options.interface';
4
+ export interface KeycloakConnectModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
5
+ inject?: any[];
6
+ useExisting?: Type<KeycloakConnectOptionsFactory>;
7
+ useClass?: Type<KeycloakConnectOptionsFactory>;
8
+ useFactory?: (...args: any[]) => Promise<KeycloakConnectOptions> | KeycloakConnectOptions;
9
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,4 @@
1
+ import { KeycloakConnectOptions } from './keycloak-connect-options.interface';
2
+ export interface KeycloakConnectOptionsFactory {
3
+ createKeycloakConnectOptions(): Promise<KeycloakConnectOptions> | KeycloakConnectOptions;
4
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,170 @@
1
+ import { PolicyEnforcementMode, RoleMerge, TokenValidation } from '../constants';
2
+ export type KeycloakConnectOptions = string | KeycloakConnectConfig;
3
+ /**
4
+ * Multi tenant configuration.
5
+ */
6
+ export interface MultiTenantOptions {
7
+ /**
8
+ * Option to always resolve the realm and secret. Disabled by default.
9
+ */
10
+ resolveAlways?: boolean;
11
+ /**
12
+ * The realm resolver function.
13
+ */
14
+ realmResolver: (request: any) => Promise<string> | string;
15
+ /**
16
+ * The realm secret resolver function.
17
+ */
18
+ realmSecretResolver?: (realm: string, request?: any) => Promise<string> | string;
19
+ /**
20
+ * The realm auth server url resolver function.
21
+ */
22
+ realmAuthServerUrlResolver?: (realm: string, request?: any) => Promise<string> | string;
23
+ /**
24
+ * The realm client id resolver function.
25
+ */
26
+ realmClientIdResolver: (realm: string, request?: any) => Promise<string> | string;
27
+ }
28
+ /**
29
+ * Library only configuration.
30
+ */
31
+ export interface NestKeycloakConfig {
32
+ /**
33
+ * Cookie key.
34
+ */
35
+ cookieKey?: string;
36
+ /**
37
+ * Sets the policy enforcement mode for this adapter, defaults to {@link PolicyEnforcementMode.PERMISSIVE}.
38
+ */
39
+ policyEnforcement?: PolicyEnforcementMode;
40
+ /**
41
+ * Sets the token validation method, defaults to {@link TokenValidation.ONLINE}.
42
+ */
43
+ tokenValidation?: TokenValidation;
44
+ /**
45
+ * Multi tenant options.
46
+ */
47
+ multiTenant?: MultiTenantOptions;
48
+ /**
49
+ * Role merging options.
50
+ */
51
+ roleMerge?: RoleMerge;
52
+ }
53
+ /**
54
+ * Keycloak Connect options.
55
+ * @see {@link https://github.com/keycloak/keycloak-nodejs-connect/blob/f8e011aea5/middleware/auth-utils/config.js}
56
+ */
57
+ export interface KeycloakConnectConfig extends NestKeycloakConfig {
58
+ /**
59
+ * Realm ID.
60
+ */
61
+ realm?: string;
62
+ /**
63
+ * Client/Application ID.
64
+ */
65
+ resource?: string;
66
+ /**
67
+ * Client/Application ID.
68
+ * @see {KeycloakConnectOptions#resource}
69
+ */
70
+ 'client-id'?: string;
71
+ /**
72
+ * Client/Application ID.
73
+ * @see {KeycloakConnectOptions#resource}
74
+ */
75
+ clientId?: string;
76
+ /**
77
+ * Client/Application secret.
78
+ */
79
+ credentials?: KeycloakCredentials;
80
+ /**
81
+ * Client/Application secret.
82
+ * @see {KeycloakCredentials#secret}
83
+ */
84
+ secret: string;
85
+ /**
86
+ * If this is a public application or confidential.
87
+ * @see {KeycloakConnectOptions#public}
88
+ */
89
+ 'public-client'?: boolean;
90
+ /**
91
+ * If this is a public application or confidential.
92
+ */
93
+ public?: boolean;
94
+ /**
95
+ * Authentication server URL.
96
+ * @see {KeycloakConnectOptions#authServerUrl}
97
+ */
98
+ 'auth-server-url'?: string;
99
+ /**
100
+ * Authentication server URL.
101
+ * @see {KeycloakConnectOptions#authServerUrl}
102
+ */
103
+ 'server-url'?: string;
104
+ /**
105
+ * Authentication server URL.
106
+ * @see {KeycloakConnectOptions#authServerUrl}
107
+ */
108
+ serverUrl?: string;
109
+ /**
110
+ * Authentication server URL.
111
+ */
112
+ authServerUrl?: string;
113
+ /**
114
+ * How many minutes before retrying getting the keys.
115
+ * @see {KeycloakConnectOptions#minTimeBetweenJwksRequests}
116
+ */
117
+ 'min-time-between-jwks-requests'?: number;
118
+ /**
119
+ * How many minutes before retrying getting the keys.
120
+ */
121
+ minTimeBetweenJwksRequests?: number;
122
+ /**
123
+ * If this is a Bearer Only application.
124
+ * @see {KeycloakConnectOptions#bearerOnly}
125
+ */
126
+ 'bearer-only'?: boolean;
127
+ /**
128
+ * If this is a Bearer Only application.
129
+ */
130
+ bearerOnly?: boolean;
131
+ /**
132
+ * Formatted public-key.
133
+ * @see {KeycloakConnectOptions#realmPublicKey}
134
+ */
135
+ 'realm-public-key'?: string;
136
+ /**
137
+ * Formatted public-key.
138
+ */
139
+ realmPublicKey?: string;
140
+ /**
141
+ * Verify token audience.
142
+ * @see {KeycloakConnectOptions#verifyTokenAudience}
143
+ */
144
+ 'verify-token-audience'?: boolean;
145
+ /**
146
+ * Verify token audience.
147
+ */
148
+ verifyTokenAudience?: boolean;
149
+ /**
150
+ * Confidential port.
151
+ */
152
+ 'confidential-port'?: string | number;
153
+ /**
154
+ * Require SSL.
155
+ */
156
+ 'ssl-required'?: string;
157
+ /**
158
+ * Allowed issuers.
159
+ */
160
+ 'allowed-issuers'?: string[] | string;
161
+ }
162
+ /**
163
+ * Represents Keycloak credentials.
164
+ */
165
+ export interface KeycloakCredentials {
166
+ /**
167
+ * Client/Application secret.
168
+ */
169
+ secret: string;
170
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ // The typings are a bit of a mess, I'm sure there's a better way to do this.
3
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,8 @@
1
+ import { ExecutionContext } from '@nestjs/common';
2
+ import KeycloakConnect from 'keycloak-connect';
3
+ import { KeycloakConnectConfig } from './interface/keycloak-connect-options.interface';
4
+ import { KeycloakMultiTenantService } from './services/keycloak-multitenant.service';
5
+ export declare const useKeycloak: (request: any, jwt: string, singleTenant: KeycloakConnect.Keycloak, multiTenant: KeycloakMultiTenantService, opts: KeycloakConnectConfig) => Promise<KeycloakConnect.Keycloak>;
6
+ export declare const attachCookieToHeader: (request: any, cookieKey: string) => any;
7
+ export declare const extractRequest: (context: ExecutionContext) => [any, any];
8
+ export declare const extractRequestAndAttachCookie: (context: ExecutionContext, cookieKey: string) => any[];
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.extractRequestAndAttachCookie = exports.extractRequest = exports.attachCookieToHeader = exports.useKeycloak = void 0;
13
+ const util_1 = require("./util");
14
+ // Confusing and all, but I needed to extract this fn to avoid more repeating code
15
+ // TODO: Rework in 2.0
16
+ const useKeycloak = (request, jwt, singleTenant, multiTenant, opts) => __awaiter(void 0, void 0, void 0, function* () {
17
+ if (opts.multiTenant && opts.multiTenant.realmResolver) {
18
+ const resolvedRealm = opts.multiTenant.realmResolver(request);
19
+ const realm = resolvedRealm instanceof Promise ? yield resolvedRealm : resolvedRealm;
20
+ return yield multiTenant.get(realm, request);
21
+ }
22
+ else if (!opts.realm) {
23
+ const payload = (0, util_1.parseToken)(jwt);
24
+ const issuerRealm = payload.iss.split('/').pop();
25
+ return yield multiTenant.get(issuerRealm, request);
26
+ }
27
+ return singleTenant;
28
+ });
29
+ exports.useKeycloak = useKeycloak;
30
+ const attachCookieToHeader = (request, cookieKey) => {
31
+ // Attach cookie as authorization header
32
+ if (request && request.cookies && request.cookies[cookieKey]) {
33
+ request.headers.authorization = `Bearer ${request.cookies[cookieKey]}`;
34
+ }
35
+ return request;
36
+ };
37
+ exports.attachCookieToHeader = attachCookieToHeader;
38
+ const extractRequest = (context) => {
39
+ let request, response;
40
+ // Check if request is coming from graphql or http
41
+ if (context.getType() === 'http') {
42
+ // http request
43
+ const httpContext = context.switchToHttp();
44
+ request = httpContext.getRequest();
45
+ response = httpContext.getResponse();
46
+ }
47
+ else if (context.getType() === 'graphql') {
48
+ let gql;
49
+ // Check if graphql is installed
50
+ try {
51
+ gql = require('@nestjs/graphql');
52
+ }
53
+ catch (er) {
54
+ throw new Error('@nestjs/graphql is not installed, cannot proceed');
55
+ }
56
+ // graphql request
57
+ const gqlContext = gql.GqlExecutionContext.create(context).getContext();
58
+ request = gqlContext.req;
59
+ response = gqlContext.res;
60
+ }
61
+ return [request, response];
62
+ };
63
+ exports.extractRequest = extractRequest;
64
+ const extractRequestAndAttachCookie = (context, cookieKey) => {
65
+ const [tmpRequest, response] = (0, exports.extractRequest)(context);
66
+ const request = (0, exports.attachCookieToHeader)(tmpRequest, cookieKey);
67
+ return [request, response];
68
+ };
69
+ exports.extractRequestAndAttachCookie = extractRequestAndAttachCookie;
@@ -0,0 +1,32 @@
1
+ import { DynamicModule, Logger } from '@nestjs/common';
2
+ import { KeycloakConnectModuleAsyncOptions } from './interface/keycloak-connect-module-async-options.interface';
3
+ import { KeycloakConnectOptions, NestKeycloakConfig } from './interface/keycloak-connect-options.interface';
4
+ export * from './constants';
5
+ export * from './decorators/access-token.decorator';
6
+ export * from './decorators/enforcer-options.decorator';
7
+ export * from './decorators/keycloak-user.decorator';
8
+ export * from './decorators/public.decorator';
9
+ export * from './decorators/resource.decorator';
10
+ export * from './decorators/roles.decorator';
11
+ export * from './decorators/scopes.decorator';
12
+ export * from './guards/auth.guard';
13
+ export * from './guards/resource.guard';
14
+ export * from './guards/role.guard';
15
+ export * from './interface/keycloak-connect-module-async-options.interface';
16
+ export * from './interface/keycloak-connect-options-factory.interface';
17
+ export * from './interface/keycloak-connect-options.interface';
18
+ export * from './services/keycloak-multitenant.service';
19
+ export * from './util';
20
+ export declare class KeycloakConnectModule {
21
+ static logger: Logger;
22
+ /**
23
+ * Register the `KeycloakConnect` module.
24
+ * @param opts `keycloak.json` path in string or {@link NestKeycloakConfig} object.
25
+ * @param config {@link NestKeycloakConfig} when using `keycloak.json` path, optional
26
+ * @returns
27
+ */
28
+ static register(opts: KeycloakConnectOptions, config?: NestKeycloakConfig): DynamicModule;
29
+ static registerAsync(opts: KeycloakConnectModuleAsyncOptions): DynamicModule;
30
+ private static createAsyncProviders;
31
+ private static createAsyncOptionsProvider;
32
+ }