@cifra-x/nest-keycloak 0.0.2

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 (73) hide show
  1. package/README.md +45 -0
  2. package/dist/src/async-class-provider.type-guard.d.ts +9 -0
  3. package/dist/src/async-class-provider.type-guard.js +6 -0
  4. package/dist/src/common/interfaces/authentication-request.interface.d.ts +11 -0
  5. package/dist/src/common/interfaces/authentication-request.interface.js +2 -0
  6. package/dist/src/common/interfaces/jwt-payload.interface.d.ts +12 -0
  7. package/dist/src/common/interfaces/jwt-payload.interface.js +2 -0
  8. package/dist/src/common/interfaces/token.interface.d.ts +2 -0
  9. package/dist/src/common/interfaces/token.interface.js +2 -0
  10. package/dist/src/common/utils/extract-request.util.d.ts +2 -0
  11. package/dist/src/common/utils/extract-request.util.js +27 -0
  12. package/dist/src/common/utils/extract-request.util.spec.d.ts +1 -0
  13. package/dist/src/common/utils/extract-request.util.spec.js +64 -0
  14. package/dist/src/common/utils/get-keycloak.util.d.ts +4 -0
  15. package/dist/src/common/utils/get-keycloak.util.js +25 -0
  16. package/dist/src/common/utils/get-keycloak.util.spec.d.ts +1 -0
  17. package/dist/src/common/utils/get-keycloak.util.spec.js +55 -0
  18. package/dist/src/common/utils/parse-token.util.d.ts +2 -0
  19. package/dist/src/common/utils/parse-token.util.js +7 -0
  20. package/dist/src/common/utils/parse-token.util.spec.d.ts +1 -0
  21. package/dist/src/common/utils/parse-token.util.spec.js +19 -0
  22. package/dist/src/constants/index.d.ts +5 -0
  23. package/dist/src/constants/index.js +21 -0
  24. package/dist/src/constants/keycloak-module.constants.d.ts +20 -0
  25. package/dist/src/constants/keycloak-module.constants.js +23 -0
  26. package/dist/src/constants/policy-enforcement-mode.enum.d.ts +13 -0
  27. package/dist/src/constants/policy-enforcement-mode.enum.js +17 -0
  28. package/dist/src/constants/role-matching-mode.enum.d.ts +13 -0
  29. package/dist/src/constants/role-matching-mode.enum.js +17 -0
  30. package/dist/src/constants/role-merge.enum.d.ts +10 -0
  31. package/dist/src/constants/role-merge.enum.js +14 -0
  32. package/dist/src/constants/token-validation.enum.d.ts +17 -0
  33. package/dist/src/constants/token-validation.enum.js +21 -0
  34. package/dist/src/decorators/authenticated-user.decorator.d.ts +4 -0
  35. package/dist/src/decorators/authenticated-user.decorator.js +12 -0
  36. package/dist/src/decorators/authenticated-user.decorator.spec.d.ts +1 -0
  37. package/dist/src/decorators/authenticated-user.decorator.spec.js +28 -0
  38. package/dist/src/decorators/enforcer-options.decorator.d.ts +8 -0
  39. package/dist/src/decorators/enforcer-options.decorator.js +12 -0
  40. package/dist/src/decorators/public.decorator.d.ts +14 -0
  41. package/dist/src/decorators/public.decorator.js +20 -0
  42. package/dist/src/decorators/resource.decorator.d.ts +6 -0
  43. package/dist/src/decorators/resource.decorator.js +11 -0
  44. package/dist/src/decorators/roles.decorator.d.ts +8 -0
  45. package/dist/src/decorators/roles.decorator.js +12 -0
  46. package/dist/src/decorators/scopes.decorator.d.ts +6 -0
  47. package/dist/src/decorators/scopes.decorator.js +11 -0
  48. package/dist/src/guards/auth.guard.d.ts +21 -0
  49. package/dist/src/guards/auth.guard.js +179 -0
  50. package/dist/src/guards/resource.guard.d.ts +19 -0
  51. package/dist/src/guards/resource.guard.js +151 -0
  52. package/dist/src/guards/role.guard.d.ts +18 -0
  53. package/dist/src/guards/role.guard.js +140 -0
  54. package/dist/src/index.d.ts +1 -0
  55. package/dist/src/index.js +18 -0
  56. package/dist/src/interface/keycloak-connect-module-async-options.interface.d.ts +9 -0
  57. package/dist/src/interface/keycloak-connect-module-async-options.interface.js +2 -0
  58. package/dist/src/interface/keycloak-connect-options-factory.interface.d.ts +4 -0
  59. package/dist/src/interface/keycloak-connect-options-factory.interface.js +2 -0
  60. package/dist/src/interface/keycloak-connect-options.interface.d.ts +121 -0
  61. package/dist/src/interface/keycloak-connect-options.interface.js +2 -0
  62. package/dist/src/interface/role-decorator-options.interface.d.ts +11 -0
  63. package/dist/src/interface/role-decorator-options.interface.js +2 -0
  64. package/dist/src/keycloak-connect.module.d.ts +37 -0
  65. package/dist/src/keycloak-connect.module.js +142 -0
  66. package/dist/src/providers/keycloak.provider.d.ts +2 -0
  67. package/dist/src/providers/keycloak.provider.js +25 -0
  68. package/dist/src/providers/logger.provider.d.ts +2 -0
  69. package/dist/src/providers/logger.provider.js +20 -0
  70. package/dist/src/services/keycloak-multitenant.service.d.ts +23 -0
  71. package/dist/src/services/keycloak-multitenant.service.js +139 -0
  72. package/dist/tsconfig.tsbuildinfo +1 -0
  73. package/package.json +135 -0
package/README.md ADDED
@@ -0,0 +1,45 @@
1
+ <div align="center">
2
+
3
+ # Nest Keycloak Adapter
4
+
5
+ ### [keycloak-nodejs-connect](https://github.com/keycloak/keycloak-nodejs-connect) module for NestJS 11
6
+
7
+ *NestJS module adapter of keycloak-nodejs-connect library*
8
+
9
+ </div>
10
+
11
+ ## Decorators
12
+
13
+ | Decorator | Description |
14
+ | ------------------ | --------------------------------------------------------------------------------------------------------- |
15
+ | @AuthenticatedUser | Retrieves the current Keycloak logged-in user. (must be per method, unless controller is request scoped.) |
16
+ | @EnforcerOptions | Keycloak enforcer options. |
17
+ | @Public | Allow any user to use the route. |
18
+ | @Resource | Keycloak application resource name. |
19
+ | @Scopes | Keycloak application scope name. |
20
+ | @Roles | Keycloak realm/application roles. |
21
+
22
+ ### Nest Keycloak Options
23
+
24
+ | Option | Description | Required | Default |
25
+ | ----------------- | ------------------------------------------------------------------------ | -------- | ------------ |
26
+ | cookieKey | Cookie Key | no | KEYCLOAK_JWT |
27
+ | logLevels | Built-in logger level (deprecated, will be removed in 2.0) | no | log |
28
+ | useNestLogger | Use the nest logger (deprecated, will be removed in 2.0) | no | true |
29
+ | policyEnforcement | Sets the policy enforcement mode | no | PERMISSIVE |
30
+ | tokenValidation | Sets the token validation method | no | ONLINE |
31
+ | multiTenant | Sets the options for [multi-tenant configuration](#multi-tenant-options) | no | - |
32
+ | roleMerge | Sets the merge mode for @Role decorator | no | OVERRIDE |
33
+
34
+ ### Multi Tenant Options
35
+
36
+ | Option | Description | Required | Default |
37
+ | -------------------------- | ------------------------------------------------------------------------------------------------------- | -------- | ------- |
38
+ | resolveAlways | Option to always resolve the realm and secret. Disabled by default. | no | false |
39
+ | realmResolver | A function that passes a request (from respective platform i.e express or fastify) and returns a string | yes | - |
40
+ | realmSecretResolver | A function that passes the realm string, and an optional request and returns the secret string | no | - |
41
+ | realmAuthServerUrlResolver | A function that passes the realm string, and an optional request and returns the auth server url string | no | - |
42
+
43
+
44
+ ### TODO:
45
+ 1. keycloakProvider
@@ -0,0 +1,9 @@
1
+ import { Type } from '@nestjs/common/interfaces';
2
+ import { KeycloakConnectModuleAsyncOptions } from './interface/keycloak-connect-module-async-options.interface';
3
+ import { KeycloakConnectOptionsFactory } from './interface/keycloak-connect-options-factory.interface';
4
+ export declare function hasAsyncClassProvider(options: KeycloakConnectModuleAsyncOptions): options is KeycloakConnectModuleAsyncOptions & {
5
+ useExisting: Type<KeycloakConnectOptionsFactory>;
6
+ };
7
+ export declare function hasAsyncClassProvider(options: KeycloakConnectModuleAsyncOptions): options is KeycloakConnectModuleAsyncOptions & {
8
+ useClass: Type<KeycloakConnectOptionsFactory>;
9
+ };
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasAsyncClassProvider = hasAsyncClassProvider;
4
+ function hasAsyncClassProvider(options) {
5
+ return options.useExisting !== undefined || options.useClass !== undefined;
6
+ }
@@ -0,0 +1,11 @@
1
+ import { JwtPayload } from './jwt-payload.interface';
2
+ /**
3
+ * Расширение интерфейса Request полями аутентификации
4
+ */
5
+ export interface AuthenticationRequest {
6
+ user?: JwtPayload;
7
+ accessTokenJWT?: string;
8
+ headers?: Record<string, string | undefined>;
9
+ cookies?: Record<string, string | undefined>;
10
+ [key: string]: unknown;
11
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * JWT payload with common claims
3
+ */
4
+ export interface JwtPayload {
5
+ iss?: string;
6
+ sub?: string;
7
+ aud?: string | string[];
8
+ exp?: number;
9
+ iat?: number;
10
+ jti?: string;
11
+ [key: string]: unknown;
12
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ import KeycloakConnect from 'keycloak-connect';
2
+ export type KeycloakConnectToken = KeycloakConnect.Token;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ import { ExecutionContext } from '@nestjs/common';
2
+ export declare const extractRequest: <TRequest = unknown, TResponse = unknown>(context: ExecutionContext) => [TRequest, TResponse];
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractRequest = void 0;
4
+ let GqlExecutionContext = undefined;
5
+ try {
6
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
7
+ GqlExecutionContext = require('@nestjs/graphql').GqlExecutionContext;
8
+ }
9
+ catch {
10
+ // Пакет не установлен
11
+ }
12
+ const extractRequest = (context) => {
13
+ const contextType = context.getType();
14
+ if (contextType === 'http') {
15
+ const httpContext = context.switchToHttp();
16
+ return [httpContext.getRequest(), httpContext.getResponse()];
17
+ }
18
+ if (contextType === 'graphql') {
19
+ if (!GqlExecutionContext) {
20
+ throw new Error('@nestjs/graphql is not installed, cannot proceed');
21
+ }
22
+ const gqlContext = GqlExecutionContext.create(context).getContext();
23
+ return [gqlContext.req, gqlContext.res];
24
+ }
25
+ throw new Error('Unsupported context type');
26
+ };
27
+ exports.extractRequest = extractRequest;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const gqlContext = jest.fn();
4
+ jest.mock('@nestjs/graphql', () => {
5
+ const original = jest.requireActual('@nestjs/graphql');
6
+ return {
7
+ ...original,
8
+ GqlExecutionContext: {
9
+ create: jest.fn().mockImplementation(() => ({
10
+ getContext: gqlContext,
11
+ })),
12
+ },
13
+ };
14
+ });
15
+ const extract_request_util_1 = require("./extract-request.util");
16
+ describe('extractRequest', () => {
17
+ const context = {
18
+ getType: jest.fn(),
19
+ switchToHttp: jest.fn(),
20
+ switchToRpc: jest.fn(),
21
+ switchToWs: jest.fn(),
22
+ getArgs: jest.fn(),
23
+ getArgByIndex: jest.fn(),
24
+ getHandler: jest.fn(),
25
+ getClass: jest.fn(),
26
+ };
27
+ it('should return request and response form http context', () => {
28
+ context.getType.mockReturnValue('http');
29
+ context.switchToHttp.mockReturnValue({
30
+ getNext() {
31
+ return {};
32
+ },
33
+ getRequest: jest.fn().mockReturnValue({ _name: 'request' }),
34
+ getResponse: jest.fn().mockReturnValue({ _name: 'response' }),
35
+ });
36
+ const result = (0, extract_request_util_1.extractRequest)(context);
37
+ expect(result).toEqual([{ _name: 'request' }, { _name: 'response' }]);
38
+ });
39
+ it('should return request and response form graphql context', () => {
40
+ context.getType.mockReturnValue('graphql');
41
+ gqlContext.mockReturnValue({
42
+ req: { _name: 'gql-request' },
43
+ res: { _name: 'gql-response' },
44
+ });
45
+ const result = (0, extract_request_util_1.extractRequest)(context);
46
+ expect(result).toEqual([
47
+ { _name: 'gql-request' },
48
+ { _name: 'gql-response' },
49
+ ]);
50
+ });
51
+ it('should throw new Error when no graphql execution context', () => {
52
+ // 2. Сбрасываем кэш модулей, чтобы файл утилиты подгрузился заново
53
+ // jest.resetModules();
54
+ jest.doMock('@nestjs/graphql', () => ({
55
+ GqlExecutionContext: null,
56
+ }));
57
+ context.getType.mockReturnValue('graphql');
58
+ const result = (0, extract_request_util_1.extractRequest)(context);
59
+ expect(result).toEqual([
60
+ { _name: 'gql-request' },
61
+ { _name: 'gql-response' },
62
+ ]);
63
+ });
64
+ });
@@ -0,0 +1,4 @@
1
+ import KeycloakConnect from 'keycloak-connect';
2
+ import { KeycloakMultiTenantService } from '../../services/keycloak-multitenant.service';
3
+ import { KeycloakConnectOptions } from '../../interface/keycloak-connect-options.interface';
4
+ export declare const getKeycloakInstance: <TRequest = unknown>(request: TRequest, jwt: string, singleTenant: KeycloakConnect.Keycloak, multiTenant: KeycloakMultiTenantService, opts: KeycloakConnectOptions) => Promise<KeycloakConnect.Keycloak>;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getKeycloakInstance = void 0;
4
+ const parse_token_util_1 = require("./parse-token.util");
5
+ const getKeycloakInstance = async (request, jwt, singleTenant, multiTenant, opts) => {
6
+ // Multi-tenant с кастомным realmResolver
7
+ if (opts.multiTenant?.realmResolver) {
8
+ const realm = await opts.multiTenant.realmResolver(request);
9
+ return await multiTenant.get(realm, request);
10
+ }
11
+ // Multi-tenant без realmResolver — извлекаем realm из JWT
12
+ if (!opts.realm) {
13
+ const payload = (0, parse_token_util_1.parseToken)(jwt);
14
+ if (!payload.iss) {
15
+ throw new Error('JWT token does not contain an issuer (iss) claim');
16
+ }
17
+ const issuerRealm = payload.iss.split('/').pop();
18
+ if (!issuerRealm) {
19
+ throw new Error('No issuer realm');
20
+ }
21
+ return await multiTenant.get(issuerRealm, request);
22
+ }
23
+ return singleTenant;
24
+ };
25
+ exports.getKeycloakInstance = getKeycloakInstance;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const get_keycloak_util_1 = require("./get-keycloak.util");
4
+ describe('getKeycloak', () => {
5
+ it('should return keycloak instance single tenant', async () => {
6
+ const request = {};
7
+ const jwt = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IkxlbXJKamY4d1pVWGZ0Nld5cFdDbHY1cWswTlRCSXBxNUlMbF8wVmkwNjQifQ.eyJleHAiOjE3ODM2Njc1MTAsImlhdCI6MTc4MzY2NDQ2NCwianRpIjoib25ydGFjOmEyNmQ5NDNlLWY3ZWEtNmYzMC04MTNlLTVhYjE5NmQzZGM1NSIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA5MC9yZWFsbXMvZXN3aXBlIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjljNTYzNWU1LTk4NDAtNGRkMi1hNDI5LWE2M2M4OWExYmZjMSIsImFjciI6IjAiLCJzY29wZSI6Im9wZW5pZCJ9.B_IMLshPJEyH_vowwzbWPqTJ3w2T_a9U7JwSOwyPt9pzuMmsYyuJ_PLTfGjcPNDqLtsXjLV2hHUu6KdkVHRmNxqqSLYP61GcRF9yeiOcdH8dykGqK-ER_iUhwt24o_fNs0PXzAI8DaGdOQ9LIuHwYtU6B8jUIGa4g13N1pr7fiqSNQIc1J18OPZu34zSx-oOVZhCLT4xhBe6Sphi4IDO9_DWoMl91olFtQZnp_KZ9de3q_S1f_OdA1ypZYgPu2N0k_mz2AKMJxs5byBTAapLxL-6TVPVkeSn6I_i2VUoSte4Ns1Uc4BrFCkVImnav_tyzpB9t8ZSulseNqM0a_543Q';
8
+ const singleTenant = {};
9
+ const multiTenant = {};
10
+ const opts = {
11
+ authServerUrl: '',
12
+ realm: 'test-realm',
13
+ secret: '',
14
+ };
15
+ const keycloak = await (0, get_keycloak_util_1.getKeycloakInstance)(request, jwt, singleTenant, multiTenant, opts);
16
+ expect(keycloak).toEqual(singleTenant);
17
+ });
18
+ it('should return keycloak instance multi tenant', async () => {
19
+ const request = {};
20
+ const jwt = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IkxlbXJKamY4d1pVWGZ0Nld5cFdDbHY1cWswTlRCSXBxNUlMbF8wVmkwNjQifQ.eyJleHAiOjE3ODM2Njc1MTAsImlhdCI6MTc4MzY2NDQ2NCwianRpIjoib25ydGFjOmEyNmQ5NDNlLWY3ZWEtNmYzMC04MTNlLTVhYjE5NmQzZGM1NSIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA5MC9yZWFsbXMvZXN3aXBlIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjljNTYzNWU1LTk4NDAtNGRkMi1hNDI5LWE2M2M4OWExYmZjMSIsImFjciI6IjAiLCJzY29wZSI6Im9wZW5pZCJ9.B_IMLshPJEyH_vowwzbWPqTJ3w2T_a9U7JwSOwyPt9pzuMmsYyuJ_PLTfGjcPNDqLtsXjLV2hHUu6KdkVHRmNxqqSLYP61GcRF9yeiOcdH8dykGqK-ER_iUhwt24o_fNs0PXzAI8DaGdOQ9LIuHwYtU6B8jUIGa4g13N1pr7fiqSNQIc1J18OPZu34zSx-oOVZhCLT4xhBe6Sphi4IDO9_DWoMl91olFtQZnp_KZ9de3q_S1f_OdA1ypZYgPu2N0k_mz2AKMJxs5byBTAapLxL-6TVPVkeSn6I_i2VUoSte4Ns1Uc4BrFCkVImnav_tyzpB9t8ZSulseNqM0a_543Q';
21
+ const singleTenant = {};
22
+ const multiTenantKeycloakItem = {
23
+ _name: 'multi',
24
+ };
25
+ const multiTenant = {
26
+ get: jest.fn().mockResolvedValue(multiTenantKeycloakItem),
27
+ };
28
+ const opts = {
29
+ authServerUrl: '',
30
+ multiTenant: {
31
+ realmResolver: () => '',
32
+ },
33
+ secret: '',
34
+ };
35
+ const keycloak = await (0, get_keycloak_util_1.getKeycloakInstance)(request, jwt, singleTenant, multiTenant, opts);
36
+ expect(keycloak).toEqual(multiTenantKeycloakItem);
37
+ });
38
+ it('should return keycloak instance multi tenant without realmResolver', async () => {
39
+ const request = {};
40
+ const jwt = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IkxlbXJKamY4d1pVWGZ0Nld5cFdDbHY1cWswTlRCSXBxNUlMbF8wVmkwNjQifQ.eyJleHAiOjE3ODM2Njc1MTAsImlhdCI6MTc4MzY2NDQ2NCwianRpIjoib25ydGFjOmEyNmQ5NDNlLWY3ZWEtNmYzMC04MTNlLTVhYjE5NmQzZGM1NSIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA5MC9yZWFsbXMvZXN3aXBlIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjljNTYzNWU1LTk4NDAtNGRkMi1hNDI5LWE2M2M4OWExYmZjMSIsImFjciI6IjAiLCJzY29wZSI6Im9wZW5pZCJ9.B_IMLshPJEyH_vowwzbWPqTJ3w2T_a9U7JwSOwyPt9pzuMmsYyuJ_PLTfGjcPNDqLtsXjLV2hHUu6KdkVHRmNxqqSLYP61GcRF9yeiOcdH8dykGqK-ER_iUhwt24o_fNs0PXzAI8DaGdOQ9LIuHwYtU6B8jUIGa4g13N1pr7fiqSNQIc1J18OPZu34zSx-oOVZhCLT4xhBe6Sphi4IDO9_DWoMl91olFtQZnp_KZ9de3q_S1f_OdA1ypZYgPu2N0k_mz2AKMJxs5byBTAapLxL-6TVPVkeSn6I_i2VUoSte4Ns1Uc4BrFCkVImnav_tyzpB9t8ZSulseNqM0a_543Q';
41
+ const singleTenant = {};
42
+ const multiTenantKeycloakItem = {
43
+ _name: 'multi',
44
+ };
45
+ const multiTenant = {
46
+ get: jest.fn().mockResolvedValue(multiTenantKeycloakItem),
47
+ };
48
+ const opts = {
49
+ authServerUrl: '',
50
+ secret: '',
51
+ };
52
+ const keycloak = await (0, get_keycloak_util_1.getKeycloakInstance)(request, jwt, singleTenant, multiTenant, opts);
53
+ expect(keycloak).toEqual(multiTenantKeycloakItem);
54
+ });
55
+ });
@@ -0,0 +1,2 @@
1
+ import { JwtPayload } from '../interfaces/jwt-payload.interface';
2
+ export declare function parseToken<T extends JwtPayload = JwtPayload>(token: string): T;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseToken = parseToken;
4
+ function parseToken(token) {
5
+ const parts = token.split('.');
6
+ return JSON.parse(Buffer.from(parts[1], 'base64').toString());
7
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const parse_token_util_1 = require("./parse-token.util");
4
+ describe('parseToken', () => {
5
+ it('should parse token payload from string', () => {
6
+ const token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IkxlbXJKamY4d1pVWGZ0Nld5cFdDbHY1cWswTlRCSXBxNUlMbF8wVmkwNjQifQ.eyJleHAiOjE3ODM2Njc1MTAsImlhdCI6MTc4MzY2NDQ2NCwianRpIjoib25ydGFjOmEyNmQ5NDNlLWY3ZWEtNmYzMC04MTNlLTVhYjE5NmQzZGM1NSIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA5MC9yZWFsbXMvZXN3aXBlIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjljNTYzNWU1LTk4NDAtNGRkMi1hNDI5LWE2M2M4OWExYmZjMSIsImFjciI6IjAiLCJzY29wZSI6Im9wZW5pZCJ9.B_IMLshPJEyH_vowwzbWPqTJ3w2T_a9U7JwSOwyPt9pzuMmsYyuJ_PLTfGjcPNDqLtsXjLV2hHUu6KdkVHRmNxqqSLYP61GcRF9yeiOcdH8dykGqK-ER_iUhwt24o_fNs0PXzAI8DaGdOQ9LIuHwYtU6B8jUIGa4g13N1pr7fiqSNQIc1J18OPZu34zSx-oOVZhCLT4xhBe6Sphi4IDO9_DWoMl91olFtQZnp_KZ9de3q_S1f_OdA1ypZYgPu2N0k_mz2AKMJxs5byBTAapLxL-6TVPVkeSn6I_i2VUoSte4Ns1Uc4BrFCkVImnav_tyzpB9t8ZSulseNqM0a_543Q';
7
+ const payload = (0, parse_token_util_1.parseToken)(token);
8
+ expect(payload).toStrictEqual({
9
+ acr: '0',
10
+ aud: 'account',
11
+ exp: 1783667510,
12
+ iat: 1783664464,
13
+ iss: 'http://localhost:8090/realms/eswipe',
14
+ jti: 'onrtac:a26d943e-f7ea-6f30-813e-5ab196d3dc55',
15
+ scope: 'openid',
16
+ sub: '9c5635e5-9840-4dd2-a429-a63c89a1bfc1',
17
+ });
18
+ });
19
+ });
@@ -0,0 +1,5 @@
1
+ export * from './keycloak-module.constants';
2
+ export * from './policy-enforcement-mode.enum';
3
+ export * from './role-matching-mode.enum';
4
+ export * from './role-merge.enum';
5
+ export * from './token-validation.enum';
@@ -0,0 +1,21 @@
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
+ __exportStar(require("./keycloak-module.constants"), exports);
18
+ __exportStar(require("./policy-enforcement-mode.enum"), exports);
19
+ __exportStar(require("./role-matching-mode.enum"), exports);
20
+ __exportStar(require("./role-merge.enum"), exports);
21
+ __exportStar(require("./token-validation.enum"), exports);
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Used internally, provides keycloak options for the Nest guards.
3
+ */
4
+ export declare const KEYCLOAK_CONNECT_OPTIONS = "KEYCLOAK_CONNECT_OPTIONS";
5
+ /**
6
+ * Key for injecting a keycloak instance.
7
+ */
8
+ export declare const KEYCLOAK_INSTANCE = "KEYCLOAK_INSTANCE";
9
+ /**
10
+ * Key for injecting a keycloak multi tenant service.
11
+ */
12
+ export declare const KEYCLOAK_MULTITENANT_SERVICE = "KEYCLOAK_MULTITENANT_SERVICE";
13
+ /**
14
+ * Key for injecting the nest keycloak logger.
15
+ */
16
+ export declare const KEYCLOAK_LOGGER = "KEYCLOAK_LOGGER";
17
+ /**
18
+ * Default cookie key.
19
+ */
20
+ export declare const KEYCLOAK_COOKIE_DEFAULT = "KEYCLOAK_JWT";
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KEYCLOAK_COOKIE_DEFAULT = exports.KEYCLOAK_LOGGER = exports.KEYCLOAK_MULTITENANT_SERVICE = exports.KEYCLOAK_INSTANCE = exports.KEYCLOAK_CONNECT_OPTIONS = void 0;
4
+ /**
5
+ * Used internally, provides keycloak options for the Nest guards.
6
+ */
7
+ exports.KEYCLOAK_CONNECT_OPTIONS = 'KEYCLOAK_CONNECT_OPTIONS';
8
+ /**
9
+ * Key for injecting a keycloak instance.
10
+ */
11
+ exports.KEYCLOAK_INSTANCE = 'KEYCLOAK_INSTANCE';
12
+ /**
13
+ * Key for injecting a keycloak multi tenant service.
14
+ */
15
+ exports.KEYCLOAK_MULTITENANT_SERVICE = 'KEYCLOAK_MULTITENANT_SERVICE';
16
+ /**
17
+ * Key for injecting the nest keycloak logger.
18
+ */
19
+ exports.KEYCLOAK_LOGGER = 'KEYCLOAK_LOGGER';
20
+ /**
21
+ * Default cookie key.
22
+ */
23
+ exports.KEYCLOAK_COOKIE_DEFAULT = 'KEYCLOAK_JWT';
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Policy enforcement mode.
3
+ */
4
+ export declare enum PolicyEnforcementMode {
5
+ /**
6
+ * Deny all request when there is no matching resource.
7
+ */
8
+ ENFORCING = "enforcing",
9
+ /**
10
+ * Allow all request even when there's no matching resource.
11
+ */
12
+ PERMISSIVE = "permissive"
13
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PolicyEnforcementMode = void 0;
4
+ /**
5
+ * Policy enforcement mode.
6
+ */
7
+ var PolicyEnforcementMode;
8
+ (function (PolicyEnforcementMode) {
9
+ /**
10
+ * Deny all request when there is no matching resource.
11
+ */
12
+ PolicyEnforcementMode["ENFORCING"] = "enforcing";
13
+ /**
14
+ * Allow all request even when there's no matching resource.
15
+ */
16
+ PolicyEnforcementMode["PERMISSIVE"] = "permissive";
17
+ })(PolicyEnforcementMode || (exports.PolicyEnforcementMode = PolicyEnforcementMode = {}));
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Role matching mode.
3
+ */
4
+ export declare enum RoleMatchingMode {
5
+ /**
6
+ * Match all roles
7
+ */
8
+ ALL = "all",
9
+ /**
10
+ * Match any roles
11
+ */
12
+ ANY = "any"
13
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RoleMatchingMode = void 0;
4
+ /**
5
+ * Role matching mode.
6
+ */
7
+ var RoleMatchingMode;
8
+ (function (RoleMatchingMode) {
9
+ /**
10
+ * Match all roles
11
+ */
12
+ RoleMatchingMode["ALL"] = "all";
13
+ /**
14
+ * Match any roles
15
+ */
16
+ RoleMatchingMode["ANY"] = "any";
17
+ })(RoleMatchingMode || (exports.RoleMatchingMode = RoleMatchingMode = {}));
@@ -0,0 +1,10 @@
1
+ export declare enum RoleMerge {
2
+ /**
3
+ * Overrides roles if defined both controller and handlers, with controller taking over.
4
+ */
5
+ OVERRIDE = 0,
6
+ /**
7
+ * Merges all roles from both controller and handlers.
8
+ */
9
+ ALL = 1
10
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RoleMerge = void 0;
4
+ var RoleMerge;
5
+ (function (RoleMerge) {
6
+ /**
7
+ * Overrides roles if defined both controller and handlers, with controller taking over.
8
+ */
9
+ RoleMerge[RoleMerge["OVERRIDE"] = 0] = "OVERRIDE";
10
+ /**
11
+ * Merges all roles from both controller and handlers.
12
+ */
13
+ RoleMerge[RoleMerge["ALL"] = 1] = "ALL";
14
+ })(RoleMerge || (exports.RoleMerge = RoleMerge = {}));
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Token validation methods.
3
+ */
4
+ export declare enum TokenValidation {
5
+ /**
6
+ * The default validation method, performs live validation via Keycloak servers.
7
+ */
8
+ ONLINE = "online",
9
+ /**
10
+ * Validate offline against the configured keycloak options.
11
+ */
12
+ OFFLINE = "offline",
13
+ /**
14
+ * Does not check for any validation. Should only be used for special cases (i.e development, internal networks)
15
+ */
16
+ NONE = "none"
17
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TokenValidation = void 0;
4
+ /**
5
+ * Token validation methods.
6
+ */
7
+ var TokenValidation;
8
+ (function (TokenValidation) {
9
+ /**
10
+ * The default validation method, performs live validation via Keycloak servers.
11
+ */
12
+ TokenValidation["ONLINE"] = "online";
13
+ /**
14
+ * Validate offline against the configured keycloak options.
15
+ */
16
+ TokenValidation["OFFLINE"] = "offline";
17
+ /**
18
+ * Does not check for any validation. Should only be used for special cases (i.e development, internal networks)
19
+ */
20
+ TokenValidation["NONE"] = "none";
21
+ })(TokenValidation || (exports.TokenValidation = TokenValidation = {}));
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Retrieves the current Keycloak logged-in user
3
+ */
4
+ export declare const AuthenticatedUser: (...dataOrPipes: unknown[]) => ParameterDecorator;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuthenticatedUser = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ const extract_request_util_1 = require("../common/utils/extract-request.util");
6
+ /**
7
+ * Retrieves the current Keycloak logged-in user
8
+ */
9
+ exports.AuthenticatedUser = (0, common_1.createParamDecorator)((data, ctx) => {
10
+ const [req] = (0, extract_request_util_1.extractRequest)(ctx);
11
+ return req.user;
12
+ });
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const authenticated_user_decorator_1 = require("./authenticated-user.decorator");
4
+ const extract_request_util_1 = require("../common/utils/extract-request.util");
5
+ jest.mock('@common/utils/extract-request.util', () => ({
6
+ extractRequest: jest.fn(),
7
+ }));
8
+ // Мокаем createParamDecorator, чтобы он возвращал саму фабричную функцию
9
+ jest.mock('@nestjs/common', () => {
10
+ const originalModule = jest.requireActual('@nestjs/common');
11
+ return {
12
+ ...originalModule,
13
+ createParamDecorator: (factory) => factory,
14
+ };
15
+ });
16
+ describe('AuthenticatedUser decorator', () => {
17
+ it('should return user object', () => {
18
+ const context = {};
19
+ // Задаем возвращаемое значение для extractRequest
20
+ extract_request_util_1.extractRequest.mockReturnValue([
21
+ { user: { _name: 'user' } },
22
+ {},
23
+ ]);
24
+ // Благодаря моку createParamDecorator, AuthenticatedUser теперь является самой фабричной функцией
25
+ const result = (0, authenticated_user_decorator_1.AuthenticatedUser)({}, context);
26
+ expect(result).toEqual({ _name: 'user' });
27
+ });
28
+ });
@@ -0,0 +1,8 @@
1
+ import * as KeycloakConnect from 'keycloak-connect';
2
+ export declare const META_ENFORCER_OPTIONS = "enforcer-options";
3
+ /**
4
+ * Keycloak enforcer options
5
+ * @param opts - enforcer options
6
+ * @since 1.3.0
7
+ */
8
+ export declare const EnforcerOptions: (opts: KeycloakConnect.EnforcerOptions) => import("@nestjs/common").CustomDecorator<string>;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EnforcerOptions = exports.META_ENFORCER_OPTIONS = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ exports.META_ENFORCER_OPTIONS = 'enforcer-options';
6
+ /**
7
+ * Keycloak enforcer options
8
+ * @param opts - enforcer options
9
+ * @since 1.3.0
10
+ */
11
+ const EnforcerOptions = (opts) => (0, common_1.SetMetadata)(exports.META_ENFORCER_OPTIONS, opts);
12
+ exports.EnforcerOptions = EnforcerOptions;
@@ -0,0 +1,14 @@
1
+ export declare const META_UNPROTECTED = "unprotected";
2
+ export declare const META_SKIP_AUTH = "skip-auth";
3
+ /**
4
+ * Allow user to use unprotected routes.
5
+ * @since 1.2.0
6
+ * @param skipAuth attaches authorization header to user object when `false`, defaults to `true`
7
+ */
8
+ export declare const Unprotected: (skipAuth?: boolean) => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
9
+ /**
10
+ * Alias for `@Unprotected`.
11
+ * @since 1.2.0
12
+ * @param skipAuth attaches authorization header to user object when `false`, defaults to `true`
13
+ */
14
+ export declare const Public: (skipAuth?: boolean) => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;