@adatechnology/auth-keycloak 0.0.2 → 0.0.3
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.
- package/README.md +46 -0
- package/dist/errors/keycloak-error.d.ts +11 -0
- package/dist/errors/keycloak-error.js +20 -0
- package/dist/errors/keycloak-error.js.map +1 -0
- package/dist/index.d.ts +6 -65
- package/dist/index.js +16 -223
- package/dist/index.js.map +1 -0
- package/dist/keycloak.client.d.ts +27 -0
- package/dist/keycloak.client.js +320 -0
- package/dist/keycloak.client.js.map +1 -0
- package/dist/keycloak.http.interceptor.d.ts +9 -0
- package/dist/keycloak.http.interceptor.js +37 -0
- package/dist/keycloak.http.interceptor.js.map +1 -0
- package/dist/keycloak.interface.d.ts +74 -0
- package/dist/keycloak.interface.js +3 -0
- package/dist/keycloak.interface.js.map +1 -0
- package/dist/keycloak.module.d.ts +6 -0
- package/dist/keycloak.module.js +63 -0
- package/dist/keycloak.module.js.map +1 -0
- package/dist/keycloak.token.d.ts +3 -0
- package/dist/keycloak.token.js +7 -0
- package/dist/keycloak.token.js.map +1 -0
- package/dist/roles.decorator.d.ts +19 -0
- package/dist/roles.decorator.js +34 -0
- package/dist/roles.decorator.js.map +1 -0
- package/dist/roles.guard.d.ts +10 -0
- package/dist/roles.guard.js +103 -0
- package/dist/roles.guard.js.map +1 -0
- package/package.json +5 -3
- package/dist/index.d.mts +0 -65
- package/dist/index.mjs +0 -200
|
@@ -0,0 +1,103 @@
|
|
|
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 __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.RolesGuard = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const core_1 = require("@nestjs/core");
|
|
18
|
+
const roles_decorator_1 = require("./roles.decorator");
|
|
19
|
+
const keycloak_token_1 = require("./keycloak.token");
|
|
20
|
+
const shared_1 = require("@adatechnology/shared");
|
|
21
|
+
let RolesGuard = class RolesGuard {
|
|
22
|
+
reflector;
|
|
23
|
+
config;
|
|
24
|
+
constructor(reflector, config) {
|
|
25
|
+
this.reflector = reflector;
|
|
26
|
+
this.config = config;
|
|
27
|
+
}
|
|
28
|
+
canActivate(context) {
|
|
29
|
+
const meta = this.reflector.get(roles_decorator_1.ROLES_META_KEY, context.getHandler()) ||
|
|
30
|
+
this.reflector.get(roles_decorator_1.ROLES_META_KEY, context.getClass());
|
|
31
|
+
if (!meta || !meta.roles || meta.roles.length === 0)
|
|
32
|
+
return true;
|
|
33
|
+
const req = context.switchToHttp().getRequest();
|
|
34
|
+
const authHeader = req.headers?.authorization || req.headers?.Authorization;
|
|
35
|
+
const token = authHeader
|
|
36
|
+
? String(authHeader).split(" ")[1]
|
|
37
|
+
: req.query?.token;
|
|
38
|
+
if (!token)
|
|
39
|
+
throw new shared_1.BaseAppError({
|
|
40
|
+
message: "Authorization token not provided",
|
|
41
|
+
status: 403,
|
|
42
|
+
code: "FORBIDDEN_MISSING_TOKEN",
|
|
43
|
+
context: {},
|
|
44
|
+
});
|
|
45
|
+
const payload = this.decodeJwtPayload(token);
|
|
46
|
+
const availableRoles = new Set();
|
|
47
|
+
// realm roles
|
|
48
|
+
if (payload?.realm_access?.roles &&
|
|
49
|
+
Array.isArray(payload.realm_access.roles)) {
|
|
50
|
+
payload.realm_access.roles.forEach((r) => availableRoles.add(r));
|
|
51
|
+
}
|
|
52
|
+
// client roles (resource_access)
|
|
53
|
+
const clientId = this.config?.credentials?.clientId;
|
|
54
|
+
if (clientId && payload?.resource_access?.[clientId]?.roles) {
|
|
55
|
+
payload.resource_access[clientId].roles.forEach((r) => availableRoles.add(r));
|
|
56
|
+
}
|
|
57
|
+
// also consider all client roles if type is 'both' and resource_access exists
|
|
58
|
+
if (meta.type === "both" && payload?.resource_access) {
|
|
59
|
+
Object.values(payload.resource_access).forEach((entry) => {
|
|
60
|
+
if (entry?.roles && Array.isArray(entry.roles)) {
|
|
61
|
+
entry.roles.forEach((r) => availableRoles.add(r));
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// matching
|
|
66
|
+
const required = meta.roles || [];
|
|
67
|
+
const hasMatch = required.map((r) => availableRoles.has(r));
|
|
68
|
+
const result = meta.mode === "all" ? hasMatch.every(Boolean) : hasMatch.some(Boolean);
|
|
69
|
+
if (!result)
|
|
70
|
+
throw new shared_1.BaseAppError({
|
|
71
|
+
message: "Insufficient roles",
|
|
72
|
+
status: 403,
|
|
73
|
+
code: "FORBIDDEN_INSUFFICIENT_ROLES",
|
|
74
|
+
context: { required: required },
|
|
75
|
+
});
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
decodeJwtPayload(token) {
|
|
79
|
+
try {
|
|
80
|
+
const parts = token.split(".");
|
|
81
|
+
if (parts.length < 2)
|
|
82
|
+
return {};
|
|
83
|
+
const payload = parts[1];
|
|
84
|
+
const BufferCtor = globalThis.Buffer;
|
|
85
|
+
if (!BufferCtor)
|
|
86
|
+
return {};
|
|
87
|
+
const decoded = BufferCtor.from(payload, "base64").toString("utf8");
|
|
88
|
+
return JSON.parse(decoded);
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
return {};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
exports.RolesGuard = RolesGuard;
|
|
96
|
+
exports.RolesGuard = RolesGuard = __decorate([
|
|
97
|
+
(0, common_1.Injectable)(),
|
|
98
|
+
__param(0, (0, common_1.Inject)(core_1.Reflector)),
|
|
99
|
+
__param(1, (0, common_1.Optional)()),
|
|
100
|
+
__param(1, (0, common_1.Inject)(keycloak_token_1.KEYCLOAK_CONFIG)),
|
|
101
|
+
__metadata("design:paramtypes", [core_1.Reflector, Object])
|
|
102
|
+
], RolesGuard);
|
|
103
|
+
//# sourceMappingURL=roles.guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roles.guard.js","sourceRoot":"","sources":["../src/roles.guard.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adatechnology/auth-keycloak",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"dist"
|
|
12
12
|
],
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@adatechnology/http-client": "
|
|
14
|
+
"@adatechnology/http-client": "0.0.3",
|
|
15
|
+
"@adatechnology/logger": "0.0.2",
|
|
16
|
+
"@adatechnology/shared": "0.0.1"
|
|
15
17
|
},
|
|
16
18
|
"peerDependencies": {
|
|
17
19
|
"@nestjs/common": "^11.0.16",
|
|
@@ -23,7 +25,7 @@
|
|
|
23
25
|
"typescript": "^5.2.0"
|
|
24
26
|
},
|
|
25
27
|
"scripts": {
|
|
26
|
-
"build": "
|
|
28
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
27
29
|
"build:watch": "tsup --watch",
|
|
28
30
|
"check": "tsc -p tsconfig.json --noEmit",
|
|
29
31
|
"test": "echo \"no tests\""
|
package/dist/index.d.mts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { DynamicModule } from '@nestjs/common';
|
|
2
|
-
import { AxiosRequestConfig, AxiosInstance } from 'axios';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Keycloak token response
|
|
6
|
-
*/
|
|
7
|
-
interface KeycloakTokenResponse {
|
|
8
|
-
access_token: string;
|
|
9
|
-
expires_in: number;
|
|
10
|
-
refresh_expires_in: number;
|
|
11
|
-
refresh_token: string;
|
|
12
|
-
token_type: string;
|
|
13
|
-
'not-before-policy': number;
|
|
14
|
-
session_state: string;
|
|
15
|
-
scope: string;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Keycloak client credentials
|
|
19
|
-
*/
|
|
20
|
-
interface KeycloakCredentials {
|
|
21
|
-
clientId: string;
|
|
22
|
-
clientSecret: string;
|
|
23
|
-
username?: string;
|
|
24
|
-
password?: string;
|
|
25
|
-
grantType: 'client_credentials' | 'password';
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Keycloak configuration
|
|
29
|
-
*/
|
|
30
|
-
interface KeycloakConfig {
|
|
31
|
-
baseUrl: string;
|
|
32
|
-
realm: string;
|
|
33
|
-
credentials: KeycloakCredentials;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Keycloak client interface
|
|
37
|
-
*/
|
|
38
|
-
interface KeycloakClientInterface {
|
|
39
|
-
/**
|
|
40
|
-
* Get access token
|
|
41
|
-
*/
|
|
42
|
-
getAccessToken(): Promise<string>;
|
|
43
|
-
/**
|
|
44
|
-
* Refresh access token
|
|
45
|
-
*/
|
|
46
|
-
refreshToken(refreshToken: string): Promise<KeycloakTokenResponse>;
|
|
47
|
-
/**
|
|
48
|
-
* Validate token
|
|
49
|
-
*/
|
|
50
|
-
validateToken(token: string): Promise<boolean>;
|
|
51
|
-
/**
|
|
52
|
-
* Get user info
|
|
53
|
-
*/
|
|
54
|
-
getUserInfo(token: string): Promise<any>;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
declare class KeycloakModule {
|
|
58
|
-
static forRoot(config: KeycloakConfig, httpConfig?: AxiosRequestConfig | AxiosInstance): DynamicModule;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
declare const KEYCLOAK_CONFIG = "KEYCLOAK_CONFIG";
|
|
62
|
-
declare const KEYCLOAK_CLIENT = "KEYCLOAK_CLIENT";
|
|
63
|
-
declare const KEYCLOAK_HTTP_INTERCEPTOR = "KEYCLOAK_HTTP_INTERCEPTOR";
|
|
64
|
-
|
|
65
|
-
export { KEYCLOAK_CLIENT, KEYCLOAK_CONFIG, KEYCLOAK_HTTP_INTERCEPTOR, type KeycloakClientInterface, type KeycloakConfig, KeycloakModule, type KeycloakTokenResponse };
|
package/dist/index.mjs
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
-
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
-
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
-
if (decorator = decorators[i])
|
|
7
|
-
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
-
if (kind && result) __defProp(target, key, result);
|
|
9
|
-
return result;
|
|
10
|
-
};
|
|
11
|
-
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
12
|
-
|
|
13
|
-
// src/keycloak.module.ts
|
|
14
|
-
import { Module } from "@nestjs/common";
|
|
15
|
-
import { HTTP_PROVIDER as HTTP_PROVIDER2, HttpModule } from "@adatechnology/http-client";
|
|
16
|
-
|
|
17
|
-
// src/keycloak.client.ts
|
|
18
|
-
import { Inject, Injectable } from "@nestjs/common";
|
|
19
|
-
import { HTTP_PROVIDER } from "@adatechnology/http-client";
|
|
20
|
-
var KeycloakClient = class {
|
|
21
|
-
constructor(config, httpProvider) {
|
|
22
|
-
this.config = config;
|
|
23
|
-
this.httpProvider = httpProvider;
|
|
24
|
-
}
|
|
25
|
-
tokenCache = null;
|
|
26
|
-
tokenPromise = null;
|
|
27
|
-
async getAccessToken() {
|
|
28
|
-
const now = Date.now();
|
|
29
|
-
if (this.tokenCache && now < this.tokenCache.expiresAt) {
|
|
30
|
-
return this.tokenCache.token;
|
|
31
|
-
}
|
|
32
|
-
if (this.tokenPromise) return this.tokenPromise;
|
|
33
|
-
this.tokenPromise = (async () => {
|
|
34
|
-
try {
|
|
35
|
-
const tokenResponse = await this.requestToken();
|
|
36
|
-
const expiresAt = Date.now() + (tokenResponse.expires_in - 60) * 1e3;
|
|
37
|
-
this.tokenCache = { token: tokenResponse.access_token, expiresAt };
|
|
38
|
-
return tokenResponse.access_token;
|
|
39
|
-
} finally {
|
|
40
|
-
this.tokenPromise = null;
|
|
41
|
-
}
|
|
42
|
-
})();
|
|
43
|
-
return this.tokenPromise;
|
|
44
|
-
}
|
|
45
|
-
async getTokenWithCredentials(username, password) {
|
|
46
|
-
const tokenUrl = `${this.config.baseUrl}/realms/${this.config.realm}/protocol/openid-connect/token`;
|
|
47
|
-
const data = new URLSearchParams();
|
|
48
|
-
data.append("client_id", this.config.credentials.clientId);
|
|
49
|
-
data.append("grant_type", "password");
|
|
50
|
-
data.append("username", username);
|
|
51
|
-
data.append("password", password);
|
|
52
|
-
if (this.config.credentials.clientSecret) {
|
|
53
|
-
data.append("client_secret", this.config.credentials.clientSecret);
|
|
54
|
-
}
|
|
55
|
-
const response = await this.httpProvider.post(tokenUrl, data, {
|
|
56
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" }
|
|
57
|
-
});
|
|
58
|
-
return response.data;
|
|
59
|
-
}
|
|
60
|
-
async requestToken() {
|
|
61
|
-
const tokenUrl = `${this.config.baseUrl}/realms/${this.config.realm}/protocol/openid-connect/token`;
|
|
62
|
-
const data = new URLSearchParams();
|
|
63
|
-
data.append("client_id", this.config.credentials.clientId);
|
|
64
|
-
data.append("grant_type", this.config.credentials.grantType);
|
|
65
|
-
if (this.config.credentials.clientSecret) {
|
|
66
|
-
data.append("client_secret", this.config.credentials.clientSecret);
|
|
67
|
-
}
|
|
68
|
-
if (this.config.credentials.grantType === "password") {
|
|
69
|
-
if (this.config.credentials.username && this.config.credentials.password) {
|
|
70
|
-
data.append("username", this.config.credentials.username);
|
|
71
|
-
data.append("password", this.config.credentials.password);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
const response = await this.httpProvider.post(
|
|
75
|
-
tokenUrl,
|
|
76
|
-
data,
|
|
77
|
-
{
|
|
78
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" }
|
|
79
|
-
}
|
|
80
|
-
);
|
|
81
|
-
return response.data;
|
|
82
|
-
}
|
|
83
|
-
async refreshToken(refreshToken) {
|
|
84
|
-
const tokenUrl = `${this.config.baseUrl}/realms/${this.config.realm}/protocol/openid-connect/token`;
|
|
85
|
-
const data = new URLSearchParams();
|
|
86
|
-
data.append("client_id", this.config.credentials.clientId);
|
|
87
|
-
data.append("grant_type", "refresh_token");
|
|
88
|
-
data.append("refresh_token", refreshToken);
|
|
89
|
-
if (this.config.credentials.clientSecret) {
|
|
90
|
-
data.append("client_secret", this.config.credentials.clientSecret);
|
|
91
|
-
}
|
|
92
|
-
const response = await this.httpProvider.post(
|
|
93
|
-
tokenUrl,
|
|
94
|
-
data,
|
|
95
|
-
{
|
|
96
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" }
|
|
97
|
-
}
|
|
98
|
-
);
|
|
99
|
-
const expiresAt = Date.now() + (response.data.expires_in - 60) * 1e3;
|
|
100
|
-
this.tokenCache = { token: response.data.access_token, expiresAt };
|
|
101
|
-
return response.data;
|
|
102
|
-
}
|
|
103
|
-
async validateToken(token) {
|
|
104
|
-
try {
|
|
105
|
-
const introspectUrl = `${this.config.baseUrl}/realms/${this.config.realm}/protocol/openid-connect/token/introspect`;
|
|
106
|
-
const data = new URLSearchParams();
|
|
107
|
-
data.append("token", token);
|
|
108
|
-
data.append("client_id", this.config.credentials.clientId);
|
|
109
|
-
if (this.config.credentials.clientSecret) {
|
|
110
|
-
data.append("client_secret", this.config.credentials.clientSecret);
|
|
111
|
-
}
|
|
112
|
-
const response = await this.httpProvider.post(introspectUrl, data, {
|
|
113
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" }
|
|
114
|
-
});
|
|
115
|
-
return response.data.active === true;
|
|
116
|
-
} catch (error) {
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
async getUserInfo(token) {
|
|
121
|
-
const userInfoUrl = `${this.config.baseUrl}/realms/${this.config.realm}/protocol/openid-connect/userinfo`;
|
|
122
|
-
const response = await this.httpProvider.get(userInfoUrl, {
|
|
123
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
124
|
-
});
|
|
125
|
-
return response.data;
|
|
126
|
-
}
|
|
127
|
-
clearTokenCache() {
|
|
128
|
-
this.tokenCache = null;
|
|
129
|
-
}
|
|
130
|
-
static maskToken(token, visibleChars = 8) {
|
|
131
|
-
if (!token || typeof token !== "string") return "";
|
|
132
|
-
return token.length <= visibleChars ? token : `${token.slice(0, visibleChars)}...`;
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
KeycloakClient = __decorateClass([
|
|
136
|
-
Injectable(),
|
|
137
|
-
__decorateParam(1, Inject(HTTP_PROVIDER))
|
|
138
|
-
], KeycloakClient);
|
|
139
|
-
|
|
140
|
-
// src/keycloak.http.interceptor.ts
|
|
141
|
-
import {
|
|
142
|
-
Inject as Inject2,
|
|
143
|
-
Injectable as Injectable2
|
|
144
|
-
} from "@nestjs/common";
|
|
145
|
-
|
|
146
|
-
// src/keycloak.token.ts
|
|
147
|
-
var KEYCLOAK_CONFIG = "KEYCLOAK_CONFIG";
|
|
148
|
-
var KEYCLOAK_CLIENT = "KEYCLOAK_CLIENT";
|
|
149
|
-
var KEYCLOAK_HTTP_INTERCEPTOR = "KEYCLOAK_HTTP_INTERCEPTOR";
|
|
150
|
-
|
|
151
|
-
// src/keycloak.http.interceptor.ts
|
|
152
|
-
var KeycloakHttpInterceptor = class {
|
|
153
|
-
constructor(keycloakClient) {
|
|
154
|
-
this.keycloakClient = keycloakClient;
|
|
155
|
-
}
|
|
156
|
-
intercept(context, next) {
|
|
157
|
-
const request = context.switchToHttp().getRequest();
|
|
158
|
-
if (request.url && !request.url.includes("keycloak")) {
|
|
159
|
-
}
|
|
160
|
-
return next.handle();
|
|
161
|
-
}
|
|
162
|
-
};
|
|
163
|
-
KeycloakHttpInterceptor = __decorateClass([
|
|
164
|
-
Injectable2(),
|
|
165
|
-
__decorateParam(0, Inject2(KEYCLOAK_CLIENT))
|
|
166
|
-
], KeycloakHttpInterceptor);
|
|
167
|
-
|
|
168
|
-
// src/keycloak.module.ts
|
|
169
|
-
var KeycloakModule = class {
|
|
170
|
-
static forRoot(config, httpConfig) {
|
|
171
|
-
return {
|
|
172
|
-
module: KeycloakModule,
|
|
173
|
-
global: true,
|
|
174
|
-
imports: [HttpModule.forRoot(httpConfig)],
|
|
175
|
-
providers: [
|
|
176
|
-
{ provide: KEYCLOAK_CONFIG, useValue: config },
|
|
177
|
-
{
|
|
178
|
-
provide: KEYCLOAK_CLIENT,
|
|
179
|
-
useFactory: (cfg, httpProvider) => new KeycloakClient(cfg, httpProvider),
|
|
180
|
-
inject: [KEYCLOAK_CONFIG, HTTP_PROVIDER2]
|
|
181
|
-
},
|
|
182
|
-
{
|
|
183
|
-
provide: KEYCLOAK_HTTP_INTERCEPTOR,
|
|
184
|
-
useFactory: (client) => new KeycloakHttpInterceptor(client),
|
|
185
|
-
inject: [KEYCLOAK_CLIENT]
|
|
186
|
-
}
|
|
187
|
-
],
|
|
188
|
-
exports: [KEYCLOAK_CLIENT, KEYCLOAK_HTTP_INTERCEPTOR]
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
KeycloakModule = __decorateClass([
|
|
193
|
-
Module({})
|
|
194
|
-
], KeycloakModule);
|
|
195
|
-
export {
|
|
196
|
-
KEYCLOAK_CLIENT,
|
|
197
|
-
KEYCLOAK_CONFIG,
|
|
198
|
-
KEYCLOAK_HTTP_INTERCEPTOR,
|
|
199
|
-
KeycloakModule
|
|
200
|
-
};
|