@jmlq/auth-plugin-jose 0.0.1-alpha.9 → 0.0.1-beta.1

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.es.md ADDED
@@ -0,0 +1,197 @@
1
+ # @jmlq/auth-plugin-jose 🧩
2
+
3
+ ## 🎯 Objetivo
4
+
5
+ `@jmlq/auth-plugin-jose` es el plugin de infraestructura que implementa `ITokenServicePort` de `@jmlq/auth` usando la librería `jose`.
6
+
7
+ Su responsabilidad real es:
8
+
9
+ - generar access tokens y refresh tokens,
10
+ - verificar tokens firmados,
11
+ - normalizar configuración criptográfica,
12
+ - traducir errores técnicos de `jose` a errores compatibles con el core.
13
+
14
+ El punto de entrada recomendado es `createJoseTokenService(...)`.
15
+
16
+ ## ⭐ Importancia
17
+
18
+ Este plugin permite que el core `@jmlq/auth` siga siendo independiente de la tecnología JWT concreta.
19
+
20
+ En la práctica:
21
+
22
+ - el core depende de `ITokenServicePort`,
23
+ - este plugin implementa ese contrato,
24
+ - el host decide si usa `HS256`, `RS256` o `ES256`,
25
+ - la lógica de negocio permanece en `@jmlq/auth`.
26
+
27
+ ## 🏗️ Arquitectura (visión rápida)
28
+
29
+ - `createJoseTokenService(...)` valida precondiciones mínimas y construye `JoseTokenService`.
30
+ - `JoseTokenService` encapsula `SignJWT`, `jwtVerify`, `decodeJwt`, `importPKCS8` e `importSPKI`.
31
+ - `jose-error.mapper.ts` traduce errores técnicos a códigos del core.
32
+ - `audience` se usa **solo para emisión**; en verificación no se pasa a `jwtVerify`.
33
+
34
+ ➡️ Ver detalle en: [architecture.md](./docs/es/architecture.md)
35
+
36
+ ## 🔧 Implementación
37
+
38
+ ### 5.1 Instalación
39
+
40
+ ```bash
41
+ npm i @jmlq/auth @jmlq/auth-plugin-jose jose
42
+ ```
43
+
44
+ ### 5.2 Dependencias
45
+
46
+ Dependencias directas del paquete:
47
+
48
+ - `@jmlq/auth`
49
+ - `jose`
50
+
51
+ ### 5.3 Quickstart (implementación rápida)
52
+
53
+ Uso real recomendado desde infrastructure:
54
+
55
+ ```ts
56
+ import { createJoseTokenService } from "@jmlq/auth-plugin-jose";
57
+
58
+ const tokenService = createJoseTokenService(
59
+ {
60
+ keyMaterial: {
61
+ alg: "RS256",
62
+ privateKey: process.env.JWT_PRIVATE_KEY!,
63
+ publicKey: process.env.JWT_PUBLIC_KEY!,
64
+ },
65
+ issuer: process.env.JWT_ISSUER,
66
+ audience: process.env.JWT_AUDIENCE,
67
+ clockSkewSeconds: Number(process.env.JWT_CLOCK_SKEW_SECONDS ?? 5),
68
+ defaultExpiresIn: {
69
+ accessToken: "15m",
70
+ refreshToken: "7d",
71
+ },
72
+ },
73
+ {
74
+ createAuthError,
75
+ },
76
+ );
77
+ ```
78
+
79
+ También es válido con `HS256`:
80
+
81
+ ```ts
82
+ const tokenService = createJoseTokenService(
83
+ {
84
+ keyMaterial: {
85
+ alg: "HS256",
86
+ secret: process.env.JWT_SECRET!,
87
+ },
88
+ issuer: process.env.JWT_ISSUER,
89
+ audience: process.env.JWT_AUDIENCE,
90
+ defaultExpiresIn: {
91
+ accessToken: "15m",
92
+ refreshToken: "7d",
93
+ },
94
+ },
95
+ { createAuthError },
96
+ );
97
+ ```
98
+
99
+ ### 5.4 Variables de entorno (.env) 📦
100
+
101
+ El plugin **no lee `.env` directamente**.
102
+ El host resuelve variables y construye `JoseTokenServiceOptions`.
103
+
104
+ Variables típicas y coherentes con esta integración:
105
+
106
+ ```ts
107
+ process.env.JWT_SECRET;
108
+ process.env.JWT_PRIVATE_KEY;
109
+ process.env.JWT_PUBLIC_KEY;
110
+ process.env.JWT_ISSUER;
111
+ process.env.JWT_AUDIENCE;
112
+ process.env.JWT_CLOCK_SKEW_SECONDS;
113
+ ```
114
+
115
+ ### 5.5 Helpers y funcionalidades clave
116
+
117
+ #### `createJoseTokenService(...)`
118
+
119
+ Firma real:
120
+
121
+ ```ts
122
+ createJoseTokenService(
123
+ options: JoseTokenServiceOptions,
124
+ deps: { createAuthError: CreateAuthErrorFn },
125
+ )
126
+ ```
127
+
128
+ Responsabilidades reales:
129
+
130
+ - validar `keyMaterial`,
131
+ - exigir `createAuthError`,
132
+ - construir una implementación concreta de `ITokenServicePort`.
133
+
134
+ #### `JoseTokenServiceOptions`
135
+
136
+ Configuración pública real:
137
+
138
+ ```ts
139
+ export interface JoseTokenServiceOptions {
140
+ keyMaterial: JoseKeyMaterial;
141
+ issuer?: string;
142
+ audience?: string | string[];
143
+ clockSkewSeconds?: number;
144
+ defaultExpiresIn?: {
145
+ accessToken?: string;
146
+ refreshToken?: string;
147
+ };
148
+ }
149
+ ```
150
+
151
+ #### `JoseKeyMaterial`
152
+
153
+ ```ts
154
+ type JoseKeyMaterial =
155
+ | { alg: "HS256"; secret: string }
156
+ | { alg: "RS256" | "ES256"; privateKey: string; publicKey: string };
157
+ ```
158
+
159
+ #### Validación real de material criptográfico
160
+
161
+ El plugin valida:
162
+
163
+ - `HS256` requiere `secret`
164
+ - `RS256` / `ES256` requieren `privateKey` y `publicKey`
165
+
166
+ #### Audience: comportamiento importante
167
+
168
+ Regla real implementada en el plugin:
169
+
170
+ - `audience` se usa para **firmar** (`jwt.setAudience(...)`)
171
+ - `audience` **no se pasa** a `jwtVerify(...)`
172
+
173
+ Esto evita convertir `aud` en requisito obligatorio durante verificación.
174
+
175
+ ## ✅ Checklist (pasos rápidos)
176
+
177
+ - [Instalar](#51-instalación)
178
+ - [Definir `keyMaterial` según algoritmo](./docs/es/configuration.md#configuración-del-keymaterial)
179
+ - [Construir el servicio con `createJoseTokenService`](./docs/configuration.md#construcción-del-token-service)
180
+ - [Integrarlo en infrastructure del host](./docs/es/integration-express.md#integración-en-infrastructure-del-host)
181
+ - [Verificar reglas de `audience` y clock skew](./docs/es/troubleshooting.md#1-el-token-se-firma-con-aud-pero-falla-la-verificación-esperada)
182
+ - [Revisar troubleshooting](./docs/es/troubleshooting.md)
183
+
184
+ ## 📌 Menú
185
+
186
+ - [Arquitectura](./docs/es/architecture.md)
187
+ - [Configuración](./docs/es/configuration.md)
188
+ - [Integración Express](./docs/es/integration-express.md)
189
+ - [Troubleshooting](./docs/es/troubleshooting.md)
190
+
191
+ ## 🔗 Referencias
192
+
193
+ - [`@jmlq/auth`](https://github.com/MLahuasi/jmlq-auth#readme)
194
+
195
+ ## ⬅️ 🌐 Ecosistema
196
+
197
+ - [`@jmlq`](https://github.com/MLahuasi/jmlq-ecosystem#readme)
package/README.md CHANGED
@@ -1,228 +1,197 @@
1
- # @jmlq/auth-plugin-jose
1
+ # @jmlq/auth-plugin-jose 🧩
2
2
 
3
- ## Introducción técnica
3
+ ## 🎯 Objective
4
4
 
5
- Plugin de **infraestructura JWT** basado en la librería [`jose`](https://github.com/panva/jose) para el core de autenticación [`@jmlq/auth`](https://www.npmjs.com/package/@jmlq/auth).
5
+ `@jmlq/auth-plugin-jose` is the infrastructure plugin that implements `ITokenServicePort` from `@jmlq/auth` using the `jose` library.
6
6
 
7
- Este paquete **NO contiene lógica de negocio**.
8
- Su única responsabilidad es **implementar los ports de tokens definidos por el core** y traducir detalles técnicos de `jose` a contratos estables del dominio.
7
+ Its real responsibility is:
9
8
 
10
- El plugin se encarga de:
9
+ - generate access tokens and refresh tokens,
10
+ - verify signed tokens,
11
+ - normalize cryptographic configuration,
12
+ - translate technical errors from `jose` into core-compatible errors.
11
13
 
12
- - Generación y verificación de JWT
13
- - Soporte para algoritmos HS256, RS256 y ES256
14
- - Normalización y validación de claims estándar (`iss`, `aud`, `exp`, `nbf`, `iat`, `sub`, `jti`)
15
- - Validación de expiración y clock skew
16
- - Manejo de claves simétricas y asimétricas
17
- - Traducción de errores técnicos de `jose` a errores del core
14
+ The recommended entry point is `createJoseTokenService(...)`.
18
15
 
19
- ---
16
+ ## ⭐ Importance
20
17
 
21
- ## Relación con @jmlq/auth
18
+ This plugin allows the `@jmlq/auth` core to remain independent from the specific JWT technology.
22
19
 
23
- - **@jmlq/auth**
24
- Core de autenticación (domain + application).
25
- Define contratos, errores y casos de uso.
20
+ In practice:
26
21
 
27
- - **@jmlq/auth-plugin-jose**
28
- Plugin de **infraestructura**.
29
- Depende del core e implementa estrictamente sus ports.
22
+ - the core depends on `ITokenServicePort`,
23
+ - this plugin implements that contract,
24
+ - the host decides whether to use `HS256`, `RS256`, or `ES256`,
25
+ - business logic remains in `@jmlq/auth`.
30
26
 
31
- > ⚠️ Bajo ninguna circunstancia el core depende de este plugin.
27
+ ## 🏗️ Architecture (quick view)
32
28
 
33
- ---
29
+ - `createJoseTokenService(...)` validates minimal preconditions and builds `JoseTokenService`.
30
+ - `JoseTokenService` encapsulates `SignJWT`, `jwtVerify`, `decodeJwt`, `importPKCS8`, and `importSPKI`.
31
+ - `jose-error.mapper.ts` translates technical errors into core error codes.
32
+ - `audience` is used **only for issuing**; it is not passed to `jwtVerify` during verification.
34
33
 
35
- ## Componentes principales expuestos
34
+ ➡️ See details in: [architecture.md](./docs/en/architecture.md)
36
35
 
37
- ### Factory
36
+ ## 🔧 Implementation
38
37
 
39
- - **createJoseTokenService**
40
- Factory de alto nivel que construye una implementación de `ITokenServicePort`
41
- compatible con `@jmlq/auth`.
42
-
43
- Responsabilidades:
44
-
45
- - Validar configuración técnica mínima (keys, algoritmo, opciones)
46
- - Normalizar issuer, audience, expiración y clock skew
47
- - Garantizar una política de expiración consistente
48
- - Encapsular completamente el uso de `jose`
49
-
50
- > Es el **único punto recomendado de entrada** para consumidores del plugin.
51
-
52
- ---
53
-
54
- ### Tipos de configuración
55
-
56
- - **JoseTokenServiceOptions**
57
- Define la configuración necesaria para construir el servicio de tokens:
58
- - `algorithm`: Algoritmo JWT (`HS256`, `RS256`, `ES256`)
59
- - `keys`: Material criptográfico (secret o key pair)
60
- - `issuer`: Emisor esperado (`iss`)
61
- - `audience`: Audiencia permitida (`aud`)
62
- - `defaultExpiresIn`: Expiración por defecto (ej: `"15m"`)
63
- - `clockSkewSeconds`: Tolerancia de desajuste de reloj
64
-
65
- Estos tipos existen para **configuración explícita y tipada**, sin estado global.
66
-
67
- ---
68
-
69
- ## Estrategia de errores (OBLIGATORIA)
70
-
71
- - El plugin **NO expone errores propios**
72
-
73
- Reglas:
74
-
75
- - Todos los errores de `jose` se traducen a `AuthDomainError`
76
- - No se definen nuevos códigos de error
77
- - El error original se conserva como `cause` o `meta`
78
- - La aplicación **solo maneja errores del core**
79
-
80
- Ejemplos de mapeo:
81
-
82
- | Error `jose` | Error del core |
83
- | ---------------------- | ------------------------- |
84
- | Token expirado | `TOKEN_EXPIRED` |
85
- | Firma inválida | `SIGNATURE_INVALID` |
86
- | Token malformado | `TOKEN_MALFORMED` |
87
- | Algoritmo no soportado | `ALGORITHM_UNSUPPORTED` |
88
- | Claims inválidos | `CLAIMS_VALIDATION_ERROR` |
89
-
90
- ---
91
-
92
- ## 📦 Instalación
38
+ ### 5.1 Installation
93
39
 
94
40
  ```bash
95
- npm install @jmlq/auth @jmlq/auth-plugin-jose
41
+ npm i @jmlq/auth @jmlq/auth-plugin-jose jose
96
42
  ```
97
43
 
98
- Versiones recomendadas:
44
+ ### 5.2 Dependencies
99
45
 
100
- - `@jmlq/auth` >= `0.1.0-alpha`
101
- - `jose` >= `5.x`
46
+ Direct dependencies of the package:
102
47
 
103
- ---
48
+ - `@jmlq/auth`
49
+ - `jose`
104
50
 
105
- ## Uso básico
51
+ ### 5.3 Quickstart (quick implementation)
106
52
 
107
- ### Crear el servicio de tokens
53
+ Recommended real usage from infrastructure:
108
54
 
109
55
  ```ts
110
56
  import { createJoseTokenService } from "@jmlq/auth-plugin-jose";
111
57
 
112
- const tokenService = createJoseTokenService({
113
- algorithm: "HS256",
114
- keys: { secret: "super-secret" },
115
- issuer: "my-api",
116
- audience: "my-clients",
117
- defaultExpiresIn: "15m",
118
- clockSkewSeconds: 5,
119
- });
58
+ const tokenService = createJoseTokenService(
59
+ {
60
+ keyMaterial: {
61
+ alg: "RS256",
62
+ privateKey: process.env.JWT_PRIVATE_KEY!,
63
+ publicKey: process.env.JWT_PUBLIC_KEY!,
64
+ },
65
+ issuer: process.env.JWT_ISSUER,
66
+ audience: process.env.JWT_AUDIENCE,
67
+ clockSkewSeconds: Number(process.env.JWT_CLOCK_SKEW_SECONDS ?? 5),
68
+ defaultExpiresIn: {
69
+ accessToken: "15m",
70
+ refreshToken: "7d",
71
+ },
72
+ },
73
+ {
74
+ createAuthError,
75
+ },
76
+ );
120
77
  ```
121
78
 
122
- ---
123
-
124
- ### Generar un access token
79
+ Also valid with `HS256`:
125
80
 
126
81
  ```ts
127
- const token = await tokenService.generateAccessToken({
128
- subject: "user-123",
129
- payload: {
130
- scope: ["user"],
82
+ const tokenService = createJoseTokenService(
83
+ {
84
+ keyMaterial: {
85
+ alg: "HS256",
86
+ secret: process.env.JWT_SECRET!,
87
+ },
88
+ issuer: process.env.JWT_ISSUER,
89
+ audience: process.env.JWT_AUDIENCE,
90
+ defaultExpiresIn: {
91
+ accessToken: "15m",
92
+ refreshToken: "7d",
93
+ },
131
94
  },
132
- });
95
+ { createAuthError },
96
+ );
133
97
  ```
134
98
 
135
- ---
99
+ ### 5.4 Environment variables (.env) 📦
136
100
 
137
- ### Verificar un token
101
+ The plugin **does not read `.env` directly**.
102
+ The host resolves variables and builds `JoseTokenServiceOptions`.
138
103
 
139
- ```ts
140
- const result = await tokenService.verifyAccessToken(token);
104
+ Typical variables consistent with this integration:
141
105
 
142
- console.log(result.payload.sub); // "user-123"
106
+ ```ts
107
+ process.env.JWT_SECRET;
108
+ process.env.JWT_PRIVATE_KEY;
109
+ process.env.JWT_PUBLIC_KEY;
110
+ process.env.JWT_ISSUER;
111
+ process.env.JWT_AUDIENCE;
112
+ process.env.JWT_CLOCK_SKEW_SECONDS;
143
113
  ```
144
114
 
145
- Si ocurre un error:
115
+ ### 5.5 Helpers and key functionalities
146
116
 
147
- - Se lanza un `AuthDomainError`
148
- - Nunca se lanza un error de `jose`
117
+ #### `createJoseTokenService(...)`
149
118
 
150
- ---
119
+ Real signature:
151
120
 
152
- ### Ejemplo de integración en una API (Express)
121
+ ```ts
122
+ createJoseTokenService(
123
+ options: JoseTokenServiceOptions,
124
+ deps: { createAuthError: CreateAuthErrorFn },
125
+ )
126
+ ```
153
127
 
154
- Se recomienda realizar esta integración en la **capa infrastructure** de la API.
128
+ Real responsibilities:
155
129
 
156
- ```ts
157
- // src/infrastructure/auth/token-service.ts
130
+ - validate `keyMaterial`,
131
+ - require `createAuthError`,
132
+ - build a concrete implementation of `ITokenServicePort`.
158
133
 
159
- import { createJoseTokenService } from "@jmlq/auth-plugin-jose";
134
+ #### `JoseTokenServiceOptions`
160
135
 
161
- export const tokenService = createJoseTokenService({
162
- algorithm: "RS256",
163
- keys: {
164
- publicKey: process.env.JWT_PUBLIC_KEY!,
165
- privateKey: process.env.JWT_PRIVATE_KEY!,
166
- },
167
- issuer: "auth-api",
168
- audience: "web-app",
169
- defaultExpiresIn: "15m",
170
- });
136
+ Real public configuration:
137
+
138
+ ```ts
139
+ export interface JoseTokenServiceOptions {
140
+ keyMaterial: JoseKeyMaterial;
141
+ issuer?: string;
142
+ audience?: string | string[];
143
+ clockSkewSeconds?: number;
144
+ defaultExpiresIn?: {
145
+ accessToken?: string;
146
+ refreshToken?: string;
147
+ };
148
+ }
171
149
  ```
172
150
 
173
- ## Este servicio se inyecta luego en los casos de uso del core (`@jmlq/auth`).
151
+ #### `JoseKeyMaterial`
174
152
 
175
- ---
153
+ ```ts
154
+ type JoseKeyMaterial =
155
+ | { alg: "HS256"; secret: string }
156
+ | { alg: "RS256" | "ES256"; privateKey: string; publicKey: string };
157
+ ```
176
158
 
177
- ## Variables de entorno (recomendadas)
159
+ #### Real cryptographic material validation
178
160
 
179
- El plugin **No lee variables de entorno directamente**, pero una API típica puede definir::
161
+ The plugin validates:
180
162
 
181
- ```ini
182
- JWT_ALGORITHM=RS256
183
- JWT_ISSUER=auth-api
184
- JWT_AUDIENCE=web-app
185
- JWT_DEFAULT_EXPIRES_IN=15m
186
- JWT_CLOCK_SKEW_SECONDS=5
163
+ - `HS256` requires `secret`
164
+ - `RS256` / `ES256` require `privateKey` and `publicKey`
187
165
 
188
- JWT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----..."
189
- JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----..."
166
+ #### Audience: important behavior
190
167
 
191
- ```
168
+ Real rule implemented in the plugin:
192
169
 
193
- La lectura y validación de estas variables es responsabilidad de la aplicación.
170
+ - `audience` is used for **signing** (`jwt.setAudience(...)`)
171
+ - `audience` is **not passed** to `jwtVerify(...)`
194
172
 
195
- ---
173
+ This prevents turning `aud` into a mandatory requirement during verification.
196
174
 
197
- ## Estructura del proyecto
175
+ ## Checklist (quick steps)
198
176
 
199
- ```
200
- src/
201
- ├─ application/
202
- │ ├─ factories/
203
- │ │ └─ create-jose-token-service.ts
204
- │ ├─ types/
205
- │ │ └─ jose-token-service.options.ts
206
- │ └─ internal/
207
- │ └─ *.util.ts
208
-
209
- ├─ infrastructure/
210
- │ ├─ services/
211
- │ │ └─ jose-token.service.ts
212
- │ └─ mappers/
213
- │ └─ jose-error.mapper.ts
214
-
215
- └─ index.ts
177
+ - [Install](#51-installation)
178
+ - [Define `keyMaterial` according to algorithm](./docs/en/configuration.md#configuring-keymaterial)
179
+ - [Build the service with `createJoseTokenService`](./docs/en/configuration.md#building-the-token-service)
180
+ - [Integrate it into the host infrastructure](./docs/en/integration-express.md#integration-in-host-infrastructure)
181
+ - [Verify `audience` and clock skew rules](./docs/en/troubleshooting.md#1-token-is-signed-with-aud-but-verification-fails-as-expected)
182
+ - [Review troubleshooting](./docs/en/troubleshooting.md)
216
183
 
217
- ```
184
+ ## 📌 Menu
218
185
 
219
- ---
186
+ - [Architecture](./docs/en/architecture.md)
187
+ - [Configuration](./docs/en/configuration.md)
188
+ - [Express Integration](./docs/en/integration-express.md)
189
+ - [Troubleshooting](./docs/en/troubleshooting.md)
220
190
 
221
- ## 📄 Más información
191
+ ## 🔗 References
222
192
 
223
- - **[🏗️ Documentación de Arquitectura](./architecture.md)** - Clean Architecture, capas y patrones
224
- - **[📚 Ejemplos de Código](./examples/)** - Casos de uso reales y implementaciones
193
+ - [`@jmlq/auth`](https://github.com/MLahuasi/jmlq-auth#readme)
225
194
 
226
- ## 📄 Licencia
195
+ ## ⬅️ 🌐 Ecosystem
227
196
 
228
- MIT © Mauricio Lahuasi
197
+ - [`@jmlq`](https://github.com/MLahuasi/jmlq-ecosystem#readme)
@@ -0,0 +1,11 @@
1
+ import type { AuthErrorCode } from "@jmlq/auth";
2
+ /**
3
+ * Convierte un Error (posiblemente sin code) en uno con `code: AuthErrorCode`
4
+ * de forma segura y mínima, SIN cambiar el tipo del error original.
5
+ *
6
+ * - Si ya tiene code string, lo usa.
7
+ * - Si no, usa un fallback dado (por ej. "JWT_ERROR").
8
+ */
9
+ export declare function ensureAuthErrorCode(err: Error, fallback: AuthErrorCode): Error & {
10
+ code: AuthErrorCode;
11
+ };
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ensureAuthErrorCode = ensureAuthErrorCode;
4
+ /**
5
+ * Convierte un Error (posiblemente sin code) en uno con `code: AuthErrorCode`
6
+ * de forma segura y mínima, SIN cambiar el tipo del error original.
7
+ *
8
+ * - Si ya tiene code string, lo usa.
9
+ * - Si no, usa un fallback dado (por ej. "JWT_ERROR").
10
+ */
11
+ function ensureAuthErrorCode(err, fallback) {
12
+ const e = err;
13
+ const code = typeof e.code === "string" ? e.code : undefined;
14
+ // Importante: aquí NO validamos que code pertenezca al union.
15
+ // Solo garantizamos que al menos sea un AuthErrorCode conocido o fallback.
16
+ const safeCode = code ?? fallback;
17
+ // Mutación mínima: añadimos code si falta
18
+ e.code = safeCode;
19
+ return e;
20
+ }
@@ -0,0 +1 @@
1
+ export * from "./ensure-auth-error-code";
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./ensure-auth-error-code"), exports);
@@ -1,3 +1,4 @@
1
+ import type { AuthErrorCode } from "@jmlq/auth";
1
2
  import type { JoseErrorContext, MappedAuthError } from "./types";
2
3
  export declare function mapJoseErrorToAuthError(err: unknown, ctx: JoseErrorContext): MappedAuthError;
3
4
  export declare function toAuthDomainError<TAuthError extends Error>(createAuthError: (args: {
@@ -5,4 +6,6 @@ export declare function toAuthDomainError<TAuthError extends Error>(createAuthEr
5
6
  message: string;
6
7
  cause?: unknown;
7
8
  meta?: Record<string, unknown>;
8
- }) => TAuthError, err: unknown, ctx: JoseErrorContext): TAuthError;
9
+ }) => TAuthError, err: unknown, ctx: JoseErrorContext): Error & {
10
+ code: AuthErrorCode;
11
+ };
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
- //src/infrastructure/mappers/jose-error.mapper.ts
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
3
  exports.mapJoseErrorToAuthError = mapJoseErrorToAuthError;
5
4
  exports.toAuthDomainError = toAuthDomainError;
6
5
  const auth_1 = require("@jmlq/auth");
6
+ const errors_1 = require("./errors");
7
7
  /**
8
8
  * Mapper de errores de `jose` → error “entendible” por el core (@jmlq/auth).
9
9
  *
@@ -29,20 +29,32 @@ function buildSafeMeta(joseErrorName, ctx) {
29
29
  alg: ctx.alg,
30
30
  };
31
31
  }
32
+ /**
33
+ * Tabla: err.name (jose) -> AuthErrorCode (core)
34
+ */
32
35
  const JOSE_NAME_TO_AUTH_CODE = {
36
+ // Tiempo
33
37
  JWTExpired: "TOKEN_EXPIRED",
34
38
  JWTNotBefore: "TOKEN_NOT_YET_VALID",
35
39
  JWTNotYetValid: "TOKEN_NOT_YET_VALID",
40
+ // Firma
36
41
  JWSSignatureVerificationFailed: "SIGNATURE_INVALID",
37
- JWSInvalid: "SIGNATURE_INVALID",
38
- JWSError: "SIGNATURE_INVALID",
42
+ // Estructura/serialización JWS inválida
43
+ JWSInvalid: "TOKEN_MALFORMED",
44
+ JWSMalformed: "TOKEN_MALFORMED",
45
+ // Error genérico JWS (no asumir firma)
46
+ JWSError: "TOKEN_INVALID",
47
+ // Claims inválidos (issuer, exp, nbf, etc. dependiendo de jose)
39
48
  JWTClaimValidationFailed: "CLAIMS_VALIDATION_ERROR",
49
+ // Token inválido (pero no necesariamente “malformed”)
40
50
  JWTInvalid: "TOKEN_INVALID",
51
+ // Token malformado
41
52
  JWTMalformed: "TOKEN_MALFORMED",
42
- JWSMalformed: "TOKEN_MALFORMED",
43
53
  JOSEError: "TOKEN_MALFORMED",
54
+ // Algoritmo / soporte
44
55
  JOSENotSupported: "ALGORITHM_UNSUPPORTED",
45
56
  JWTAlgorithmNotAllowed: "ALGORITHM_UNSUPPORTED",
57
+ // Key material
46
58
  JWKInvalid: "KEY_MISMATCH",
47
59
  JWKInvalidFormat: "KEY_MISMATCH",
48
60
  };
@@ -54,6 +66,8 @@ const JOSE_NAME_TO_AUTH_CODE = {
54
66
  * - Por eso es Partial<Record<AuthErrorCode, string>> + fallback.
55
67
  */
56
68
  const AUTH_CODE_TO_MESSAGE = {
69
+ JWT_EMPTY: "Invalid empty JWT",
70
+ JWT_MALFORMED: "JWT is malformed",
57
71
  TOKEN_INVALID: "Token is invalid",
58
72
  TOKEN_EXPIRED: "Token has expired",
59
73
  TOKEN_MALFORMED: "Token is malformed",
@@ -74,6 +88,14 @@ function mapJoseErrorToAuthError(err, ctx) {
74
88
  const message = AUTH_CODE_TO_MESSAGE[code] ??
75
89
  // fallback defensivo (no dependemos de message de jose)
76
90
  "JWT operation failed";
91
+ // console.log({
92
+ // package: "@jmlq/auth-plugin-jose",
93
+ // name,
94
+ // meta,
95
+ // code,
96
+ // message,
97
+ // err,
98
+ // });
77
99
  return {
78
100
  code,
79
101
  message,
@@ -82,14 +104,17 @@ function mapJoseErrorToAuthError(err, ctx) {
82
104
  };
83
105
  }
84
106
  function toAuthDomainError(createAuthError, err, ctx) {
107
+ // Si ya es error del core, conservarlo (NO remapear)
85
108
  if (auth_1.AuthDomainError.isAuthError(err)) {
86
109
  return err;
87
110
  }
88
111
  const mapped = mapJoseErrorToAuthError(err, ctx);
89
- return createAuthError({
112
+ const created = createAuthError({
90
113
  code: mapped.code,
91
114
  message: mapped.message,
92
115
  cause: mapped.cause,
93
116
  meta: mapped.meta,
94
117
  });
118
+ // Garantizamos code usable por `isRetryableAuthCode`
119
+ return (0, errors_1.ensureAuthErrorCode)(created, mapped.code);
95
120
  }
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- // src/infrastructure/services/jose-token.service.ts
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
3
  exports.JoseTokenService = void 0;
5
4
  const jose_1 = require("jose");
@@ -103,7 +102,19 @@ class JoseTokenService {
103
102
  return await fn();
104
103
  }
105
104
  catch (err) {
106
- throw (0, mappers_1.toAuthDomainError)(this.createAuthError, err, this.ctx(operation, kind));
105
+ // 1) Si ya viene como error del core, NO tocarlo
106
+ if (auth_1.AuthDomainError.isAuthError(err)) {
107
+ throw err;
108
+ }
109
+ // 2) Si es error de jose u otro, mapear SIEMPRE
110
+ const authErr = (0, mappers_1.toAuthDomainError)(this.createAuthError, err, this.ctx(operation, kind));
111
+ // 3) (Opcional) Si tienes retry logic, que dependa solo de code del core
112
+ // y NO de códigos internos de jose.
113
+ // Ej: si no quieres reintentos, elimina esto.
114
+ if ((0, auth_1.isRetryableAuthCode)(authErr.code)) {
115
+ throw authErr;
116
+ }
117
+ throw authErr;
107
118
  }
108
119
  }
109
120
  // ---------------------------------------------------------------------------
@@ -173,24 +184,47 @@ class JoseTokenService {
173
184
  }
174
185
  async verifyToken(kind, operation, token) {
175
186
  return this.withAuthError(operation, kind, async () => {
176
- const keys = await this.getNormalizedKeys();
177
- const key = keys.alg === "HS256" ? keys.secret : keys.publicKey;
178
187
  /**
179
- * Verificación:
180
- * - issuer: se aplica si está configurado (si no, se omite)
181
- * - clockTolerance: ya normalizado (clock skew seguro)
188
+ * Validación estructural temprana (core):
189
+ * - JWT_EMPTY si token está vacío
190
+ * - JWT_MALFORMED si no tiene 3 segmentos
182
191
  *
183
- * Nota: NO pasamos `audience` a jwtVerify.
184
- * - La audiencia en este plugin es SOLO para emisión.
185
- * - Pasarla aquí convertiría audience en una restricción (“must match”)
186
- * y rompería compatibilidad con tokens válidos sin `aud`.
192
+ * Nota:
193
+ * - Esto evita que `jose` arroje errores “ruidosos” por formato inválido.
194
+ * - Estos errores YA son AuthDomainError y deben conservar su code.
187
195
  */
188
- const result = await (0, jose_1.jwtVerify)(token, key, {
189
- issuer: this.eff.issuer,
190
- clockTolerance: this.eff.clockSkewSeconds,
191
- });
192
- // Delegar a la regla canónica del core: valida + normaliza payload.
193
- return (0, auth_1.normalizeJwtPayload)(result.payload);
196
+ (0, auth_1.assertJwtStructure)(token);
197
+ const keys = await this.getNormalizedKeys();
198
+ const key = keys.alg === "HS256" ? keys.secret : keys.publicKey;
199
+ try {
200
+ /**
201
+ * Verificación:
202
+ * - issuer: se aplica si está configurado (si no, se omite)
203
+ * - clockTolerance: ya normalizado (clock skew seguro)
204
+ *
205
+ * Nota: NO pasamos `audience` a jwtVerify.
206
+ * - La audiencia en este plugin es SOLO para emisión.
207
+ * - Pasarla aquí convertiría audience en una restricción (“must match”)
208
+ * y rompería compatibilidad con tokens válidos sin `aud`.
209
+ */
210
+ const result = await (0, jose_1.jwtVerify)(token, key, {
211
+ issuer: this.eff.issuer,
212
+ clockTolerance: this.eff.clockSkewSeconds,
213
+ });
214
+ // Delegar a la regla canónica del core: valida + normaliza payload.
215
+ return (0, auth_1.normalizeJwtPayload)(result.payload);
216
+ }
217
+ catch (err) {
218
+ /**
219
+ * Importante:
220
+ * - NO relanzar error crudo de jose.
221
+ * - Convertirlo a AuthDomainError con code estable (TOKEN_INVALID, SIGNATURE_INVALID, etc.)
222
+ * usando el mapper oficial.
223
+ *
224
+ * Esto evita que el host termine con INTERNAL + alert emails para 401 esperados.
225
+ */
226
+ throw (0, mappers_1.toAuthDomainError)(this.createAuthError, err, this.ctx(operation, kind));
227
+ }
194
228
  });
195
229
  }
196
230
  async tryGetExpirationByVerify(kind, token) {
package/package.json CHANGED
@@ -1,23 +1,21 @@
1
1
  {
2
2
  "name": "@jmlq/auth-plugin-jose",
3
3
  "description": "Infrastructure plugin that integrates the jose library with @jmlq/auth, providing JWT token generation and verification following Clean Architecture principles.",
4
- "version": "0.0.1-alpha.9",
4
+ "version": "0.0.1-beta.1",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/MLahuasi/jmlq-auth-plugin-jose.git"
10
+ },
11
+ "homepage": "https://github.com/MLahuasi/jmlq-auth-plugin-jose#readme",
7
12
  "scripts": {
8
13
  "dev": "rimraf dist && mkdir dist && tsc -p tsconfig.json",
9
14
  "build": "rimraf dist && mkdir dist && tsc -p tsconfig.build.json",
10
15
  "prepublishOnly": "npm run build",
11
- "package:script": "node scripts/package-script.mjs",
12
16
  "test": "jest --passWithNoTests",
13
17
  "test:watch": "jest --watch",
14
- "test:coverage": "jest --coverage",
15
- "example:services": "tsx examples/index.example.ts services",
16
- "example:service-helpers": "tsx examples/index.example.ts service-helpers",
17
- "example:factories": "tsx examples/index.example.ts factories",
18
- "example:factory-helpers": "tsx examples/index.example.ts factory-helpers",
19
- "example:help": "tsx examples/index.example.ts help",
20
- "example:all": "tsx examples/index.example.ts"
18
+ "test:coverage": "jest --coverage"
21
19
  },
22
20
  "keywords": [
23
21
  "jwt",
@@ -29,7 +27,7 @@
29
27
  "author": "MLahuasi",
30
28
  "license": "MIT",
31
29
  "dependencies": {
32
- "@jmlq/auth": "^0.0.1-alpha.30",
30
+ "@jmlq/auth": "beta",
33
31
  "jose": "^6.1.3"
34
32
  },
35
33
  "devDependencies": {