nestjs-keycloak-auth 1.0.0

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 (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +391 -0
  3. package/dist/constants.d.ts +65 -0
  4. package/dist/constants.js +72 -0
  5. package/dist/controllers/keycloak-admin.controller.d.ts +16 -0
  6. package/dist/controllers/keycloak-admin.controller.js +94 -0
  7. package/dist/decorators/access-token.decorator.d.ts +5 -0
  8. package/dist/decorators/access-token.decorator.js +13 -0
  9. package/dist/decorators/enforcer-options.decorator.d.ts +8 -0
  10. package/dist/decorators/enforcer-options.decorator.js +12 -0
  11. package/dist/decorators/keycloak-user.decorator.d.ts +5 -0
  12. package/dist/decorators/keycloak-user.decorator.js +13 -0
  13. package/dist/decorators/public.decorator.d.ts +6 -0
  14. package/dist/decorators/public.decorator.js +11 -0
  15. package/dist/decorators/resource.decorator.d.ts +6 -0
  16. package/dist/decorators/resource.decorator.js +11 -0
  17. package/dist/decorators/roles.decorator.d.ts +10 -0
  18. package/dist/decorators/roles.decorator.js +15 -0
  19. package/dist/decorators/scopes.decorator.d.ts +19 -0
  20. package/dist/decorators/scopes.decorator.js +27 -0
  21. package/dist/decorators/token-scopes.decorator.d.ts +19 -0
  22. package/dist/decorators/token-scopes.decorator.js +24 -0
  23. package/dist/errors.d.ts +24 -0
  24. package/dist/errors.js +44 -0
  25. package/dist/guards/auth.guard.d.ts +25 -0
  26. package/dist/guards/auth.guard.js +176 -0
  27. package/dist/guards/resource.guard.d.ts +26 -0
  28. package/dist/guards/resource.guard.js +245 -0
  29. package/dist/guards/role.guard.d.ts +17 -0
  30. package/dist/guards/role.guard.js +113 -0
  31. package/dist/index.d.ts +1 -0
  32. package/dist/index.js +18 -0
  33. package/dist/interface/enforcer-options.interface.d.ts +8 -0
  34. package/dist/interface/enforcer-options.interface.js +2 -0
  35. package/dist/interface/jwks.interface.d.ts +22 -0
  36. package/dist/interface/jwks.interface.js +2 -0
  37. package/dist/interface/jwt.interface.d.ts +46 -0
  38. package/dist/interface/jwt.interface.js +2 -0
  39. package/dist/interface/keycloak-auth-module-async-options.interface.d.ts +11 -0
  40. package/dist/interface/keycloak-auth-module-async-options.interface.js +2 -0
  41. package/dist/interface/keycloak-auth-options-factory.interface.d.ts +4 -0
  42. package/dist/interface/keycloak-auth-options-factory.interface.js +2 -0
  43. package/dist/interface/keycloak-auth-options.interface.d.ts +162 -0
  44. package/dist/interface/keycloak-auth-options.interface.js +3 -0
  45. package/dist/interface/keycloak-grant.interface.d.ts +33 -0
  46. package/dist/interface/keycloak-grant.interface.js +2 -0
  47. package/dist/interface/keycloak-request.interface.d.ts +8 -0
  48. package/dist/interface/keycloak-request.interface.js +2 -0
  49. package/dist/interface/oidc.interface.d.ts +14 -0
  50. package/dist/interface/oidc.interface.js +2 -0
  51. package/dist/interface/server.interface.d.ts +9 -0
  52. package/dist/interface/server.interface.js +2 -0
  53. package/dist/interface/tenant-config.interface.d.ts +13 -0
  54. package/dist/interface/tenant-config.interface.js +2 -0
  55. package/dist/internal.util.d.ts +13 -0
  56. package/dist/internal.util.js +54 -0
  57. package/dist/keycloak-auth.module.d.ts +54 -0
  58. package/dist/keycloak-auth.module.js +174 -0
  59. package/dist/keycloak-auth.providers.d.ts +7 -0
  60. package/dist/keycloak-auth.providers.js +122 -0
  61. package/dist/services/backchannel-logout.service.d.ts +30 -0
  62. package/dist/services/backchannel-logout.service.js +100 -0
  63. package/dist/services/jwks-cache.service.d.ts +25 -0
  64. package/dist/services/jwks-cache.service.js +130 -0
  65. package/dist/services/keycloak-admin.service.d.ts +37 -0
  66. package/dist/services/keycloak-admin.service.js +268 -0
  67. package/dist/services/keycloak-grant.service.d.ts +23 -0
  68. package/dist/services/keycloak-grant.service.js +88 -0
  69. package/dist/services/keycloak-http.service.d.ts +41 -0
  70. package/dist/services/keycloak-http.service.js +211 -0
  71. package/dist/services/keycloak-multitenant.service.d.ts +24 -0
  72. package/dist/services/keycloak-multitenant.service.js +161 -0
  73. package/dist/services/keycloak-url.service.d.ts +12 -0
  74. package/dist/services/keycloak-url.service.js +43 -0
  75. package/dist/services/oidc-discovery.service.d.ts +24 -0
  76. package/dist/services/oidc-discovery.service.js +86 -0
  77. package/dist/services/token-validation.service.d.ts +29 -0
  78. package/dist/services/token-validation.service.js +215 -0
  79. package/dist/token/keycloak-grant.d.ts +24 -0
  80. package/dist/token/keycloak-grant.js +37 -0
  81. package/dist/token/keycloak-token.d.ts +57 -0
  82. package/dist/token/keycloak-token.js +105 -0
  83. package/dist/types/conditional-scope.type.d.ts +2 -0
  84. package/dist/types/conditional-scope.type.js +2 -0
  85. package/dist/util.d.ts +3 -0
  86. package/dist/util.js +17 -0
  87. package/package.json +130 -0
@@ -0,0 +1,122 @@
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 __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.createKeycloakAuthOptionProvider = exports.keycloakProvider = void 0;
27
+ const fs = __importStar(require("fs"));
28
+ const path = __importStar(require("path"));
29
+ const errors_1 = require("./errors");
30
+ const keycloak_auth_module_1 = require("./keycloak-auth.module");
31
+ const constants_1 = require("./constants");
32
+ /**
33
+ * Resolve environment variable references in config values.
34
+ * Matches keycloak-connect config.js resolveValue() logic.
35
+ * Supports: ${env.MY_VAR} and ${env.MY_VAR:fallback}
36
+ */
37
+ function resolveValue(value) {
38
+ var _a;
39
+ if (typeof value !== 'string') {
40
+ return value;
41
+ }
42
+ const regex = /^\$\{env\.([^:}]*?)(?::([^}]*))?\}$/;
43
+ const match = value.match(regex);
44
+ if (!match) {
45
+ return value;
46
+ }
47
+ const envVar = match[1];
48
+ const fallbackVal = (_a = match[2]) !== null && _a !== void 0 ? _a : '';
49
+ const envVal = process.env[envVar];
50
+ return envVal !== null && envVal !== void 0 ? envVal : fallbackVal;
51
+ }
52
+ /**
53
+ * Resolves a KeycloakAuthConfig into a ResolvedTenantConfig.
54
+ */
55
+ const resolveConfig = (opts) => {
56
+ var _a, _b, _c, _d;
57
+ const authServerUrl = resolveValue(opts.authServerUrl ||
58
+ opts['auth-server-url'] ||
59
+ opts.serverUrl ||
60
+ opts['server-url'] ||
61
+ '').replace(/\/+$/, '');
62
+ const realm = resolveValue(opts.realm || '');
63
+ const clientId = resolveValue(opts.clientId || opts['client-id'] || opts.resource || '');
64
+ const secret = resolveValue(opts.secret || (opts.credentials && opts.credentials.secret) || '');
65
+ const realmUrl = realm ? `${authServerUrl}/realms/${realm}` : authServerUrl;
66
+ const realmAdminUrl = realm
67
+ ? `${authServerUrl}/admin/realms/${realm}`
68
+ : authServerUrl;
69
+ const isPublic = !!resolveValue((_b = (_a = opts['public-client']) !== null && _a !== void 0 ? _a : opts.public) !== null && _b !== void 0 ? _b : false);
70
+ const bearerOnly = !!resolveValue((_d = (_c = opts['bearer-only']) !== null && _c !== void 0 ? _c : opts.bearerOnly) !== null && _d !== void 0 ? _d : false);
71
+ return {
72
+ authServerUrl,
73
+ realm,
74
+ clientId,
75
+ secret,
76
+ realmUrl,
77
+ realmAdminUrl,
78
+ isPublic,
79
+ bearerOnly,
80
+ };
81
+ };
82
+ exports.keycloakProvider = {
83
+ provide: constants_1.KEYCLOAK_INSTANCE,
84
+ useFactory: (opts) => {
85
+ if (typeof opts === 'string') {
86
+ throw new errors_1.KeycloakConfigError('Keycloak configuration should have been parsed by this point.');
87
+ }
88
+ // Warn if using token validation none
89
+ if (opts.tokenValidation && opts.tokenValidation === constants_1.TokenValidation.NONE) {
90
+ keycloak_auth_module_1.KeycloakAuthModule.logger.warn('Token validation is disabled, please only do this on development/special use-cases.');
91
+ }
92
+ return resolveConfig(opts);
93
+ },
94
+ inject: [constants_1.KEYCLOAK_AUTH_OPTIONS],
95
+ };
96
+ const parseConfig = (opts, config) => {
97
+ if (typeof opts === 'string') {
98
+ const configPathRelative = path.join(__dirname, opts);
99
+ const configPathRoot = path.join(process.cwd(), opts);
100
+ let configPath;
101
+ if (fs.existsSync(configPathRelative)) {
102
+ configPath = configPathRelative;
103
+ }
104
+ else if (fs.existsSync(configPathRoot)) {
105
+ configPath = configPathRoot;
106
+ }
107
+ else {
108
+ throw new errors_1.KeycloakConfigError(`Cannot find files, looked in [ ${configPathRelative}, ${configPathRoot} ]`);
109
+ }
110
+ const json = fs.readFileSync(configPath);
111
+ const keycloakConfig = JSON.parse(json.toString());
112
+ return Object.assign(keycloakConfig, config);
113
+ }
114
+ return opts;
115
+ };
116
+ const createKeycloakAuthOptionProvider = (opts, config) => {
117
+ return {
118
+ provide: constants_1.KEYCLOAK_AUTH_OPTIONS,
119
+ useValue: parseConfig(opts, config),
120
+ };
121
+ };
122
+ exports.createKeycloakAuthOptionProvider = createKeycloakAuthOptionProvider;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * In-memory store tracking revoked sessions and users from
3
+ * OIDC back-channel logout tokens.
4
+ *
5
+ * Entries are automatically cleaned up after the configured TTL
6
+ * (default 24 hours) to prevent unbounded memory growth.
7
+ */
8
+ export declare class BackchannelLogoutService {
9
+ private readonly logger;
10
+ private readonly revokedSessions;
11
+ private readonly revokedUsers;
12
+ private readonly ttlMs;
13
+ /**
14
+ * Mark a session and/or user as revoked.
15
+ * At least one of `sid` or `sub` must be provided.
16
+ */
17
+ revoke(sid?: string, sub?: string): void;
18
+ /**
19
+ * Check whether a session or user has been revoked.
20
+ * @param sid Session ID from the token
21
+ * @param sub Subject (user ID) from the token
22
+ * @param iat Issued-at timestamp (seconds) from the token — tokens issued
23
+ * after the revocation are from a new session and should be allowed.
24
+ */
25
+ isRevoked(sid?: string, sub?: string, iat?: number): boolean;
26
+ /**
27
+ * Remove entries older than the TTL to prevent unbounded growth.
28
+ */
29
+ private cleanup;
30
+ }
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var BackchannelLogoutService_1;
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.BackchannelLogoutService = void 0;
11
+ const common_1 = require("@nestjs/common");
12
+ /**
13
+ * In-memory store tracking revoked sessions and users from
14
+ * OIDC back-channel logout tokens.
15
+ *
16
+ * Entries are automatically cleaned up after the configured TTL
17
+ * (default 24 hours) to prevent unbounded memory growth.
18
+ */
19
+ let BackchannelLogoutService = BackchannelLogoutService_1 = class BackchannelLogoutService {
20
+ constructor() {
21
+ this.logger = new common_1.Logger(BackchannelLogoutService_1.name);
22
+ this.revokedSessions = new Map(); // sid → revokedAt
23
+ this.revokedUsers = new Map(); // sub → revokedAt
24
+ this.ttlMs = 24 * 60 * 60 * 1000; // 24 hours
25
+ }
26
+ /**
27
+ * Mark a session and/or user as revoked.
28
+ * At least one of `sid` or `sub` must be provided.
29
+ */
30
+ revoke(sid, sub) {
31
+ const now = Date.now();
32
+ if (sid) {
33
+ this.revokedSessions.set(sid, now);
34
+ this.logger.log(`Session revoked: ${sid}`);
35
+ }
36
+ if (sub) {
37
+ this.revokedUsers.set(sub, now);
38
+ this.logger.log(`User revoked: ${sub}`);
39
+ }
40
+ this.cleanup();
41
+ }
42
+ /**
43
+ * Check whether a session or user has been revoked.
44
+ * @param sid Session ID from the token
45
+ * @param sub Subject (user ID) from the token
46
+ * @param iat Issued-at timestamp (seconds) from the token — tokens issued
47
+ * after the revocation are from a new session and should be allowed.
48
+ */
49
+ isRevoked(sid, sub, iat) {
50
+ const cutoff = Date.now() - this.ttlMs;
51
+ const issuedAtMs = iat ? iat * 1000 : 0;
52
+ if (sid) {
53
+ const revokedAt = this.revokedSessions.get(sid);
54
+ if (revokedAt !== undefined) {
55
+ if (revokedAt < cutoff) {
56
+ this.revokedSessions.delete(sid);
57
+ }
58
+ else {
59
+ return true;
60
+ }
61
+ }
62
+ }
63
+ if (sub) {
64
+ const revokedAt = this.revokedUsers.get(sub);
65
+ if (revokedAt !== undefined) {
66
+ if (revokedAt < cutoff) {
67
+ this.revokedUsers.delete(sub);
68
+ }
69
+ else if (issuedAtMs > revokedAt) {
70
+ // Token was issued after the revocation — new session, allow it
71
+ return false;
72
+ }
73
+ else {
74
+ return true;
75
+ }
76
+ }
77
+ }
78
+ return false;
79
+ }
80
+ /**
81
+ * Remove entries older than the TTL to prevent unbounded growth.
82
+ */
83
+ cleanup() {
84
+ const cutoff = Date.now() - this.ttlMs;
85
+ for (const [key, revokedAt] of this.revokedSessions) {
86
+ if (revokedAt < cutoff) {
87
+ this.revokedSessions.delete(key);
88
+ }
89
+ }
90
+ for (const [key, revokedAt] of this.revokedUsers) {
91
+ if (revokedAt < cutoff) {
92
+ this.revokedUsers.delete(key);
93
+ }
94
+ }
95
+ }
96
+ };
97
+ exports.BackchannelLogoutService = BackchannelLogoutService;
98
+ exports.BackchannelLogoutService = BackchannelLogoutService = BackchannelLogoutService_1 = __decorate([
99
+ (0, common_1.Injectable)()
100
+ ], BackchannelLogoutService);
@@ -0,0 +1,25 @@
1
+ /// <reference types="node" />
2
+ import * as crypto from 'crypto';
3
+ import { KeycloakHttpService } from './keycloak-http.service';
4
+ import { KeycloakAuthConfig } from '../interface/keycloak-auth-options.interface';
5
+ export declare class JwksCacheService {
6
+ private readonly keycloakHttp;
7
+ private readonly logger;
8
+ private readonly cache;
9
+ private minTimeBetweenRequestsMs;
10
+ constructor(keycloakOpts: KeycloakAuthConfig, keycloakHttp: KeycloakHttpService);
11
+ /**
12
+ * Set the minimum time between JWKS requests (for rate limiting).
13
+ */
14
+ setMinTimeBetweenRequests(ms: number): void;
15
+ /**
16
+ * Get the public key for a given realm URL and key ID.
17
+ * Fetches JWKS if not cached, or if the kid is not found and rate limit allows.
18
+ */
19
+ getKey(realmUrl: string, kid: string): Promise<crypto.KeyObject>;
20
+ /**
21
+ * Clear all cached JWKS keys. Matches keycloak-connect Rotation.clearCache().
22
+ */
23
+ clearCache(): void;
24
+ private fetchAndCache;
25
+ }
@@ -0,0 +1,130 @@
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 JwksCacheService_1;
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.JwksCacheService = void 0;
49
+ const crypto = __importStar(require("crypto"));
50
+ const errors_1 = require("../errors");
51
+ const constants_1 = require("../constants");
52
+ const keycloak_http_service_1 = require("./keycloak-http.service");
53
+ const common_1 = require("@nestjs/common");
54
+ let JwksCacheService = JwksCacheService_1 = class JwksCacheService {
55
+ constructor(keycloakOpts, keycloakHttp) {
56
+ var _a;
57
+ this.keycloakHttp = keycloakHttp;
58
+ this.logger = new common_1.Logger(JwksCacheService_1.name);
59
+ this.cache = new Map();
60
+ // Wire minTimeBetweenJwksRequests from config.
61
+ // Original keycloak-connect uses seconds (default 10);
62
+ // we convert to milliseconds internally.
63
+ const configValue = (_a = keycloakOpts.minTimeBetweenJwksRequests) !== null && _a !== void 0 ? _a : keycloakOpts['min-time-between-jwks-requests'];
64
+ this.minTimeBetweenRequestsMs =
65
+ configValue != null ? configValue * 1000 : 10000;
66
+ }
67
+ /**
68
+ * Set the minimum time between JWKS requests (for rate limiting).
69
+ */
70
+ setMinTimeBetweenRequests(ms) {
71
+ this.minTimeBetweenRequestsMs = ms;
72
+ }
73
+ /**
74
+ * Get the public key for a given realm URL and key ID.
75
+ * Fetches JWKS if not cached, or if the kid is not found and rate limit allows.
76
+ */
77
+ getKey(realmUrl, kid) {
78
+ return __awaiter(this, void 0, void 0, function* () {
79
+ let cached = this.cache.get(realmUrl);
80
+ // First fetch
81
+ if (!cached) {
82
+ cached = yield this.fetchAndCache(realmUrl);
83
+ }
84
+ // Try to find the key
85
+ let jwk = cached.keys.get(kid);
86
+ // Key not found — may be key rotation, try refetching if allowed
87
+ if (!jwk) {
88
+ const now = Date.now();
89
+ if (now - cached.fetchedAt >= this.minTimeBetweenRequestsMs) {
90
+ this.logger.verbose(`Key '${kid}' not found, refetching JWKS for ${realmUrl}`);
91
+ cached = yield this.fetchAndCache(realmUrl);
92
+ jwk = cached.keys.get(kid);
93
+ }
94
+ }
95
+ if (!jwk) {
96
+ throw new errors_1.KeycloakTokenError(`Key '${kid}' not found in JWKS for realm: ${realmUrl}`);
97
+ }
98
+ return crypto.createPublicKey({
99
+ key: jwk,
100
+ format: 'jwk',
101
+ });
102
+ });
103
+ }
104
+ /**
105
+ * Clear all cached JWKS keys. Matches keycloak-connect Rotation.clearCache().
106
+ */
107
+ clearCache() {
108
+ this.cache.clear();
109
+ }
110
+ fetchAndCache(realmUrl) {
111
+ return __awaiter(this, void 0, void 0, function* () {
112
+ const response = yield this.keycloakHttp.fetchJwks(realmUrl);
113
+ const keys = new Map();
114
+ for (const key of response.keys) {
115
+ if (key.kid) {
116
+ keys.set(key.kid, key);
117
+ }
118
+ }
119
+ const cached = { keys, fetchedAt: Date.now() };
120
+ this.cache.set(realmUrl, cached);
121
+ return cached;
122
+ });
123
+ }
124
+ };
125
+ exports.JwksCacheService = JwksCacheService;
126
+ exports.JwksCacheService = JwksCacheService = JwksCacheService_1 = __decorate([
127
+ (0, common_1.Injectable)(),
128
+ __param(0, (0, common_1.Inject)(constants_1.KEYCLOAK_AUTH_OPTIONS)),
129
+ __metadata("design:paramtypes", [Object, keycloak_http_service_1.KeycloakHttpService])
130
+ ], JwksCacheService);
@@ -0,0 +1,37 @@
1
+ import { JwksCacheService } from './jwks-cache.service';
2
+ import { ServerRequest } from '../interface/server.interface';
3
+ import { BackchannelLogoutService } from './backchannel-logout.service';
4
+ import { TokenValidationService } from './token-validation.service';
5
+ import { ResolvedTenantConfig } from '../interface/tenant-config.interface';
6
+ import { KeycloakMultiTenantService } from './keycloak-multitenant.service';
7
+ import { KeycloakAuthConfig } from '../interface/keycloak-auth-options.interface';
8
+ /**
9
+ * Encapsulates all business logic for Keycloak admin callbacks:
10
+ * push-not-before policy and OIDC back-channel logout.
11
+ */
12
+ export declare class KeycloakAdminService {
13
+ private readonly tenantConfig;
14
+ private readonly keycloakOpts;
15
+ private readonly multiTenant;
16
+ private readonly tokenValidation;
17
+ private readonly jwksCache;
18
+ private readonly backchannelLogoutService;
19
+ private readonly logger;
20
+ constructor(tenantConfig: ResolvedTenantConfig, keycloakOpts: KeycloakAuthConfig, multiTenant: KeycloakMultiTenantService, tokenValidation: TokenValidationService, jwksCache: JwksCacheService, backchannelLogoutService: BackchannelLogoutService);
21
+ /**
22
+ * Process a push-not-before admin callback.
23
+ * Verifies the token signature and updates the not-before policy.
24
+ */
25
+ processPushNotBefore(body: unknown, request: ServerRequest): Promise<void>;
26
+ /**
27
+ * Process an OIDC back-channel logout token.
28
+ * Verifies signature, validates claims, and revokes the session/user.
29
+ */
30
+ processBackchannelLogout(body: unknown, request: ServerRequest): Promise<void>;
31
+ private extractAdminPayload;
32
+ private extractLogoutToken;
33
+ private static readonly ALLOWED_ALGS;
34
+ private verifySignature;
35
+ private verifyTokenSignature;
36
+ private resolveTenantConfig;
37
+ }