@miguelmorales13/nestkit 0.5.1 → 0.6.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.
- package/README.md +81 -2
- package/dist/auth/index.cjs +12 -70
- package/dist/auth/index.js +11 -69
- package/dist/auth/oauth/http.d.ts +38 -0
- package/dist/auth/oauth/index.cjs +610 -0
- package/dist/auth/oauth/index.d.ts +12 -0
- package/dist/auth/oauth/index.js +610 -0
- package/dist/auth/oauth/oauth-auth.module.d.ts +28 -0
- package/dist/auth/oauth/oauth-auth.service.d.ts +101 -0
- package/dist/auth/oauth/oauth-callback.filter.d.ts +20 -0
- package/dist/auth/oauth/oauth-provider.d.ts +73 -0
- package/dist/auth/oauth/oauth.controller.d.ts +38 -0
- package/dist/auth/oauth/oauth.options.d.ts +89 -0
- package/dist/auth/oauth/oauth.ports.d.ts +86 -0
- package/dist/chunk-A3B2EY4V.js +75 -0
- package/dist/chunk-NVCI3CQI.cjs +75 -0
- package/dist/index.cjs +8 -8
- package/dist/index.js +10 -10
- package/dist/umami/index.cjs +109 -0
- package/dist/umami/index.d.ts +4 -0
- package/dist/umami/index.js +109 -0
- package/dist/umami/umami.module.d.ts +6 -0
- package/dist/umami/umami.options.d.ts +20 -0
- package/dist/umami/umami.service.d.ts +40 -0
- package/dist/umami/umami.types.d.ts +24 -0
- package/package.json +11 -1
package/README.md
CHANGED
|
@@ -628,6 +628,81 @@ necesitás otra lógica de roles al alta (ej. el primer usuario de un tenant nue
|
|
|
628
628
|
`BaseAuthService` y sobreescribí `register()`, o llamá `users.create()` directo con los roles que
|
|
629
629
|
quieras desde tu propio controller.
|
|
630
630
|
|
|
631
|
+
### `auth/oauth` — entrar con Google o Facebook, sin Passport
|
|
632
|
+
|
|
633
|
+
Lo de arriba es email + contraseña. Esto es lo otro: alta y acceso delegados a un proveedor,
|
|
634
|
+
vinculación de cuentas, y sesiones con refresh rotativo de un solo uso guardado hasheado. Son
|
|
635
|
+
módulos separados y se pueden usar los dos, uno, o ninguno.
|
|
636
|
+
|
|
637
|
+
**No usa Passport, a propósito.** Su paso de redirección llama a `res.setHeader()`, que no existe
|
|
638
|
+
en el reply de Fastify, así que el inicio del flujo hay que armarlo a mano igual; y su mapeo de
|
|
639
|
+
perfiles es una tabla de traducción no documentada que reenvía tal cual los nombres de campo que no
|
|
640
|
+
reconoce —así es como `photos.type(large)` llega a la Graph API de Facebook pidiendo los álbumes
|
|
641
|
+
subidos por la persona, que necesitan un permiso que la app no tiene, y el callback muere con un
|
|
642
|
+
500. El flujo entero son unas cuarenta líneas de `fetch` y cada campo que se manda se ve en
|
|
643
|
+
`oauth-provider.ts`.
|
|
644
|
+
|
|
645
|
+
Un proveedor es un objeto, no una clase que heredar: agregar Apple o GitHub es declarar cuatro URLs
|
|
646
|
+
y una función que mapee su JSON a `OAuthProfile`.
|
|
647
|
+
|
|
648
|
+
```ts
|
|
649
|
+
import {
|
|
650
|
+
OAuthAuthModule, OAUTH_STORE, REFRESH_TOKEN_STORE,
|
|
651
|
+
googleProvider, facebookProvider, createOAuthController,
|
|
652
|
+
} from '@miguelmorales13/nestkit/auth/oauth';
|
|
653
|
+
|
|
654
|
+
@Module({
|
|
655
|
+
imports: [
|
|
656
|
+
MiStoreModule, // exporta la clase que implementa los puertos
|
|
657
|
+
OAuthAuthModule.forRoot({
|
|
658
|
+
imports: [MiStoreModule],
|
|
659
|
+
providers: [
|
|
660
|
+
{ provide: OAUTH_STORE, useExisting: MiStore },
|
|
661
|
+
{ provide: REFRESH_TOKEN_STORE, useExisting: MiStore },
|
|
662
|
+
],
|
|
663
|
+
options: {
|
|
664
|
+
providers: [googleProvider(), facebookProvider()],
|
|
665
|
+
webUrl: process.env.WEB_URL!,
|
|
666
|
+
cookie: { path: '/api/auth' }, // acotá la cookie a las rutas que la usan
|
|
667
|
+
mapUser: (u) => ({ id: u.id, email: u.email, name: u.name }),
|
|
668
|
+
},
|
|
669
|
+
}),
|
|
670
|
+
],
|
|
671
|
+
controllers: [class AuthController extends createOAuthController() {}],
|
|
672
|
+
})
|
|
673
|
+
export class AuthModule {}
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
Eso da `GET /auth/:provider`, `GET /auth/:provider/callback`, `POST /auth/refresh`,
|
|
677
|
+
`POST /auth/logout`, `GET /auth/me`, `GET /auth/linked-accounts` y `POST /auth/link-intent`. El
|
|
678
|
+
proveedor va como parámetro de ruta y no una pareja de rutas por proveedor, así que sumar Apple
|
|
679
|
+
después toca solo configuración.
|
|
680
|
+
|
|
681
|
+
**Los dos puertos van separados** (`OAuthStorePort` para identidades, `RefreshTokenStorePort` para
|
|
682
|
+
sesiones) porque responden preguntas distintas y un proyecto puede querer las sesiones en Redis. Si
|
|
683
|
+
van a la misma base, una sola clase implementa los dos y se atan con `useExisting`, como arriba.
|
|
684
|
+
|
|
685
|
+
`revokeIfActive` **tiene que ser atómico** —un UPDATE condicional, no un leer-y-escribir—: esa
|
|
686
|
+
fila es lo único que impide que un refresh token robado se use dos veces.
|
|
687
|
+
|
|
688
|
+
Personalizable sin tocar la librería: nombre y path de la cookie, TTLs, `sameSite`, a dónde vuelve
|
|
689
|
+
el navegador tras entrar / fallar / vincular, si un login cierra las demás sesiones
|
|
690
|
+
(`singleSession`, por defecto sí), qué campos del usuario salen por la API (`mapUser`), el guard de
|
|
691
|
+
las rutas con sesión (`guard`, si la app ya tiene el suyo) y todos los textos que ve el usuario.
|
|
692
|
+
|
|
693
|
+
`syncProfile` es la opción que casi siempre hay que pasar en cuanto la app deja elegir foto de
|
|
694
|
+
perfil: por defecto resincroniza nombre y foto del proveedor en cada login, lo que deshace en
|
|
695
|
+
silencio la elección de la persona la próxima vez que entre.
|
|
696
|
+
|
|
697
|
+
Sin email no hay alta. Facebook puede legítimamente no darlo —cuenta registrada con teléfono, o
|
|
698
|
+
permiso retirado en la pantalla de consentimiento—, y en ese caso se corta con un mensaje que la
|
|
699
|
+
persona puede accionar, no con un fallo interno. `OAuthCallbackFilter` se encarga de que cualquier
|
|
700
|
+
error del callback devuelva al navegador a la pantalla de acceso: sin él la persona se queda
|
|
701
|
+
mirando un JSON en una página en blanco, porque no llegó ahí por una petición de la app sino
|
|
702
|
+
redirigida desde el proveedor.
|
|
703
|
+
|
|
704
|
+
Requiere cookies en el adaptador HTTP: `@fastify/cookie` o `cookie-parser`.
|
|
705
|
+
|
|
631
706
|
### `email/resend` y `email/nodemailer` — dos transportes, elegís uno por proyecto
|
|
632
707
|
|
|
633
708
|
Mismo patrón que `database/postgres` vs `database/supabase`: dos módulos independientes, no una
|
|
@@ -741,6 +816,8 @@ usa Bunny Stream/TUS para video grande.
|
|
|
741
816
|
| `@miguelmorales13/nestkit/whatsapp` | `WhatsAppModule`, `WHATSAPP_CLIENT`, `WhatsAppClient` |
|
|
742
817
|
| `@miguelmorales13/nestkit/stripe` | `StripeModule`, `STRIPE_CLIENT`, `createStripeWebhookController` |
|
|
743
818
|
| `@miguelmorales13/nestkit/auth` | `AuthUserPort`, `BaseAuthService`, `createAuthController`, `TokenService`, `JwtAuthGuard`, `RolesGuard`, `RequireTenantGuard`, `CurrentUser`, `CurrentTenant`, `Roles` |
|
|
819
|
+
| `@miguelmorales13/nestkit/auth/oauth` | `OAuthAuthModule`, `OAuthAuthService`, `createOAuthController`, `googleProvider`, `facebookProvider`, `OAuthStorePort`, `RefreshTokenStorePort` |
|
|
820
|
+
| `@miguelmorales13/nestkit/umami` | `UmamiModule`, `UmamiService` — lectura de la API de analítica |
|
|
744
821
|
| `@miguelmorales13/nestkit/storage` | `StorageModule`, `STORAGE`, `StoragePort`, adaptadores `LocalStorage`/`S3Storage`/`BunnyStorage` |
|
|
745
822
|
| `@miguelmorales13/nestkit/email/resend` | `ResendModule`, `RESEND_CLIENT` |
|
|
746
823
|
| `@miguelmorales13/nestkit/email/nodemailer` | `NodemailerModule`, `NODEMAILER_TRANSPORT` |
|
|
@@ -749,5 +826,7 @@ usa Bunny Stream/TUS para video grande.
|
|
|
749
826
|
|
|
750
827
|
## Estado del paquete
|
|
751
828
|
|
|
752
|
-
`0.
|
|
753
|
-
|
|
829
|
+
`0.6.0`. Sin adaptador Mongo real. Sin tests unitarios propios.
|
|
830
|
+
|
|
831
|
+
`auth/oauth` y `umami` sí tienen un consumidor real: **nutrimx** corre su acceso con Google y
|
|
832
|
+
Facebook sobre este módulo en producción. El resto sigue sin ejercitarse de verdad.
|
package/dist/auth/index.cjs
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
var _chunkNVCI3CQIcjs = require('../chunk-NVCI3CQI.cjs');
|
|
6
|
+
|
|
2
7
|
|
|
3
8
|
|
|
4
9
|
|
|
@@ -19,65 +24,8 @@ function verifyPassword(plain, passwordHash) {
|
|
|
19
24
|
return _bcryptjs.compare.call(void 0, plain, passwordHash);
|
|
20
25
|
}
|
|
21
26
|
|
|
22
|
-
// src/auth/token.service.ts
|
|
23
|
-
var _jsonwebtoken = require('jsonwebtoken'); var _jsonwebtoken2 = _interopRequireDefault(_jsonwebtoken);
|
|
24
|
-
function requireEnv(name) {
|
|
25
|
-
const value = process.env[name];
|
|
26
|
-
if (!value) {
|
|
27
|
-
throw new Error(`TokenService: ${name} environment variable is not set`);
|
|
28
|
-
}
|
|
29
|
-
return value;
|
|
30
|
-
}
|
|
31
|
-
var TokenService = class {
|
|
32
|
-
signAccessToken(payload) {
|
|
33
|
-
return _jsonwebtoken2.default.sign(payload, requireEnv("JWT_ACCESS_SECRET"), {
|
|
34
|
-
expiresIn: _nullishCoalesce(process.env.JWT_ACCESS_EXPIRES_IN, () => ( "15m"))
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
signRefreshToken(payload) {
|
|
38
|
-
return _jsonwebtoken2.default.sign(payload, requireEnv("JWT_REFRESH_SECRET"), {
|
|
39
|
-
expiresIn: _nullishCoalesce(process.env.JWT_REFRESH_EXPIRES_IN, () => ( "30d"))
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
verifyAccessToken(token) {
|
|
43
|
-
try {
|
|
44
|
-
return _jsonwebtoken2.default.verify(token, requireEnv("JWT_ACCESS_SECRET"));
|
|
45
|
-
} catch (e) {
|
|
46
|
-
throw new (0, _chunkFDNGAYTZcjs.UnauthorizedAppException)("Invalid or expired access token");
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
verifyRefreshToken(token) {
|
|
50
|
-
try {
|
|
51
|
-
return _jsonwebtoken2.default.verify(token, requireEnv("JWT_REFRESH_SECRET"));
|
|
52
|
-
} catch (e2) {
|
|
53
|
-
throw new (0, _chunkFDNGAYTZcjs.UnauthorizedAppException)("Invalid or expired refresh token");
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
// src/auth/jwt-auth.guard.ts
|
|
59
|
-
var _common = require('@nestjs/common');
|
|
60
|
-
var JwtAuthGuard = class {
|
|
61
|
-
constructor() {
|
|
62
|
-
this.tokens = new TokenService();
|
|
63
|
-
}
|
|
64
|
-
canActivate(context) {
|
|
65
|
-
const req = context.switchToHttp().getRequest();
|
|
66
|
-
const header = req.headers.authorization;
|
|
67
|
-
const token = _optionalChain([header, 'optionalAccess', _2 => _2.startsWith, 'call', _3 => _3("Bearer ")]) ? header.slice("Bearer ".length) : void 0;
|
|
68
|
-
if (!token) {
|
|
69
|
-
throw new (0, _chunkFDNGAYTZcjs.UnauthorizedAppException)("Missing Authorization bearer token");
|
|
70
|
-
}
|
|
71
|
-
req.user = this.tokens.verifyAccessToken(token);
|
|
72
|
-
return true;
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
JwtAuthGuard = exports.JwtAuthGuard = _chunk2REOCMUDcjs.__decorateClass.call(void 0, [
|
|
76
|
-
_common.Injectable.call(void 0, )
|
|
77
|
-
], JwtAuthGuard);
|
|
78
|
-
|
|
79
27
|
// src/auth/roles.decorator.ts
|
|
80
|
-
|
|
28
|
+
var _common = require('@nestjs/common');
|
|
81
29
|
var ROLES_KEY = "nestkit:roles";
|
|
82
30
|
var Roles = (...roles) => _common.SetMetadata.call(void 0, ROLES_KEY, roles);
|
|
83
31
|
|
|
@@ -95,7 +43,7 @@ var RolesGuard = class {
|
|
|
95
43
|
]);
|
|
96
44
|
if (!required || required.length === 0) return true;
|
|
97
45
|
const req = context.switchToHttp().getRequest();
|
|
98
|
-
const has = _nullishCoalesce(_optionalChain([req, 'access',
|
|
46
|
+
const has = _nullishCoalesce(_optionalChain([req, 'access', _2 => _2.user, 'optionalAccess', _3 => _3.roles, 'access', _4 => _4.some, 'call', _5 => _5((role) => required.includes(role))]), () => ( false));
|
|
99
47
|
if (!has) {
|
|
100
48
|
throw new (0, _chunkFDNGAYTZcjs.ForbiddenAppException)("Insufficient role");
|
|
101
49
|
}
|
|
@@ -106,16 +54,10 @@ RolesGuard = exports.RolesGuard = _chunk2REOCMUDcjs.__decorateClass.call(void 0,
|
|
|
106
54
|
_common.Injectable.call(void 0, )
|
|
107
55
|
], RolesGuard);
|
|
108
56
|
|
|
109
|
-
// src/auth/current-user.decorator.ts
|
|
110
|
-
|
|
111
|
-
var CurrentUser = _common.createParamDecorator.call(void 0,
|
|
112
|
-
(_, ctx) => ctx.switchToHttp().getRequest().user
|
|
113
|
-
);
|
|
114
|
-
|
|
115
57
|
// src/auth/current-tenant.decorator.ts
|
|
116
58
|
|
|
117
59
|
var CurrentTenant = _common.createParamDecorator.call(void 0,
|
|
118
|
-
(_, ctx) => _optionalChain([ctx, 'access',
|
|
60
|
+
(_, ctx) => _optionalChain([ctx, 'access', _6 => _6.switchToHttp, 'call', _7 => _7(), 'access', _8 => _8.getRequest, 'call', _9 => _9(), 'access', _10 => _10.user, 'optionalAccess', _11 => _11.tenantId])
|
|
119
61
|
);
|
|
120
62
|
|
|
121
63
|
// src/auth/require-tenant.guard.ts
|
|
@@ -123,7 +65,7 @@ var CurrentTenant = _common.createParamDecorator.call(void 0,
|
|
|
123
65
|
var RequireTenantGuard = class {
|
|
124
66
|
canActivate(context) {
|
|
125
67
|
const req = context.switchToHttp().getRequest();
|
|
126
|
-
if (!_optionalChain([req, 'access',
|
|
68
|
+
if (!_optionalChain([req, 'access', _12 => _12.user, 'optionalAccess', _13 => _13.tenantId])) {
|
|
127
69
|
throw new (0, _chunkFDNGAYTZcjs.ForbiddenAppException)("This action requires a company account");
|
|
128
70
|
}
|
|
129
71
|
return true;
|
|
@@ -164,7 +106,7 @@ _chunk2REOCMUDcjs.__decorateClass.call(void 0, [
|
|
|
164
106
|
|
|
165
107
|
// src/auth/auth.service.ts
|
|
166
108
|
var BaseAuthService = class {
|
|
167
|
-
constructor(users, tokens = new TokenService()) {
|
|
109
|
+
constructor(users, tokens = new (0, _chunkNVCI3CQIcjs.TokenService)()) {
|
|
168
110
|
this.users = users;
|
|
169
111
|
this.tokens = tokens;
|
|
170
112
|
}
|
|
@@ -262,4 +204,4 @@ function createAuthController(serviceToken, options = {}) {
|
|
|
262
204
|
|
|
263
205
|
|
|
264
206
|
|
|
265
|
-
exports.BaseAuthService = BaseAuthService; exports.CurrentTenant = CurrentTenant; exports.CurrentUser = CurrentUser; exports.JwtAuthGuard = JwtAuthGuard; exports.LoginDto = LoginDto; exports.ROLES_KEY = ROLES_KEY; exports.RefreshDto = RefreshDto; exports.RegisterDto = RegisterDto; exports.RequireTenantGuard = RequireTenantGuard; exports.Roles = Roles; exports.RolesGuard = RolesGuard; exports.TokenService = TokenService; exports.createAuthController = createAuthController; exports.hashPassword = hashPassword; exports.verifyPassword = verifyPassword;
|
|
207
|
+
exports.BaseAuthService = BaseAuthService; exports.CurrentTenant = CurrentTenant; exports.CurrentUser = _chunkNVCI3CQIcjs.CurrentUser; exports.JwtAuthGuard = _chunkNVCI3CQIcjs.JwtAuthGuard; exports.LoginDto = LoginDto; exports.ROLES_KEY = ROLES_KEY; exports.RefreshDto = RefreshDto; exports.RegisterDto = RegisterDto; exports.RequireTenantGuard = RequireTenantGuard; exports.Roles = Roles; exports.RolesGuard = RolesGuard; exports.TokenService = _chunkNVCI3CQIcjs.TokenService; exports.createAuthController = createAuthController; exports.hashPassword = hashPassword; exports.verifyPassword = verifyPassword;
|
package/dist/auth/index.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CurrentUser,
|
|
3
|
+
JwtAuthGuard,
|
|
4
|
+
TokenService
|
|
5
|
+
} from "../chunk-A3B2EY4V.js";
|
|
1
6
|
import {
|
|
2
7
|
ConflictAppException,
|
|
3
8
|
ForbiddenAppException,
|
|
@@ -19,70 +24,13 @@ function verifyPassword(plain, passwordHash) {
|
|
|
19
24
|
return compare(plain, passwordHash);
|
|
20
25
|
}
|
|
21
26
|
|
|
22
|
-
// src/auth/token.service.ts
|
|
23
|
-
import jwt from "jsonwebtoken";
|
|
24
|
-
function requireEnv(name) {
|
|
25
|
-
const value = process.env[name];
|
|
26
|
-
if (!value) {
|
|
27
|
-
throw new Error(`TokenService: ${name} environment variable is not set`);
|
|
28
|
-
}
|
|
29
|
-
return value;
|
|
30
|
-
}
|
|
31
|
-
var TokenService = class {
|
|
32
|
-
signAccessToken(payload) {
|
|
33
|
-
return jwt.sign(payload, requireEnv("JWT_ACCESS_SECRET"), {
|
|
34
|
-
expiresIn: process.env.JWT_ACCESS_EXPIRES_IN ?? "15m"
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
signRefreshToken(payload) {
|
|
38
|
-
return jwt.sign(payload, requireEnv("JWT_REFRESH_SECRET"), {
|
|
39
|
-
expiresIn: process.env.JWT_REFRESH_EXPIRES_IN ?? "30d"
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
verifyAccessToken(token) {
|
|
43
|
-
try {
|
|
44
|
-
return jwt.verify(token, requireEnv("JWT_ACCESS_SECRET"));
|
|
45
|
-
} catch {
|
|
46
|
-
throw new UnauthorizedAppException("Invalid or expired access token");
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
verifyRefreshToken(token) {
|
|
50
|
-
try {
|
|
51
|
-
return jwt.verify(token, requireEnv("JWT_REFRESH_SECRET"));
|
|
52
|
-
} catch {
|
|
53
|
-
throw new UnauthorizedAppException("Invalid or expired refresh token");
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
// src/auth/jwt-auth.guard.ts
|
|
59
|
-
import { Injectable } from "@nestjs/common";
|
|
60
|
-
var JwtAuthGuard = class {
|
|
61
|
-
constructor() {
|
|
62
|
-
this.tokens = new TokenService();
|
|
63
|
-
}
|
|
64
|
-
canActivate(context) {
|
|
65
|
-
const req = context.switchToHttp().getRequest();
|
|
66
|
-
const header = req.headers.authorization;
|
|
67
|
-
const token = header?.startsWith("Bearer ") ? header.slice("Bearer ".length) : void 0;
|
|
68
|
-
if (!token) {
|
|
69
|
-
throw new UnauthorizedAppException("Missing Authorization bearer token");
|
|
70
|
-
}
|
|
71
|
-
req.user = this.tokens.verifyAccessToken(token);
|
|
72
|
-
return true;
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
JwtAuthGuard = __decorateClass([
|
|
76
|
-
Injectable()
|
|
77
|
-
], JwtAuthGuard);
|
|
78
|
-
|
|
79
27
|
// src/auth/roles.decorator.ts
|
|
80
28
|
import { SetMetadata } from "@nestjs/common";
|
|
81
29
|
var ROLES_KEY = "nestkit:roles";
|
|
82
30
|
var Roles = (...roles) => SetMetadata(ROLES_KEY, roles);
|
|
83
31
|
|
|
84
32
|
// src/auth/roles.guard.ts
|
|
85
|
-
import { Injectable
|
|
33
|
+
import { Injectable } from "@nestjs/common";
|
|
86
34
|
import "@nestjs/core";
|
|
87
35
|
var RolesGuard = class {
|
|
88
36
|
constructor(reflector) {
|
|
@@ -103,23 +51,17 @@ var RolesGuard = class {
|
|
|
103
51
|
}
|
|
104
52
|
};
|
|
105
53
|
RolesGuard = __decorateClass([
|
|
106
|
-
|
|
54
|
+
Injectable()
|
|
107
55
|
], RolesGuard);
|
|
108
56
|
|
|
109
|
-
// src/auth/current-user.decorator.ts
|
|
110
|
-
import { createParamDecorator } from "@nestjs/common";
|
|
111
|
-
var CurrentUser = createParamDecorator(
|
|
112
|
-
(_, ctx) => ctx.switchToHttp().getRequest().user
|
|
113
|
-
);
|
|
114
|
-
|
|
115
57
|
// src/auth/current-tenant.decorator.ts
|
|
116
|
-
import { createParamDecorator
|
|
117
|
-
var CurrentTenant =
|
|
58
|
+
import { createParamDecorator } from "@nestjs/common";
|
|
59
|
+
var CurrentTenant = createParamDecorator(
|
|
118
60
|
(_, ctx) => ctx.switchToHttp().getRequest().user?.tenantId
|
|
119
61
|
);
|
|
120
62
|
|
|
121
63
|
// src/auth/require-tenant.guard.ts
|
|
122
|
-
import { Injectable as
|
|
64
|
+
import { Injectable as Injectable2 } from "@nestjs/common";
|
|
123
65
|
var RequireTenantGuard = class {
|
|
124
66
|
canActivate(context) {
|
|
125
67
|
const req = context.switchToHttp().getRequest();
|
|
@@ -130,7 +72,7 @@ var RequireTenantGuard = class {
|
|
|
130
72
|
}
|
|
131
73
|
};
|
|
132
74
|
RequireTenantGuard = __decorateClass([
|
|
133
|
-
|
|
75
|
+
Injectable2()
|
|
134
76
|
], RequireTenantGuard);
|
|
135
77
|
|
|
136
78
|
// src/auth/auth.dto.ts
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The little the OAuth controller needs from the HTTP layer, described
|
|
3
|
+
* structurally so the module works on Fastify and Express without depending
|
|
4
|
+
* on either.
|
|
5
|
+
*
|
|
6
|
+
* Cookie support is not built in on either adapter — the app has to register
|
|
7
|
+
* `@fastify/cookie` or `cookie-parser`. Fastify names the methods
|
|
8
|
+
* `setCookie`/`clearCookie`, Express names them `cookie`/`clearCookie`; the
|
|
9
|
+
* helpers below paper over exactly that difference and nothing else.
|
|
10
|
+
*/
|
|
11
|
+
export interface CookieOptions {
|
|
12
|
+
httpOnly?: boolean;
|
|
13
|
+
secure?: boolean;
|
|
14
|
+
sameSite?: 'lax' | 'strict' | 'none';
|
|
15
|
+
path?: string;
|
|
16
|
+
expires?: Date;
|
|
17
|
+
}
|
|
18
|
+
export interface ReplyLike {
|
|
19
|
+
redirect: ((url: string, statusCode?: number) => unknown) & ((...args: never[]) => unknown);
|
|
20
|
+
setCookie?(name: string, value: string, options?: CookieOptions): unknown;
|
|
21
|
+
cookie?(name: string, value: string, options?: CookieOptions): unknown;
|
|
22
|
+
clearCookie?(name: string, options?: CookieOptions): unknown;
|
|
23
|
+
}
|
|
24
|
+
export interface RequestLike {
|
|
25
|
+
cookies?: Record<string, string | undefined>;
|
|
26
|
+
query?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
export declare function setCookie(reply: ReplyLike, name: string, value: string, options: CookieOptions): void;
|
|
29
|
+
export declare function clearCookie(reply: ReplyLike, name: string, options: CookieOptions): void;
|
|
30
|
+
/**
|
|
31
|
+
* Redirects with an explicit 302.
|
|
32
|
+
*
|
|
33
|
+
* Not optional: Nest's Fastify adapter answers `reply.redirect(url)` with a
|
|
34
|
+
* 200 carrying a Location header, which no browser follows — the sign-in
|
|
35
|
+
* simply stops on a blank page. Express ignores the second argument when the
|
|
36
|
+
* first is a URL, so the same call is correct on both.
|
|
37
|
+
*/
|
|
38
|
+
export declare function redirect(reply: ReplyLike, url: string): void;
|