@monderks/nestjs-keycloak-auth 0.1.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 (37) hide show
  1. package/.eslintrc.js +26 -0
  2. package/.prettierrc +20 -0
  3. package/README.md +299 -0
  4. package/dist/decorators/current-user.decorator.d.ts +3 -0
  5. package/dist/decorators/current-user.decorator.d.ts.map +1 -0
  6. package/dist/decorators/current-user.decorator.js +10 -0
  7. package/dist/decorators/current-user.decorator.js.map +1 -0
  8. package/dist/guards/keycloak-auth.guard.d.ts +18 -0
  9. package/dist/guards/keycloak-auth.guard.d.ts.map +1 -0
  10. package/dist/guards/keycloak-auth.guard.js +81 -0
  11. package/dist/guards/keycloak-auth.guard.js.map +1 -0
  12. package/dist/index.d.ts +6 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +28 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/interfaces/keycloak-config.interface.d.ts +56 -0
  17. package/dist/interfaces/keycloak-config.interface.d.ts.map +1 -0
  18. package/dist/interfaces/keycloak-config.interface.js +3 -0
  19. package/dist/interfaces/keycloak-config.interface.js.map +1 -0
  20. package/dist/keycloak-auth.module.d.ts +5 -0
  21. package/dist/keycloak-auth.module.d.ts.map +1 -0
  22. package/dist/keycloak-auth.module.js +44 -0
  23. package/dist/keycloak-auth.module.js.map +1 -0
  24. package/dist/keycloak-auth.service.d.ts +16 -0
  25. package/dist/keycloak-auth.service.d.ts.map +1 -0
  26. package/dist/keycloak-auth.service.js +231 -0
  27. package/dist/keycloak-auth.service.js.map +1 -0
  28. package/examples/frontend-controlled-auth.ts +96 -0
  29. package/jest.config.js +14 -0
  30. package/package.json +52 -0
  31. package/src/decorators/current-user.decorator.ts +11 -0
  32. package/src/guards/keycloak-auth.guard.ts +93 -0
  33. package/src/index.ts +14 -0
  34. package/src/interfaces/keycloak-config.interface.ts +58 -0
  35. package/src/keycloak-auth.module.ts +32 -0
  36. package/src/keycloak-auth.service.ts +243 -0
  37. package/tsconfig.json +40 -0
package/.eslintrc.js ADDED
@@ -0,0 +1,26 @@
1
+ module.exports = {
2
+ parser: '@typescript-eslint/parser',
3
+ parserOptions: {
4
+ project: 'tsconfig.json',
5
+ tsconfigRootDir: __dirname,
6
+ sourceType: 'module',
7
+ },
8
+ plugins: ['@typescript-eslint/eslint-plugin'],
9
+ extends: [
10
+ 'plugin:@typescript-eslint/recommended',
11
+ 'plugin:prettier/recommended',
12
+ ],
13
+ root: true,
14
+ env: {
15
+ node: true,
16
+ jest: true,
17
+ },
18
+ ignorePatterns: ['.eslintrc.js', 'dist/**/*'],
19
+ rules: {
20
+ '@typescript-eslint/interface-name-prefix': 'off',
21
+ '@typescript-eslint/explicit-function-return-type': 'off',
22
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
23
+ '@typescript-eslint/no-explicit-any': 'off',
24
+ '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
25
+ },
26
+ };
package/.prettierrc ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "arrowParens": "always",
3
+ "bracketSameLine": true,
4
+ "bracketSpacing": true,
5
+ "semi": true,
6
+ "singleQuote": true,
7
+ "jsxSingleQuote": false,
8
+ "quoteProps": "as-needed",
9
+ "trailingComma": "all",
10
+ "singleAttributePerLine": false,
11
+ "htmlWhitespaceSensitivity": "css",
12
+ "vueIndentScriptAndStyle": false,
13
+ "proseWrap": "preserve",
14
+ "insertPragma": false,
15
+ "printWidth": 120,
16
+ "requirePragma": false,
17
+ "tabWidth": 4,
18
+ "useTabs": false,
19
+ "embeddedLanguageFormatting": "auto"
20
+ }
package/README.md ADDED
@@ -0,0 +1,299 @@
1
+ # Keycloak Auth Library
2
+
3
+ Librería para validación de tokens Keycloak en NestJS cuando el frontend controla la autenticación.
4
+
5
+ ## Características
6
+
7
+ - 🎫 Validación de tokens JWT desde frontend
8
+ - 🔄 Renovación de tokens
9
+ - 👤 Obtención de información de usuario
10
+ - 🛡️ Guards de autorización con roles
11
+ - 🏷️ Decoradores para obtener usuario actual
12
+ - 🔧 Configuración flexible
13
+ - 🌐 Optimizada para frontend (Next.js, React, etc.)
14
+
15
+ ## Instalación
16
+
17
+ ```bash
18
+ npm install @monderks/keycloak-auth
19
+ ```
20
+
21
+ ## Configuración
22
+
23
+ ### Configuración Básica
24
+
25
+ ```typescript
26
+ import { Module } from '@nestjs/common';
27
+ import { KeycloakAuthModule } from '@monderks/keycloak-auth';
28
+
29
+ @Module({
30
+ imports: [
31
+ KeycloakAuthModule.forRoot({
32
+ config: {
33
+ serverUrl: 'http://localhost:8080',
34
+ realm: 'my-realm',
35
+ clientId: 'my-client',
36
+ clientSecret: 'my-client-secret', // Necesario para refresh y logout
37
+ verifyTokenAudience: true,
38
+ verifyTokenIssuer: true,
39
+ tokenExpirationTolerance: 30, // segundos
40
+ },
41
+ }),
42
+ ],
43
+ })
44
+ export class AppModule {}
45
+ ```
46
+
47
+ ### Configuración Asíncrona
48
+
49
+ ```typescript
50
+ import { Module } from '@nestjs/common';
51
+ import { ConfigModule, ConfigService } from '@nestjs/config';
52
+ import { KeycloakAuthModule } from '@monderks/keycloak-auth';
53
+
54
+ @Module({
55
+ imports: [
56
+ ConfigModule.forRoot(),
57
+ KeycloakAuthModule.forRootAsync({
58
+ useFactory: (configService: ConfigService) => ({
59
+ serverUrl: configService.get('KEYCLOAK_SERVER_URL'),
60
+ realm: configService.get('KEYCLOAK_REALM'),
61
+ clientId: configService.get('KEYCLOAK_CLIENT_ID'),
62
+ clientSecret: configService.get('KEYCLOAK_CLIENT_SECRET'),
63
+ verifyTokenAudience: true,
64
+ verifyTokenIssuer: true,
65
+ }),
66
+ inject: [ConfigService],
67
+ }),
68
+ ],
69
+ })
70
+ export class AppModule {}
71
+ ```
72
+
73
+ ## ¿Cómo usar la librería?
74
+
75
+ Esta librería **no expone controladores**. Solo provee servicios, guards y decoradores para que tú crees tus propios endpoints y lógica de negocio.
76
+
77
+ ### Ejemplo de uso en tu propio controlador
78
+
79
+ ```typescript
80
+ import { Controller, Get, Post, Body, UseGuards } from '@nestjs/common';
81
+ import { KeycloakAuthService, KeycloakAuthGuard, KeycloakAuth, CurrentUser } from '@monderks/keycloak-auth';
82
+ import { DecodedToken } from '@monderks/keycloak-auth';
83
+
84
+ @Controller('api/auth')
85
+ export class AuthController {
86
+ constructor(private keycloakService: KeycloakAuthService) {}
87
+
88
+ @Post('validate')
89
+ async validateToken(@Body() body: { token: string }) {
90
+ return await this.keycloakService.validateToken(body.token);
91
+ }
92
+
93
+ @Get('protected')
94
+ @UseGuards(KeycloakAuthGuard)
95
+ @KeycloakAuth()
96
+ getProtectedData(@CurrentUser() user: DecodedToken) {
97
+ return {
98
+ message: 'Datos protegidos',
99
+ user,
100
+ };
101
+ }
102
+ }
103
+ ```
104
+
105
+ ## Flujo de Autenticación
106
+
107
+ ### 1. Frontend (Next.js/React) - Login
108
+
109
+ ```typescript
110
+ // Instalar keycloak-js
111
+ // npm install keycloak-js
112
+
113
+ import Keycloak from 'keycloak-js';
114
+
115
+ // Configurar Keycloak
116
+ const keycloak = new Keycloak({
117
+ url: 'http://localhost:8080',
118
+ realm: 'my-realm',
119
+ clientId: 'my-client'
120
+ });
121
+
122
+ // Inicializar y hacer login
123
+ await keycloak.init({
124
+ onLoad: 'login-required'
125
+ });
126
+
127
+ // Obtener el token
128
+ const token = keycloak.token;
129
+ ```
130
+
131
+ ### 2. Frontend - Enviar Requests al Backend
132
+
133
+ ```typescript
134
+ // Endpoint protegido
135
+ const response = await fetch('/api/auth/protected', {
136
+ headers: {
137
+ 'Authorization': `Bearer ${token}`
138
+ }
139
+ });
140
+
141
+ // Validar token
142
+ const validateResponse = await fetch('/api/auth/validate', {
143
+ method: 'POST',
144
+ headers: {
145
+ 'Content-Type': 'application/json'
146
+ },
147
+ body: JSON.stringify({ token })
148
+ });
149
+
150
+ // Renovar token
151
+ const refreshResponse = await fetch('/api/auth/refresh', {
152
+ method: 'POST',
153
+ headers: {
154
+ 'Content-Type': 'application/json'
155
+ },
156
+ body: JSON.stringify({ refreshToken: keycloak.refreshToken })
157
+ });
158
+
159
+ // Logout
160
+ const logoutResponse = await fetch('/api/auth/logout', {
161
+ method: 'POST',
162
+ headers: {
163
+ 'Content-Type': 'application/json'
164
+ },
165
+ body: JSON.stringify({ refreshToken: keycloak.refreshToken })
166
+ });
167
+ ```
168
+
169
+ ## Endpoints recomendados (tú los creas)
170
+
171
+ - POST `/api/auth/validate` → Valida un token
172
+ - POST `/api/auth/user-info` → Obtiene info del usuario
173
+ - POST `/api/auth/refresh` → Renueva el token
174
+ - POST `/api/auth/logout` → Logout
175
+ - GET `/api/auth/protected` → Endpoint protegido
176
+ - GET `/api/auth/admin` → Endpoint con roles
177
+ - GET `/api/auth/public` → Endpoint público
178
+
179
+ ## API Reference
180
+
181
+ ### KeycloakAuthService
182
+
183
+ #### Métodos de Validación
184
+ - `validateToken(token: string): Promise<TokenValidationResult>`
185
+ - `getUserInfo(accessToken: string): Promise<DecodedToken>`
186
+ - `refreshToken(refreshToken: string): Promise<RefreshTokenResult>`
187
+ - `logout(refreshToken: string): Promise<boolean>`
188
+ - `hasRole(userId: string, roleName: string, clientId?: string): Promise<boolean>`
189
+
190
+ ### Decoradores
191
+
192
+ - `@CurrentUser()` - Obtiene el usuario autenticado de la request
193
+ - `@CurrentUser('sub')` - Obtiene una propiedad específica del usuario
194
+
195
+ ### Guards
196
+
197
+ - `@KeycloakAuth()` - Protege rutas con validación de tokens
198
+ - `@KeycloakAuth({ roles: ['admin'] })` - Requiere roles específicos
199
+ - `@KeycloakAuth({ clientRoles: { 'client-id': ['role'] } })` - Requiere roles de cliente
200
+ - `@KeycloakAuth({ optional: true })` - Permite acceso sin token
201
+
202
+ ## Variables de Entorno
203
+
204
+ ```env
205
+ KEYCLOAK_SERVER_URL=http://localhost:8080
206
+ KEYCLOAK_REALM=my-realm
207
+ KEYCLOAK_CLIENT_ID=my-client
208
+ KEYCLOAK_CLIENT_SECRET=my-client-secret
209
+ ```
210
+
211
+ ## Configuración Avanzada
212
+
213
+ ### Opciones de Configuración Completas
214
+
215
+ ```typescript
216
+ const keycloakConfig = {
217
+ serverUrl: 'http://localhost:8080',
218
+ realm: 'my-realm',
219
+ clientId: 'my-client',
220
+ clientSecret: 'my-client-secret',
221
+
222
+ // Clave pública del realm (opcional, se obtiene automáticamente)
223
+ publicKey: '-----BEGIN PUBLIC KEY-----...',
224
+
225
+ // Opciones de validación
226
+ verifyTokenAudience: true,
227
+ verifyTokenIssuer: true,
228
+ tokenExpirationTolerance: 30, // segundos
229
+ };
230
+ ```
231
+
232
+ ## Ejemplos de Uso
233
+
234
+ ### Middleware Personalizado
235
+
236
+ ```typescript
237
+ @Injectable()
238
+ export class KeycloakMiddleware implements NestMiddleware {
239
+ constructor(private keycloakService: KeycloakAuthService) {}
240
+
241
+ async use(req: Request, res: Response, next: Function) {
242
+ const token = req.headers.authorization?.replace('Bearer ', '');
243
+
244
+ if (token) {
245
+ const validation = await this.keycloakService.validateToken(token);
246
+ if (validation.valid) {
247
+ req.user = validation.decoded;
248
+ }
249
+ }
250
+
251
+ next();
252
+ }
253
+ }
254
+ ```
255
+
256
+ ### Hook de React para Next.js
257
+
258
+ ```typescript
259
+ // hooks/useKeycloak.ts
260
+ import { useState, useEffect } from 'react';
261
+ import Keycloak from 'keycloak-js';
262
+
263
+ export const useKeycloak = () => {
264
+ const [keycloak, setKeycloak] = useState<Keycloak | null>(null);
265
+ const [authenticated, setAuthenticated] = useState(false);
266
+ const [loading, setLoading] = useState(true);
267
+
268
+ useEffect(() => {
269
+ const kc = new Keycloak({
270
+ url: process.env.NEXT_PUBLIC_KEYCLOAK_URL,
271
+ realm: process.env.NEXT_PUBLIC_KEYCLOAK_REALM,
272
+ clientId: process.env.NEXT_PUBLIC_KEYCLOAK_CLIENT_ID,
273
+ });
274
+
275
+ kc.init({
276
+ onLoad: 'check-sso',
277
+ silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html',
278
+ }).then((auth) => {
279
+ setKeycloak(kc);
280
+ setAuthenticated(auth);
281
+ setLoading(false);
282
+ });
283
+ }, []);
284
+
285
+ return { keycloak, authenticated, loading };
286
+ };
287
+ ```
288
+
289
+ ## Contribuir
290
+
291
+ 1. Fork el proyecto
292
+ 2. Crea una rama para tu feature (`git checkout -b feature/AmazingFeature`)
293
+ 3. Commit tus cambios (`git commit -m 'Add some AmazingFeature'`)
294
+ 4. Push a la rama (`git push origin feature/AmazingFeature`)
295
+ 5. Abre un Pull Request
296
+
297
+ ## Licencia
298
+
299
+ Este proyecto está bajo la Licencia MIT - ver el archivo [LICENSE](LICENSE) para detalles.
@@ -0,0 +1,3 @@
1
+ import { DecodedToken } from '../interfaces/keycloak-config.interface';
2
+ export declare const CurrentUser: (...dataOrPipes: (keyof DecodedToken | import("@nestjs/common").PipeTransform<any, any> | import("@nestjs/common").Type<import("@nestjs/common").PipeTransform<any, any>> | undefined)[]) => ParameterDecorator;
3
+ //# sourceMappingURL=current-user.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"current-user.decorator.d.ts","sourceRoot":"","sources":["../../src/decorators/current-user.decorator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AAEvE,eAAO,MAAM,WAAW,iNAOvB,CAAC"}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CurrentUser = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ exports.CurrentUser = (0, common_1.createParamDecorator)((data, ctx) => {
6
+ const request = ctx.switchToHttp().getRequest();
7
+ const user = request.user;
8
+ return data ? user?.[data] : user;
9
+ });
10
+ //# sourceMappingURL=current-user.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"current-user.decorator.js","sourceRoot":"","sources":["../../src/decorators/current-user.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAAwE;AAG3D,QAAA,WAAW,GAAG,IAAA,6BAAoB,EAC7C,CAAC,IAAoC,EAAE,GAAqB,EAAsB,EAAE;IAClF,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;IAChD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAE1B,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACpC,CAAC,CACF,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { CanActivate, ExecutionContext } from '@nestjs/common';
2
+ import { Reflector } from '@nestjs/core';
3
+ import { KeycloakAuthService } from '../keycloak-auth.service';
4
+ export interface KeycloakAuthOptions {
5
+ roles?: string[];
6
+ clientRoles?: Record<string, string[]>;
7
+ requireAllRoles?: boolean;
8
+ optional?: boolean;
9
+ }
10
+ export declare const KEYCLOAK_AUTH_KEY = "keycloak_auth";
11
+ export declare const KeycloakAuth: (options?: KeycloakAuthOptions) => (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
12
+ export declare class KeycloakAuthGuard implements CanActivate {
13
+ private reflector;
14
+ private keycloakService;
15
+ constructor(reflector: Reflector, keycloakService: KeycloakAuthService);
16
+ canActivate(context: ExecutionContext): Promise<boolean>;
17
+ }
18
+ //# sourceMappingURL=keycloak-auth.guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycloak-auth.guard.d.ts","sourceRoot":"","sources":["../../src/guards/keycloak-auth.guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,WAAW,EAAE,gBAAgB,EAAyB,MAAM,gBAAgB,CAAC;AAClG,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,MAAM,WAAW,mBAAmB;IAChC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACvC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAEjD,eAAO,MAAM,YAAY,GAAI,UAAS,mBAAwB,MAClD,QAAQ,GAAG,EAAE,aAAa,MAAM,EAAE,YAAY,kBAAkB,uBAI3E,CAAC;AAEF,qBACa,iBAAkB,YAAW,WAAW;IAE7C,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,eAAe;gBADf,SAAS,EAAE,SAAS,EACpB,eAAe,EAAE,mBAAmB;IAG1C,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;CAiEjE"}
@@ -0,0 +1,81 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.KeycloakAuthGuard = exports.KeycloakAuth = exports.KEYCLOAK_AUTH_KEY = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const core_1 = require("@nestjs/core");
15
+ const keycloak_auth_service_1 = require("../keycloak-auth.service");
16
+ exports.KEYCLOAK_AUTH_KEY = 'keycloak_auth';
17
+ const KeycloakAuth = (options = {}) => {
18
+ return (target, propertyKey, descriptor) => {
19
+ Reflect.defineMetadata(exports.KEYCLOAK_AUTH_KEY, options, descriptor.value);
20
+ return descriptor;
21
+ };
22
+ };
23
+ exports.KeycloakAuth = KeycloakAuth;
24
+ let KeycloakAuthGuard = class KeycloakAuthGuard {
25
+ constructor(reflector, keycloakService) {
26
+ this.reflector = reflector;
27
+ this.keycloakService = keycloakService;
28
+ }
29
+ async canActivate(context) {
30
+ const request = context.switchToHttp().getRequest();
31
+ const options = this.reflector.getAllAndOverride(exports.KEYCLOAK_AUTH_KEY, [
32
+ context.getHandler(),
33
+ context.getClass(),
34
+ ]);
35
+ const authHeader = request.headers.authorization;
36
+ if (options?.optional && (!authHeader || !authHeader.startsWith('Bearer '))) {
37
+ return true;
38
+ }
39
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
40
+ throw new common_1.UnauthorizedException('Token de autorización requerido');
41
+ }
42
+ const token = authHeader.substring(7);
43
+ const validationResult = await this.keycloakService.validateToken(token);
44
+ if (!validationResult.valid) {
45
+ throw new common_1.UnauthorizedException(validationResult.error || 'Token inválido');
46
+ }
47
+ request.user = validationResult.decoded;
48
+ if (!options || (!options.roles && !options.clientRoles)) {
49
+ return true;
50
+ }
51
+ if (options.roles && options.roles.length > 0) {
52
+ const userRoles = validationResult.decoded?.realm_access?.roles || [];
53
+ const hasRealmRole = options.requireAllRoles
54
+ ? options.roles.every((role) => userRoles.includes(role))
55
+ : options.roles.some((role) => userRoles.includes(role));
56
+ if (!hasRealmRole) {
57
+ throw new common_1.UnauthorizedException('Roles insuficientes');
58
+ }
59
+ }
60
+ if (options.clientRoles) {
61
+ const userClientRoles = validationResult.decoded?.resource_access || {};
62
+ for (const [clientId, requiredRoles] of Object.entries(options.clientRoles)) {
63
+ const userRoles = userClientRoles[clientId]?.roles || [];
64
+ const hasClientRole = options.requireAllRoles
65
+ ? requiredRoles.every((role) => userRoles.includes(role))
66
+ : requiredRoles.some((role) => userRoles.includes(role));
67
+ if (!hasClientRole) {
68
+ throw new common_1.UnauthorizedException(`Roles insuficientes para el cliente ${clientId}`);
69
+ }
70
+ }
71
+ }
72
+ return true;
73
+ }
74
+ };
75
+ exports.KeycloakAuthGuard = KeycloakAuthGuard;
76
+ exports.KeycloakAuthGuard = KeycloakAuthGuard = __decorate([
77
+ (0, common_1.Injectable)(),
78
+ __metadata("design:paramtypes", [core_1.Reflector,
79
+ keycloak_auth_service_1.KeycloakAuthService])
80
+ ], KeycloakAuthGuard);
81
+ //# sourceMappingURL=keycloak-auth.guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycloak-auth.guard.js","sourceRoot":"","sources":["../../src/guards/keycloak-auth.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAkG;AAClG,uCAAyC;AACzC,oEAA+D;AASlD,QAAA,iBAAiB,GAAG,eAAe,CAAC;AAE1C,MAAM,YAAY,GAAG,CAAC,UAA+B,EAAE,EAAE,EAAE;IAC9D,OAAO,CAAC,MAAW,EAAE,WAAmB,EAAE,UAA8B,EAAE,EAAE;QACxE,OAAO,CAAC,cAAc,CAAC,yBAAiB,EAAE,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;QACrE,OAAO,UAAU,CAAC;IACtB,CAAC,CAAC;AACN,CAAC,CAAC;AALW,QAAA,YAAY,gBAKvB;AAGK,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAC1B,YACY,SAAoB,EACpB,eAAoC;QADpC,cAAS,GAAT,SAAS,CAAW;QACpB,oBAAe,GAAf,eAAe,CAAqB;IAC7C,CAAC;IAEJ,KAAK,CAAC,WAAW,CAAC,OAAyB;QACvC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QACpD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAsB,yBAAiB,EAAE;YACrF,OAAO,CAAC,UAAU,EAAE;YACpB,OAAO,CAAC,QAAQ,EAAE;SACrB,CAAC,CAAC;QAGH,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;QAGjD,IAAI,OAAO,EAAE,QAAQ,IAAI,CAAC,CAAC,UAAU,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;YAC1E,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACnD,MAAM,IAAI,8BAAqB,CAAC,iCAAiC,CAAC,CAAC;QACvE,CAAC;QAED,MAAM,KAAK,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAGtC,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACzE,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC;YAC1B,MAAM,IAAI,8BAAqB,CAAC,gBAAgB,CAAC,KAAK,IAAI,gBAAgB,CAAC,CAAC;QAChF,CAAC;QAGD,OAAO,CAAC,IAAI,GAAG,gBAAgB,CAAC,OAAO,CAAC;QAGxC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;YACvD,OAAO,IAAI,CAAC;QAChB,CAAC;QAGD,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5C,MAAM,SAAS,GAAG,gBAAgB,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC;YACtE,MAAM,YAAY,GAAG,OAAO,CAAC,eAAe;gBACxC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACzD,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;YAE7D,IAAI,CAAC,YAAY,EAAE,CAAC;gBAChB,MAAM,IAAI,8BAAqB,CAAC,qBAAqB,CAAC,CAAC;YAC3D,CAAC;QACL,CAAC;QAGD,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,eAAe,GAAG,gBAAgB,CAAC,OAAO,EAAE,eAAe,IAAI,EAAE,CAAC;YAExE,KAAK,MAAM,CAAC,QAAQ,EAAE,aAAa,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC1E,MAAM,SAAS,GAAG,eAAe,CAAC,QAAQ,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC;gBACzD,MAAM,aAAa,GAAG,OAAO,CAAC,eAAe;oBACzC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;oBACzD,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;gBAE7D,IAAI,CAAC,aAAa,EAAE,CAAC;oBACjB,MAAM,IAAI,8BAAqB,CAAC,uCAAuC,QAAQ,EAAE,CAAC,CAAC;gBACvF,CAAC;YACL,CAAC;QACL,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ,CAAA;AAvEY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAGc,gBAAS;QACH,2CAAmB;GAHvC,iBAAiB,CAuE7B"}
@@ -0,0 +1,6 @@
1
+ export { KeycloakAuthModule } from './keycloak-auth.module';
2
+ export { KeycloakAuthService } from './keycloak-auth.service';
3
+ export { KeycloakAuthGuard, KeycloakAuth } from './guards/keycloak-auth.guard';
4
+ export { CurrentUser } from './decorators/current-user.decorator';
5
+ export * from './interfaces/keycloak-config.interface';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAG5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAG9D,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAG/E,OAAO,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAC;AAGlE,cAAc,wCAAwC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,28 @@
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
+ exports.CurrentUser = exports.KeycloakAuth = exports.KeycloakAuthGuard = exports.KeycloakAuthService = exports.KeycloakAuthModule = void 0;
18
+ var keycloak_auth_module_1 = require("./keycloak-auth.module");
19
+ Object.defineProperty(exports, "KeycloakAuthModule", { enumerable: true, get: function () { return keycloak_auth_module_1.KeycloakAuthModule; } });
20
+ var keycloak_auth_service_1 = require("./keycloak-auth.service");
21
+ Object.defineProperty(exports, "KeycloakAuthService", { enumerable: true, get: function () { return keycloak_auth_service_1.KeycloakAuthService; } });
22
+ var keycloak_auth_guard_1 = require("./guards/keycloak-auth.guard");
23
+ Object.defineProperty(exports, "KeycloakAuthGuard", { enumerable: true, get: function () { return keycloak_auth_guard_1.KeycloakAuthGuard; } });
24
+ Object.defineProperty(exports, "KeycloakAuth", { enumerable: true, get: function () { return keycloak_auth_guard_1.KeycloakAuth; } });
25
+ var current_user_decorator_1 = require("./decorators/current-user.decorator");
26
+ Object.defineProperty(exports, "CurrentUser", { enumerable: true, get: function () { return current_user_decorator_1.CurrentUser; } });
27
+ __exportStar(require("./interfaces/keycloak-config.interface"), exports);
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AACA,+DAA4D;AAAnD,0HAAA,kBAAkB,OAAA;AAG3B,iEAA8D;AAArD,4HAAA,mBAAmB,OAAA;AAG5B,oEAA+E;AAAtE,wHAAA,iBAAiB,OAAA;AAAE,mHAAA,YAAY,OAAA;AAGxC,8EAAkE;AAAzD,qHAAA,WAAW,OAAA;AAGpB,yEAAuD"}
@@ -0,0 +1,56 @@
1
+ export interface KeycloakConfig {
2
+ serverUrl: string;
3
+ realm: string;
4
+ clientId: string;
5
+ clientSecret?: string;
6
+ publicKey?: string;
7
+ verifyTokenAudience?: boolean;
8
+ verifyTokenIssuer?: boolean;
9
+ tokenExpirationTolerance?: number;
10
+ }
11
+ export interface DecodedToken {
12
+ sub: string;
13
+ iss: string;
14
+ aud: string | string[];
15
+ exp: number;
16
+ iat: number;
17
+ jti?: string;
18
+ azp?: string;
19
+ scope?: string;
20
+ realm_access?: {
21
+ roles: string[];
22
+ };
23
+ resource_access?: {
24
+ [clientId: string]: {
25
+ roles: string[];
26
+ };
27
+ };
28
+ preferred_username?: string;
29
+ email?: string;
30
+ email_verified?: boolean;
31
+ name?: string;
32
+ given_name?: string;
33
+ family_name?: string;
34
+ }
35
+ export interface TokenValidationResult {
36
+ valid: boolean;
37
+ decoded?: DecodedToken;
38
+ error?: string;
39
+ expired?: boolean;
40
+ invalidSignature?: boolean;
41
+ invalidAudience?: boolean;
42
+ invalidIssuer?: boolean;
43
+ }
44
+ export interface RefreshTokenResult {
45
+ success: boolean;
46
+ token?: {
47
+ access_token: string;
48
+ refresh_token?: string;
49
+ token_type: string;
50
+ expires_in: number;
51
+ scope?: string;
52
+ id_token?: string;
53
+ };
54
+ error?: string;
55
+ }
56
+ //# sourceMappingURL=keycloak-config.interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycloak-config.interface.d.ts","sourceRoot":"","sources":["../../src/interfaces/keycloak-config.interface.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE;QACb,KAAK,EAAE,MAAM,EAAE,CAAC;KACjB,CAAC;IACF,eAAe,CAAC,EAAE;QAChB,CAAC,QAAQ,EAAE,MAAM,GAAG;YAClB,KAAK,EAAE,MAAM,EAAE,CAAC;SACjB,CAAC;KACH,CAAC;IACF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QACN,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=keycloak-config.interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycloak-config.interface.js","sourceRoot":"","sources":["../../src/interfaces/keycloak-config.interface.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ import { DynamicModule } from '@nestjs/common';
2
+ export declare class KeycloakAuthModule {
3
+ static forRootFromEnv(): DynamicModule;
4
+ }
5
+ //# sourceMappingURL=keycloak-auth.module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycloak-auth.module.d.ts","sourceRoot":"","sources":["../src/keycloak-auth.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAMvD,qBACa,kBAAkB;IAC3B,MAAM,CAAC,cAAc,IAAI,aAAa;CAuBzC"}
@@ -0,0 +1,44 @@
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 KeycloakAuthModule_1;
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.KeycloakAuthModule = void 0;
11
+ const common_1 = require("@nestjs/common");
12
+ const config_1 = require("@nestjs/config");
13
+ const keycloak_auth_service_1 = require("./keycloak-auth.service");
14
+ const keycloak_auth_guard_1 = require("./guards/keycloak-auth.guard");
15
+ let KeycloakAuthModule = KeycloakAuthModule_1 = class KeycloakAuthModule {
16
+ static forRootFromEnv() {
17
+ return {
18
+ module: KeycloakAuthModule_1,
19
+ imports: [config_1.ConfigModule],
20
+ providers: [
21
+ {
22
+ provide: 'KEYCLOAK_CONFIG',
23
+ useFactory: (configService) => ({
24
+ serverUrl: configService.get('KEYCLOAK_SERVER_URL', 'http://localhost:8080'),
25
+ realm: configService.get('KEYCLOAK_REALM', 'my-realm'),
26
+ clientId: configService.get('KEYCLOAK_CLIENT_ID', 'backend'),
27
+ clientSecret: configService.get('KEYCLOAK_CLIENT_SECRET'),
28
+ verifyTokenIssuer: configService.get('KEYCLOAK_VERIFY_ISSUER', 'true') === 'true',
29
+ verifyTokenAudience: configService.get('KEYCLOAK_VERIFY_AUDIENCE', 'true') === 'true',
30
+ }),
31
+ inject: [config_1.ConfigService],
32
+ },
33
+ keycloak_auth_service_1.KeycloakAuthService,
34
+ keycloak_auth_guard_1.KeycloakAuthGuard,
35
+ ],
36
+ exports: [keycloak_auth_service_1.KeycloakAuthService, keycloak_auth_guard_1.KeycloakAuthGuard],
37
+ };
38
+ }
39
+ };
40
+ exports.KeycloakAuthModule = KeycloakAuthModule;
41
+ exports.KeycloakAuthModule = KeycloakAuthModule = KeycloakAuthModule_1 = __decorate([
42
+ (0, common_1.Module)({})
43
+ ], KeycloakAuthModule);
44
+ //# sourceMappingURL=keycloak-auth.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycloak-auth.module.js","sourceRoot":"","sources":["../src/keycloak-auth.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAuD;AACvD,2CAA6D;AAE7D,mEAA8D;AAC9D,sEAAiE;AAG1D,IAAM,kBAAkB,0BAAxB,MAAM,kBAAkB;IAC3B,MAAM,CAAC,cAAc;QACjB,OAAO;YACH,MAAM,EAAE,oBAAkB;YAC1B,OAAO,EAAE,CAAC,qBAAY,CAAC;YACvB,SAAS,EAAE;gBACP;oBACI,OAAO,EAAE,iBAAiB;oBAC1B,UAAU,EAAE,CAAC,aAA4B,EAAkB,EAAE,CAAC,CAAC;wBAC3D,SAAS,EAAE,aAAa,CAAC,GAAG,CAAS,qBAAqB,EAAE,uBAAuB,CAAC;wBACpF,KAAK,EAAE,aAAa,CAAC,GAAG,CAAS,gBAAgB,EAAE,UAAU,CAAC;wBAC9D,QAAQ,EAAE,aAAa,CAAC,GAAG,CAAS,oBAAoB,EAAE,SAAS,CAAC;wBACpE,YAAY,EAAE,aAAa,CAAC,GAAG,CAAS,wBAAwB,CAAC;wBACjE,iBAAiB,EAAE,aAAa,CAAC,GAAG,CAAS,wBAAwB,EAAE,MAAM,CAAC,KAAK,MAAM;wBACzF,mBAAmB,EAAE,aAAa,CAAC,GAAG,CAAS,0BAA0B,EAAE,MAAM,CAAC,KAAK,MAAM;qBAChG,CAAC;oBACF,MAAM,EAAE,CAAC,sBAAa,CAAC;iBAC1B;gBACD,2CAAmB;gBACnB,uCAAiB;aACpB;YACD,OAAO,EAAE,CAAC,2CAAmB,EAAE,uCAAiB,CAAC;SACpD,CAAC;IACN,CAAC;CACJ,CAAA;AAxBY,gDAAkB;6BAAlB,kBAAkB;IAD9B,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,kBAAkB,CAwB9B"}