@tgtone/auth-sdk 1.1.0 → 1.2.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.md +147 -7
- package/dist/tgtone-auth-client.d.ts +5 -1
- package/dist/tgtone-auth-client.d.ts.map +1 -1
- package/dist/tgtone-auth-client.js +117 -6
- package/dist/tgtone-auth-client.js.map +1 -1
- package/docs/LOVABLE_QUICK_START.md +131 -383
- package/docs/NPM_PUBLISH.md +71 -62
- package/docs/README.md +20 -48
- package/package.json +1 -1
- package/docs/SDK_FINAL_STATUS.md +0 -88
- package/docs/SDK_INTEGRATION_RULES.md +0 -433
- package/docs/tgtone-identity-auth.code-workspace +0 -11
|
@@ -1,478 +1,226 @@
|
|
|
1
|
-
# 🔐 TGT
|
|
1
|
+
# 🔐 TGT Auth SDK - Guía para Lovable/React
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Integración rápida de autenticación en proyectos React/Lovable
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## 📦 Instalación
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install tgtone
|
|
10
|
+
npm install @tgtone/auth-sdk
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
## 🚀 TGT Auth SDK v1.2.0 - Quick Start para Lovable
|
|
16
|
-
|
|
17
|
-
## 📦 Instalación
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npm install tgtone-auth-client@latest
|
|
21
|
-
```
|
|
13
|
+
**Versión actual:** 1.2.0
|
|
22
14
|
|
|
23
15
|
---
|
|
24
16
|
|
|
25
|
-
## ⚡
|
|
17
|
+
## ⚡ Setup en 3 Pasos
|
|
26
18
|
|
|
27
|
-
### **1.
|
|
19
|
+
### **1. Crear AuthContext**
|
|
28
20
|
|
|
29
21
|
```typescript
|
|
30
|
-
|
|
22
|
+
// src/contexts/AuthContext.tsx
|
|
23
|
+
import { createContext, useContext, useEffect, useState } from 'react';
|
|
24
|
+
import { TGTAuthClient, TGTSession } from '@tgtone/auth-sdk';
|
|
31
25
|
|
|
32
|
-
const
|
|
26
|
+
const authClient = new TGTAuthClient({
|
|
33
27
|
identityUrl: 'https://identity.tgtone.cl',
|
|
34
|
-
appDomain: window.location.hostname,
|
|
35
|
-
debug:
|
|
28
|
+
appDomain: window.location.hostname,
|
|
29
|
+
debug: import.meta.env.DEV
|
|
36
30
|
});
|
|
37
|
-
```
|
|
38
31
|
|
|
39
|
-
|
|
32
|
+
interface AuthContextType {
|
|
33
|
+
session: TGTSession | null;
|
|
34
|
+
loading: boolean;
|
|
35
|
+
logout: () => Promise<void>;
|
|
36
|
+
hasRole: (app: string, role: string) => boolean;
|
|
37
|
+
}
|
|
40
38
|
|
|
41
|
-
|
|
39
|
+
const AuthContext = createContext<AuthContextType | null>(null);
|
|
42
40
|
|
|
43
|
-
|
|
44
|
-
import { useEffect, useState } from 'react';
|
|
45
|
-
import { TGTAuthClient, type TGTSession } from 'tgtone-auth-client';
|
|
46
|
-
|
|
47
|
-
function App() {
|
|
41
|
+
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|
48
42
|
const [session, setSession] = useState<TGTSession | null>(null);
|
|
49
43
|
const [loading, setLoading] = useState(true);
|
|
50
44
|
|
|
51
45
|
useEffect(() => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
appDomain: window.location.hostname
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
auth.checkSession().then((session) => {
|
|
58
|
-
setSession(session);
|
|
46
|
+
authClient.checkSession().then(s => {
|
|
47
|
+
setSession(s);
|
|
59
48
|
setLoading(false);
|
|
60
49
|
});
|
|
61
50
|
}, []);
|
|
62
51
|
|
|
63
|
-
|
|
64
|
-
|
|
52
|
+
const logout = async () => {
|
|
53
|
+
await authClient.logout();
|
|
54
|
+
setSession(null);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const hasRole = (app: string, role: string) => {
|
|
58
|
+
return authClient.hasRole(app, role);
|
|
59
|
+
};
|
|
65
60
|
|
|
66
61
|
return (
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
<p>Roles: {JSON.stringify(session.roles)}</p>
|
|
71
|
-
</div>
|
|
62
|
+
<AuthContext.Provider value={{ session, loading, logout, hasRole }}>
|
|
63
|
+
{children}
|
|
64
|
+
</AuthContext.Provider>
|
|
72
65
|
);
|
|
73
66
|
}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## 🔑 Nuevas Propiedades de Sesión (v1.1)
|
|
79
|
-
|
|
80
|
-
```typescript
|
|
81
|
-
const session = await auth.checkSession();
|
|
82
67
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
68
|
+
export const useAuth = () => {
|
|
69
|
+
const context = useContext(AuthContext);
|
|
70
|
+
if (!context) throw new Error('useAuth must be used within AuthProvider');
|
|
71
|
+
return context;
|
|
72
|
+
};
|
|
73
|
+
```
|
|
86
74
|
|
|
87
|
-
|
|
88
|
-
session.roles // { "console": ["owner"], "crm": ["admin"] }
|
|
75
|
+
### **2. Envolver App**
|
|
89
76
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
77
|
+
```tsx
|
|
78
|
+
// src/App.tsx
|
|
79
|
+
import { AuthProvider } from './contexts/AuthContext';
|
|
93
80
|
|
|
94
|
-
|
|
95
|
-
|
|
81
|
+
function App() {
|
|
82
|
+
return (
|
|
83
|
+
<AuthProvider>
|
|
84
|
+
{/* Tu app */}
|
|
85
|
+
</AuthProvider>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
96
88
|
```
|
|
97
89
|
|
|
98
|
-
|
|
90
|
+
### **3. Usar en Componentes**
|
|
99
91
|
|
|
100
|
-
|
|
92
|
+
```tsx
|
|
93
|
+
import { useAuth } from './contexts/AuthContext';
|
|
101
94
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const tenantId = auth.getTenantId();
|
|
105
|
-
// Usar en requests al backend:
|
|
106
|
-
fetch(`/api/v1/tenants/${tenantId}/data`)
|
|
107
|
-
```
|
|
95
|
+
function Dashboard() {
|
|
96
|
+
const { session, loading, logout, hasRole } = useAuth();
|
|
108
97
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
//
|
|
112
|
-
if (auth.hasRole('console', 'admin')) {
|
|
113
|
-
// Mostrar panel de administración
|
|
114
|
-
}
|
|
98
|
+
if (loading) return <div>Cargando...</div>;
|
|
99
|
+
|
|
100
|
+
if (!session) return null; // Ya está redirigiendo a login
|
|
115
101
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
102
|
+
return (
|
|
103
|
+
<div>
|
|
104
|
+
<h1>Bienvenido {session.user.name}</h1>
|
|
105
|
+
<p>Tenant: {session.tenantName}</p>
|
|
106
|
+
|
|
107
|
+
{hasRole('console', 'admin') && (
|
|
108
|
+
<button>Panel Admin</button>
|
|
109
|
+
)}
|
|
110
|
+
|
|
111
|
+
<button onClick={logout}>Cerrar Sesión</button>
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
119
114
|
}
|
|
120
|
-
|
|
121
|
-
// Obtener todos los roles de una app
|
|
122
|
-
const roles = auth.getRoles('console'); // ["owner", "admin"]
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### **Cerrar Sesión**
|
|
126
|
-
```typescript
|
|
127
|
-
<button onClick={() => auth.logout()}>
|
|
128
|
-
Cerrar Sesión
|
|
129
|
-
</button>
|
|
130
115
|
```
|
|
131
116
|
|
|
132
117
|
---
|
|
133
118
|
|
|
134
|
-
##
|
|
119
|
+
## 🎯 Propiedades de Sesión
|
|
135
120
|
|
|
136
121
|
```typescript
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
'X-Tenant-ID': session.tenantId, // ✅ Tenant context
|
|
144
|
-
}
|
|
145
|
-
});
|
|
122
|
+
session.user.email // "user@empresa.cl"
|
|
123
|
+
session.user.name // "Juan Pérez"
|
|
124
|
+
session.tenantId // "uuid-tenant-123"
|
|
125
|
+
session.tenantName // "Mi Empresa"
|
|
126
|
+
session.user.roles // { console: ['owner'], crm: ['admin'] }
|
|
127
|
+
session.expiresAt // Date object
|
|
146
128
|
```
|
|
147
129
|
|
|
148
130
|
---
|
|
149
131
|
|
|
150
|
-
##
|
|
151
|
-
|
|
152
|
-
El SDK ahora **decodifica el JWT localmente** antes de validar con el backend:
|
|
153
|
-
|
|
154
|
-
✅ Verifica `tenant_id` obligatorio
|
|
155
|
-
✅ Valida expiración del token
|
|
156
|
-
✅ Extrae roles sin hacer request
|
|
157
|
-
|
|
158
|
-
**Ventaja:** Menos latencia, mejor UX.
|
|
159
|
-
|
|
160
|
-
---
|
|
161
|
-
|
|
162
|
-
## 🔐 Soporte MFA (v1.2.0)
|
|
163
|
-
|
|
164
|
-
El SDK ahora soporta **Multi-Factor Authentication** con Google Authenticator/TOTP.
|
|
165
|
-
|
|
166
|
-
### Flujo de Login con MFA
|
|
167
|
-
|
|
168
|
-
Si el usuario tiene MFA habilitado, el backend NO hace redirect automático. En su lugar, el frontend de Identity maneja el flujo MFA completo:
|
|
132
|
+
## 🔐 Verificar Roles
|
|
169
133
|
|
|
170
|
-
**1. Login con MFA habilitado:**
|
|
171
134
|
```typescript
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
//
|
|
175
|
-
// → Usuario ingresa código de Google Authenticator
|
|
176
|
-
// → Identity valida y redirige con token final
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
**2. Tu app en Lovable recibe el token:**
|
|
180
|
-
```typescript
|
|
181
|
-
// Cuando Identity redirige de vuelta a tu app:
|
|
182
|
-
const session = await auth.checkSession();
|
|
183
|
-
// ✅ El token ya viene validado (con MFA completado)
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### Métodos MFA del SDK
|
|
187
|
-
|
|
188
|
-
Aunque Identity maneja el flujo MFA visualmente, el SDK expone métodos para casos avanzados:
|
|
189
|
-
|
|
190
|
-
```typescript
|
|
191
|
-
// Verificar código MFA programáticamente (uso avanzado)
|
|
192
|
-
const session = await auth.verifyMfa(tempToken, '123456');
|
|
193
|
-
|
|
194
|
-
// Guardar tempToken en localStorage
|
|
195
|
-
auth.storeTempToken(tempToken);
|
|
196
|
-
|
|
197
|
-
// Obtener tempToken guardado
|
|
198
|
-
const tempToken = auth.getTempToken();
|
|
199
|
-
|
|
200
|
-
// Limpiar tempToken
|
|
201
|
-
auth.clearTempToken();
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
> **💡 Nota para Lovable**: En la mayoría de casos NO necesitas llamar `verifyMfa()` directamente. Identity maneja el flujo MFA completo y tu app solo recibe el token final validado.
|
|
205
|
-
|
|
206
|
-
### ¿Cuándo usar los métodos MFA?
|
|
207
|
-
|
|
208
|
-
**✅ Caso común (recomendado):**
|
|
209
|
-
```typescript
|
|
210
|
-
// Identity maneja todo el flujo MFA
|
|
211
|
-
const session = await auth.checkSession();
|
|
212
|
-
// Ya viene con MFA validado si el usuario lo tiene habilitado
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
**🔧 Caso avanzado (si implementas login custom):**
|
|
216
|
-
```typescript
|
|
217
|
-
// Solo si NO usas el frontend de Identity
|
|
218
|
-
const loginResponse = await fetch('/api/auth/login', {
|
|
219
|
-
method: 'POST',
|
|
220
|
-
body: JSON.stringify({ email, password })
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
const data = await loginResponse.json();
|
|
224
|
-
|
|
225
|
-
if (data.requiresMfa && data.tempToken) {
|
|
226
|
-
auth.storeTempToken(data.tempToken);
|
|
227
|
-
// Mostrar input para código
|
|
228
|
-
const code = getUserInputCode();
|
|
229
|
-
const session = await auth.verifyMfa(data.tempToken, code);
|
|
135
|
+
// ¿Es admin de Console?
|
|
136
|
+
if (hasRole('console', 'admin')) {
|
|
137
|
+
// Mostrar funciones de admin
|
|
230
138
|
}
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
---
|
|
234
139
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
- ✅ **Soporte MFA** con TOTP (Google Authenticator)
|
|
239
|
-
- ✅ Métodos `verifyMfa()`, `storeTempToken()`, `getTempToken()`, `clearTempToken()`
|
|
240
|
-
- ✅ Detección automática de flujo MFA en `checkSession()`
|
|
241
|
-
- ✅ Identity maneja UI de MFA (no requiere código custom en apps)
|
|
242
|
-
|
|
243
|
-
### v1.1.0 (Octubre 2025)
|
|
244
|
-
- ✅ Validación de `tenant_id` obligatorio
|
|
245
|
-
- ✅ Decodificación local de JWT
|
|
246
|
-
- ✅ Nuevo retorno `TGTSession` en lugar de `TGTUser`
|
|
247
|
-
- ✅ Métodos `hasRole()`, `getRoles()`, `hasAccessToApp()`
|
|
248
|
-
|
|
249
|
-
### v1.0.0
|
|
250
|
-
- ✅ Versión inicial con SSO básico
|
|
251
|
-
|
|
252
|
-
---
|
|
253
|
-
|
|
254
|
-
## 🔧 TypeScript Types
|
|
255
|
-
|
|
256
|
-
```typescript
|
|
257
|
-
import type {
|
|
258
|
-
TGTSession,
|
|
259
|
-
TGTUser,
|
|
260
|
-
JWTPayload
|
|
261
|
-
} from 'tgtone-auth-client';
|
|
262
|
-
|
|
263
|
-
interface TGTSession {
|
|
264
|
-
user: TGTUser;
|
|
265
|
-
tenantId: string;
|
|
266
|
-
tenantName: string;
|
|
267
|
-
roles: Record<string, string[]>; // { "console": ["admin"] }
|
|
268
|
-
expiresAt: Date;
|
|
140
|
+
// ¿Tiene acceso a CRM?
|
|
141
|
+
if (authClient.hasAccessToApp('crm')) {
|
|
142
|
+
// Mostrar enlace a CRM
|
|
269
143
|
}
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
---
|
|
273
|
-
|
|
274
|
-
## ⚠️ Breaking Changes
|
|
275
|
-
|
|
276
|
-
Si usabas v1.0, actualizar código:
|
|
277
144
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
const user = await auth.checkSession();
|
|
281
|
-
console.log(user.email);
|
|
282
|
-
|
|
283
|
-
// ✅ Ahora (v1.1)
|
|
284
|
-
const session = await auth.checkSession();
|
|
285
|
-
console.log(session.user.email);
|
|
286
|
-
console.log(session.tenantId); // ← NUEVO
|
|
145
|
+
// Obtener todos los roles
|
|
146
|
+
const roles = authClient.getRoles('console'); // ['owner', 'admin']
|
|
287
147
|
```
|
|
288
148
|
|
|
289
149
|
---
|
|
290
150
|
|
|
291
|
-
##
|
|
292
|
-
|
|
293
|
-
### Error: "Invalid token: missing tenant_id"
|
|
294
|
-
→ Tu backend no está incluyendo `tenant_id` en el JWT. Verificar `auth.service.ts`.
|
|
295
|
-
|
|
296
|
-
### Error: "Token expired"
|
|
297
|
-
→ El token tiene > 90 días (dev) o > 1h (prod). Re-login automático.
|
|
298
|
-
|
|
299
|
-
### No redirige a login
|
|
300
|
-
→ Verificar que `identityUrl` y `appDomain` sean correctos.
|
|
301
|
-
|
|
302
|
-
---
|
|
303
|
-
|
|
304
|
-
## 📚 Docs Completas
|
|
305
|
-
|
|
306
|
-
Ver: `sdk/README.md` o https://github.com/tgt-technology/tgtone-identity-auth
|
|
307
|
-
|
|
308
|
-
---
|
|
309
|
-
|
|
310
|
-
## 📋 API
|
|
311
|
-
|
|
312
|
-
### `checkSession(): Promise<TGTUser | null>`
|
|
313
|
-
Verifica sesión. Lee token de URL (si viene del redirect), lo guarda en localStorage y valida con backend.
|
|
314
|
-
|
|
315
|
-
```typescript
|
|
316
|
-
const user = await auth.checkSession();
|
|
317
|
-
if (user) {
|
|
318
|
-
console.log('Usuario logueado:', user.email);
|
|
319
|
-
}
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
### `getUser(): TGTUser | null`
|
|
323
|
-
Obtiene usuario en memoria (sin request). Requiere llamar `checkSession()` primero.
|
|
324
|
-
|
|
325
|
-
### `logout(): Promise<void>`
|
|
326
|
-
Cierra sesión, limpia localStorage y redirige al login.
|
|
151
|
+
## 🆕 Verificación Silenciosa (v1.2.0)
|
|
327
152
|
|
|
328
|
-
|
|
329
|
-
Verifica si tiene un rol específico.
|
|
153
|
+
Para páginas que no requieren autenticación obligatoria:
|
|
330
154
|
|
|
331
155
|
```typescript
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
156
|
+
// Landing page o página pública
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
const checkAuth = async () => {
|
|
159
|
+
const session = await authClient.checkSessionSilent();
|
|
160
|
+
|
|
161
|
+
if (session) {
|
|
162
|
+
// Usuario logeado - mostrar contenido personalizado
|
|
163
|
+
showButton('Ir al Dashboard', '/dashboard');
|
|
164
|
+
} else {
|
|
165
|
+
// Usuario no logeado - mostrar botón de login
|
|
166
|
+
showButton('Iniciar Sesión', () => authClient.redirectToLogin());
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
checkAuth();
|
|
171
|
+
}, []);
|
|
335
172
|
```
|
|
336
173
|
|
|
337
|
-
### `getRoles(app: string): string[]`
|
|
338
|
-
Obtiene roles del usuario en una app.
|
|
339
|
-
|
|
340
|
-
### `redirectToLogin(): void`
|
|
341
|
-
Redirige manualmente al login.
|
|
342
|
-
|
|
343
174
|
---
|
|
344
175
|
|
|
345
|
-
## 🔄 Flujo
|
|
176
|
+
## 🔄 Flujo de Autenticación
|
|
346
177
|
|
|
347
178
|
```
|
|
348
|
-
1. Usuario
|
|
349
|
-
↓
|
|
350
|
-
2. App ejecuta auth.checkSession()
|
|
179
|
+
1. Usuario entra a tu app
|
|
351
180
|
↓
|
|
352
|
-
|
|
181
|
+
2. checkSession() no encuentra token
|
|
353
182
|
↓
|
|
354
|
-
|
|
183
|
+
3. SDK redirige a identity.tgtone.cl/login
|
|
355
184
|
↓
|
|
356
|
-
|
|
357
|
-
→ Identity redirige a tu-dominio?token=eyJ...
|
|
358
|
-
→ SDK guarda token y retorna sesión
|
|
359
|
-
|
|
360
|
-
5b. 🔐 Con MFA habilitado:
|
|
361
|
-
→ Identity muestra pantalla de código de 6 dígitos
|
|
362
|
-
→ Usuario ingresa código de Google Authenticator
|
|
363
|
-
→ Identity valida código con backend
|
|
364
|
-
→ ✅ Código válido → Identity redirige a tu-dominio?token=eyJ...
|
|
365
|
-
→ SDK guarda token y retorna sesión
|
|
185
|
+
4. Usuario hace login (+ MFA si está habilitado)
|
|
366
186
|
↓
|
|
367
|
-
|
|
187
|
+
5. Identity redirige: tu-app?token=eyJhbGci...
|
|
368
188
|
↓
|
|
369
|
-
|
|
189
|
+
6. SDK guarda token y valida con backend
|
|
370
190
|
↓
|
|
371
|
-
|
|
191
|
+
7. ✅ Usuario autenticado
|
|
372
192
|
```
|
|
373
193
|
|
|
374
|
-
**💡 Clave:** Tu app en Lovable NO necesita manejar MFA. Identity lo hace todo y te devuelve el token final ya validado.
|
|
375
|
-
|
|
376
194
|
---
|
|
377
195
|
|
|
378
|
-
## 🧪 Testing
|
|
379
|
-
|
|
380
|
-
Para desarrollo rápido sin pasar por login:
|
|
196
|
+
## 🧪 Testing sin Login
|
|
381
197
|
|
|
382
198
|
```typescript
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
const testToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
|
|
387
|
-
localStorage.setItem('tgtone_auth_token', testToken);
|
|
388
|
-
|
|
389
|
-
// 3. Ahora checkSession funcionará
|
|
390
|
-
auth.checkSession().then(setUser);
|
|
391
|
-
}, []);
|
|
392
|
-
```
|
|
199
|
+
// Guardar token manualmente en localStorage
|
|
200
|
+
localStorage.setItem('tgtone_auth_token', 'eyJhbGci...');
|
|
201
|
+
window.location.reload();
|
|
393
202
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
-H "Content-Type: application/json" \
|
|
398
|
-
-d '{"email": "superadmin@tgtone.cl", "password": "Tgt123."}'
|
|
203
|
+
// Obtener token de prueba con curl:
|
|
204
|
+
// curl -X POST https://tgtone-console-backend.run.app/api/v1/auth/login \
|
|
205
|
+
// -d '{"email": "user@test.cl", "password": "Test123!"}'
|
|
399
206
|
```
|
|
400
207
|
|
|
401
208
|
---
|
|
402
209
|
|
|
403
|
-
##
|
|
404
|
-
|
|
405
|
-
```typescript
|
|
406
|
-
interface TGTUser {
|
|
407
|
-
sub: string; // ID usuario
|
|
408
|
-
email: string;
|
|
409
|
-
name: string;
|
|
410
|
-
tenant_id: string;
|
|
411
|
-
tenant_name: string;
|
|
412
|
-
roles: Record<string, string[]>; // { console: ['owner'], zenith: ['admin'] }
|
|
413
|
-
}
|
|
414
|
-
```
|
|
415
|
-
|
|
416
|
-
---
|
|
210
|
+
## 📚 API Completa
|
|
417
211
|
|
|
418
|
-
|
|
212
|
+
Ver documentación completa: **[README.md](../README.md)**
|
|
419
213
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
---
|
|
431
|
-
|
|
432
|
-
## 🔐 Cómo Funciona
|
|
433
|
-
|
|
434
|
-
1. **Token en URL**: Después del login, Identity redirige con `?token=...`
|
|
435
|
-
2. **localStorage**: SDK guarda token en `tgtone_auth_token`
|
|
436
|
-
3. **Bearer Token**: Todas las requests usan `Authorization: Bearer <token>`
|
|
437
|
-
4. **Validación**: Backend valida JWT y retorna datos del usuario
|
|
438
|
-
5. **No cookies**: Todo se maneja con Bearer tokens en localStorage
|
|
439
|
-
|
|
440
|
-
---
|
|
441
|
-
|
|
442
|
-
## 🛠️ Para Producción
|
|
443
|
-
|
|
444
|
-
Cambiar `identityUrl` según ambiente:
|
|
445
|
-
|
|
446
|
-
```typescript
|
|
447
|
-
const auth = new TGTAuthClient({
|
|
448
|
-
identityUrl: import.meta.env.PROD
|
|
449
|
-
? 'https://identity.tgtone.cl'
|
|
450
|
-
: 'https://identity.tgtone.cl', // Mismo dominio en dev y prod
|
|
451
|
-
appDomain: window.location.hostname
|
|
452
|
-
});
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
---
|
|
456
|
-
|
|
457
|
-
## 🔐 Seguridad MFA
|
|
458
|
-
|
|
459
|
-
El sistema MFA de TGT One usa **TOTP (Time-based One-Time Password)** basado en RFC 6238:
|
|
460
|
-
|
|
461
|
-
- 🔑 **Secret compartido**: Generado al activar MFA, guardado encriptado en BD
|
|
462
|
-
- ⏱️ **Códigos temporales**: Válidos por 30 segundos
|
|
463
|
-
- 🔄 **Ventana de tolerancia**: ±30 segundos para compensar desfase de reloj
|
|
464
|
-
- 📱 **Compatible con**: Google Authenticator, Microsoft Authenticator, Authy, 1Password, etc.
|
|
465
|
-
|
|
466
|
-
### Cómo funciona:
|
|
467
|
-
```
|
|
468
|
-
Usuario habilita MFA → Backend genera secret único
|
|
469
|
-
→ Muestra QR code
|
|
470
|
-
→ Usuario escanea con Google Authenticator
|
|
471
|
-
→ App genera códigos cada 30s: HMAC-SHA1(secret, tiempo)
|
|
472
|
-
→ Al login: Backend valida con mismo algoritmo
|
|
473
|
-
```
|
|
214
|
+
Métodos disponibles:
|
|
215
|
+
- `checkSession()` - Verifica sesión (redirige si no hay)
|
|
216
|
+
- `checkSessionSilent()` - Verifica sin redirigir
|
|
217
|
+
- `signup()` - Crear cuenta
|
|
218
|
+
- `login()` - Iniciar sesión
|
|
219
|
+
- `logout()` - Cerrar sesión
|
|
220
|
+
- `verifyMfa()` - Validar código MFA
|
|
221
|
+
- `hasRole()` - Verificar roles
|
|
222
|
+
- `redirectToLogin()` - Redirigir a login
|
|
474
223
|
|
|
475
224
|
---
|
|
476
225
|
|
|
477
|
-
**Última actualización:** 2025-
|
|
478
|
-
**Versión SDK:** v1.2.0
|
|
226
|
+
**Última actualización:** 2025-11-03
|