@tgtone/auth-sdk 1.2.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/dist/tgtone-auth-client.d.ts.map +1 -1
- package/dist/tgtone-auth-client.js +6 -4
- 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,433 +0,0 @@
|
|
|
1
|
-
# 🔐 Reglas de Integración del SDK TGT Auth Client
|
|
2
|
-
|
|
3
|
-
> **Para Lovable AI**: Este documento especifica EXACTAMENTE cómo integrar `tgtone-auth-client` en Console.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## ❌ Lo que Lovable NO debe hacer
|
|
8
|
-
|
|
9
|
-
### 1. NO crear sistema de "Mock Auth"
|
|
10
|
-
```typescript
|
|
11
|
-
// ❌ ELIMINAR ESTO:
|
|
12
|
-
import { checkMockSession, mockLogout } from '@/services/mockAuth.service';
|
|
13
|
-
const isMockMode = import.meta.env.VITE_AUTH_MODE === 'mock';
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
**Razón**: El SDK ya maneja todo. No necesitas simular autenticación.
|
|
17
|
-
|
|
18
|
-
### 2. NO crear variables de entorno personalizadas
|
|
19
|
-
```typescript
|
|
20
|
-
// ❌ NO USAR:
|
|
21
|
-
VITE_AUTH_MODE=mock
|
|
22
|
-
VITE_IDENTITY_URL=...
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
**Razón**: Las URLs están hardcodeadas en el código según ambiente (dev/prod).
|
|
26
|
-
|
|
27
|
-
### 3. NO inicializar el cliente múltiples veces
|
|
28
|
-
```typescript
|
|
29
|
-
// ❌ MAL - Crear cliente en cada componente
|
|
30
|
-
function MyComponent() {
|
|
31
|
-
const auth = new TGTAuthClient({ ... });
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// ✅ BIEN - Crear UNA VEZ en AuthContext
|
|
35
|
-
let authClient: TGTAuthClient | null = null;
|
|
36
|
-
const getAuthClient = () => {
|
|
37
|
-
if (!authClient) {
|
|
38
|
-
authClient = new TGTAuthClient({ ... });
|
|
39
|
-
}
|
|
40
|
-
return authClient;
|
|
41
|
-
};
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### 4. NO manejar redirecciones manualmente
|
|
45
|
-
```typescript
|
|
46
|
-
// ❌ MAL - Redirigir manualmente al login
|
|
47
|
-
if (!user) {
|
|
48
|
-
navigate('/login');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// ✅ BIEN - El SDK redirige automáticamente
|
|
52
|
-
if (!user) {
|
|
53
|
-
return null; // SDK ya está redirigiendo
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
### 5. NO crear páginas de login
|
|
58
|
-
```typescript
|
|
59
|
-
// ❌ ELIMINAR:
|
|
60
|
-
<Route path="/login" element={<Login />} />
|
|
61
|
-
<Route path="/auth" element={<Auth />} />
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
**Razón**: El login está en `identity.tgtone.cl`, no en Console.
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## ✅ Implementación Correcta
|
|
69
|
-
|
|
70
|
-
### 1. AuthContext.tsx (Mínimo Necesario)
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
import { createContext, useContext, useEffect, useState, ReactNode } from 'react';
|
|
74
|
-
import { TGTAuthClient, TGTUser } from 'tgtone-auth-client';
|
|
75
|
-
|
|
76
|
-
interface AuthContextType {
|
|
77
|
-
user: TGTUser | null;
|
|
78
|
-
loading: boolean;
|
|
79
|
-
logout: () => Promise<void>;
|
|
80
|
-
hasRole: (appName: string, roleName: string) => boolean;
|
|
81
|
-
hasAccessToApp: (appName: string) => boolean;
|
|
82
|
-
getRoles: (appName: string) => string[];
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const AuthContext = createContext<AuthContextType | null>(null);
|
|
86
|
-
|
|
87
|
-
// Singleton del cliente
|
|
88
|
-
let authClient: TGTAuthClient | null = null;
|
|
89
|
-
|
|
90
|
-
const getAuthClient = (): TGTAuthClient => {
|
|
91
|
-
if (!authClient) {
|
|
92
|
-
authClient = new TGTAuthClient({
|
|
93
|
-
// ✅ URL unificada con reverse proxy
|
|
94
|
-
identityUrl: 'https://identity.tgtone.cl',
|
|
95
|
-
appDomain: window.location.hostname,
|
|
96
|
-
debug: import.meta.env.DEV
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
return authClient;
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
export function AuthProvider({ children }: { children: ReactNode }) {
|
|
103
|
-
const [user, setUser] = useState<TGTUser | null>(null);
|
|
104
|
-
const [loading, setLoading] = useState(true);
|
|
105
|
-
|
|
106
|
-
useEffect(() => {
|
|
107
|
-
const initAuth = async () => {
|
|
108
|
-
try {
|
|
109
|
-
const auth = getAuthClient();
|
|
110
|
-
const currentUser = await auth.checkSession();
|
|
111
|
-
setUser(currentUser);
|
|
112
|
-
} catch (error) {
|
|
113
|
-
console.error('Error checking session:', error);
|
|
114
|
-
setUser(null);
|
|
115
|
-
} finally {
|
|
116
|
-
setLoading(false);
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
initAuth();
|
|
121
|
-
}, []);
|
|
122
|
-
|
|
123
|
-
const logout = async () => {
|
|
124
|
-
const auth = getAuthClient();
|
|
125
|
-
await auth.logout();
|
|
126
|
-
setUser(null);
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
const hasRole = (appName: string, roleName: string) => {
|
|
130
|
-
const auth = getAuthClient();
|
|
131
|
-
return auth.hasRole(appName, roleName);
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
const hasAccessToApp = (appName: string) => {
|
|
135
|
-
const auth = getAuthClient();
|
|
136
|
-
return auth.hasAccessToApp(appName);
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
const getRoles = (appName: string) => {
|
|
140
|
-
const auth = getAuthClient();
|
|
141
|
-
return auth.getRoles(appName);
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
return (
|
|
145
|
-
<AuthContext.Provider value={{
|
|
146
|
-
user,
|
|
147
|
-
loading,
|
|
148
|
-
logout,
|
|
149
|
-
hasRole,
|
|
150
|
-
hasAccessToApp,
|
|
151
|
-
getRoles
|
|
152
|
-
}}>
|
|
153
|
-
{children}
|
|
154
|
-
</AuthContext.Provider>
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export function useAuth() {
|
|
159
|
-
const context = useContext(AuthContext);
|
|
160
|
-
if (!context) {
|
|
161
|
-
throw new Error('useAuth debe usarse dentro de AuthProvider');
|
|
162
|
-
}
|
|
163
|
-
return context;
|
|
164
|
-
}
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
### 2. ProtectedRoute.tsx (Ya está bien implementado)
|
|
168
|
-
|
|
169
|
-
```typescript
|
|
170
|
-
import { ReactNode } from 'react';
|
|
171
|
-
import { useAuth } from '@/contexts/AuthContext';
|
|
172
|
-
import { Card, CardContent } from '@/components/ui/card';
|
|
173
|
-
|
|
174
|
-
interface ProtectedRouteProps {
|
|
175
|
-
children: ReactNode;
|
|
176
|
-
requiredApp?: string;
|
|
177
|
-
requiredRole?: string;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
export function ProtectedRoute({
|
|
181
|
-
children,
|
|
182
|
-
requiredApp,
|
|
183
|
-
requiredRole
|
|
184
|
-
}: ProtectedRouteProps) {
|
|
185
|
-
const { user, loading, hasRole, hasAccessToApp } = useAuth();
|
|
186
|
-
|
|
187
|
-
// Mostrar loading mientras verifica sesión
|
|
188
|
-
if (loading) {
|
|
189
|
-
return (
|
|
190
|
-
<div className="min-h-screen flex items-center justify-center">
|
|
191
|
-
<Card className="w-full max-w-md">
|
|
192
|
-
<CardContent className="pt-6">
|
|
193
|
-
<div className="flex flex-col items-center space-y-4">
|
|
194
|
-
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary"></div>
|
|
195
|
-
<p className="text-muted-foreground">Verificando autenticación...</p>
|
|
196
|
-
</div>
|
|
197
|
-
</CardContent>
|
|
198
|
-
</Card>
|
|
199
|
-
</div>
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Si no hay usuario, el SDK ya está redirigiendo a Identity
|
|
204
|
-
if (!user) {
|
|
205
|
-
return null;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Verificar acceso a la app
|
|
209
|
-
if (requiredApp && !hasAccessToApp(requiredApp)) {
|
|
210
|
-
return (
|
|
211
|
-
<div className="min-h-screen flex items-center justify-center">
|
|
212
|
-
<Card className="w-full max-w-md">
|
|
213
|
-
<CardContent className="pt-6">
|
|
214
|
-
<h2 className="text-xl font-semibold text-destructive">
|
|
215
|
-
Acceso Denegado
|
|
216
|
-
</h2>
|
|
217
|
-
<p className="text-muted-foreground">
|
|
218
|
-
No tienes permisos para acceder a esta aplicación.
|
|
219
|
-
</p>
|
|
220
|
-
</CardContent>
|
|
221
|
-
</Card>
|
|
222
|
-
</div>
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Verificar rol específico
|
|
227
|
-
if (requiredApp && requiredRole && !hasRole(requiredApp, requiredRole)) {
|
|
228
|
-
return (
|
|
229
|
-
<div className="min-h-screen flex items-center justify-center">
|
|
230
|
-
<Card className="w-full max-w-md">
|
|
231
|
-
<CardContent className="pt-6">
|
|
232
|
-
<h2 className="text-xl font-semibold text-destructive">
|
|
233
|
-
Permisos Insuficientes
|
|
234
|
-
</h2>
|
|
235
|
-
<p className="text-muted-foreground">
|
|
236
|
-
No tienes el rol necesario para acceder a esta página.
|
|
237
|
-
</p>
|
|
238
|
-
</CardContent>
|
|
239
|
-
</Card>
|
|
240
|
-
</div>
|
|
241
|
-
);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return <>{children}</>;
|
|
245
|
-
}
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### 3. App.tsx (Sin rutas de login)
|
|
249
|
-
|
|
250
|
-
```typescript
|
|
251
|
-
import { AuthProvider } from "./contexts/AuthContext";
|
|
252
|
-
import { ProtectedRoute } from "./components/ProtectedRoute";
|
|
253
|
-
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
|
254
|
-
|
|
255
|
-
const App = () => (
|
|
256
|
-
<BrowserRouter>
|
|
257
|
-
<AuthProvider>
|
|
258
|
-
<Routes>
|
|
259
|
-
{/* Todas las rutas protegidas */}
|
|
260
|
-
<Route
|
|
261
|
-
path="/dashboard"
|
|
262
|
-
element={
|
|
263
|
-
<ProtectedRoute requiredApp="console">
|
|
264
|
-
<Dashboard />
|
|
265
|
-
</ProtectedRoute>
|
|
266
|
-
}
|
|
267
|
-
/>
|
|
268
|
-
|
|
269
|
-
<Route
|
|
270
|
-
path="/dashboard/users"
|
|
271
|
-
element={
|
|
272
|
-
<ProtectedRoute requiredApp="console" requiredRole="admin">
|
|
273
|
-
<Users />
|
|
274
|
-
</ProtectedRoute>
|
|
275
|
-
}
|
|
276
|
-
/>
|
|
277
|
-
|
|
278
|
-
{/* Más rutas... */}
|
|
279
|
-
</Routes>
|
|
280
|
-
</AuthProvider>
|
|
281
|
-
</BrowserRouter>
|
|
282
|
-
);
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
---
|
|
286
|
-
|
|
287
|
-
## 🔄 Flujo de Autenticación (Lo que hace el SDK)
|
|
288
|
-
|
|
289
|
-
```
|
|
290
|
-
1. Usuario entra a console.tgtone.cl
|
|
291
|
-
↓
|
|
292
|
-
2. AuthContext se monta → ejecuta auth.checkSession()
|
|
293
|
-
↓
|
|
294
|
-
3. SDK busca token en URL (?token=...) o localStorage
|
|
295
|
-
↓
|
|
296
|
-
4. Si NO hay token → SDK redirige a identity.tgtone.cl/login?redirect=console.tgtone.cl
|
|
297
|
-
↓
|
|
298
|
-
5. Usuario hace login en Identity
|
|
299
|
-
↓
|
|
300
|
-
6. Identity redirige a console.tgtone.cl?token=eyJhbGci...
|
|
301
|
-
↓
|
|
302
|
-
7. SDK lee token de URL → lo guarda en localStorage
|
|
303
|
-
↓
|
|
304
|
-
8. SDK valida token con backend (GET /api/v1/auth/me)
|
|
305
|
-
↓
|
|
306
|
-
9. Si válido → setUser(userData) → App se renderiza
|
|
307
|
-
↓
|
|
308
|
-
10. Si inválido → SDK redirige a login nuevamente
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
**Lovable NO debe intervenir en este flujo. Todo lo maneja el SDK.**
|
|
312
|
-
|
|
313
|
-
---
|
|
314
|
-
|
|
315
|
-
## 🧪 Testing Local (Sin Login)
|
|
316
|
-
|
|
317
|
-
Para desarrollo rápido sin pasar por el flujo completo:
|
|
318
|
-
|
|
319
|
-
```typescript
|
|
320
|
-
// En consola del browser o en código temporal
|
|
321
|
-
const testToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'; // Token de curl
|
|
322
|
-
localStorage.setItem('tgtone_auth_token', testToken);
|
|
323
|
-
window.location.reload();
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
**Obtener token de prueba:**
|
|
327
|
-
```bash
|
|
328
|
-
curl -X POST https://tgtone-identity-backend-1086175589490.us-central1.run.app/api/v1/auth/login \
|
|
329
|
-
-H "Content-Type: application/json" \
|
|
330
|
-
-d '{"email": "console@tgtone.cl", "password": "Tgt123."}'
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
---
|
|
334
|
-
|
|
335
|
-
## 📦 Dependencias Necesarias
|
|
336
|
-
|
|
337
|
-
```json
|
|
338
|
-
{
|
|
339
|
-
"dependencies": {
|
|
340
|
-
"tgtone-auth-client": "^1.0.2"
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
**NO se necesita:**
|
|
346
|
-
- `@supabase/supabase-js`
|
|
347
|
-
- Ningún otro cliente de auth
|
|
348
|
-
|
|
349
|
-
---
|
|
350
|
-
|
|
351
|
-
## 🎯 Verificación de Roles
|
|
352
|
-
|
|
353
|
-
```typescript
|
|
354
|
-
// En cualquier componente
|
|
355
|
-
import { useAuth } from '@/contexts/AuthContext';
|
|
356
|
-
|
|
357
|
-
function MyComponent() {
|
|
358
|
-
const { user, hasRole, getRoles } = useAuth();
|
|
359
|
-
|
|
360
|
-
// Verificar rol específico
|
|
361
|
-
if (hasRole('console', 'admin')) {
|
|
362
|
-
return <AdminPanel />;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// Obtener todos los roles
|
|
366
|
-
const roles = getRoles('console'); // ['owner', 'admin']
|
|
367
|
-
|
|
368
|
-
// Acceder a datos del usuario
|
|
369
|
-
console.log(user.email); // console@tgtone.cl
|
|
370
|
-
console.log(user.tenant_name); // Console Only Tenant
|
|
371
|
-
console.log(user.roles); // { console: ['owner', 'admin'] }
|
|
372
|
-
}
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
---
|
|
376
|
-
|
|
377
|
-
## 🚨 Errores Comunes
|
|
378
|
-
|
|
379
|
-
### Error: "Failed to fetch"
|
|
380
|
-
**Causa**: CORS o backend offline
|
|
381
|
-
**Solución**: Verificar que backend esté corriendo y CORS permita tu origen
|
|
382
|
-
|
|
383
|
-
### Error: "No se puede leer token"
|
|
384
|
-
**Causa**: localStorage bloqueado o sesión expirada
|
|
385
|
-
**Solución**: Limpiar localStorage y volver a hacer login
|
|
386
|
-
|
|
387
|
-
### Error: "Bucle infinito de redirecciones"
|
|
388
|
-
**Causa**: Token inválido guardado en localStorage
|
|
389
|
-
**Solución**:
|
|
390
|
-
```typescript
|
|
391
|
-
localStorage.removeItem('tgtone_auth_token');
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
---
|
|
395
|
-
|
|
396
|
-
## 📚 Estructura de Archivos
|
|
397
|
-
|
|
398
|
-
```
|
|
399
|
-
src/
|
|
400
|
-
├── contexts/
|
|
401
|
-
│ └── AuthContext.tsx ✅ Solo este archivo
|
|
402
|
-
├── components/
|
|
403
|
-
│ └── ProtectedRoute.tsx ✅ Ya implementado
|
|
404
|
-
└── App.tsx ✅ Wrapper con AuthProvider
|
|
405
|
-
|
|
406
|
-
❌ NO crear:
|
|
407
|
-
├── services/
|
|
408
|
-
│ └── mockAuth.service.ts ❌ Eliminar
|
|
409
|
-
├── pages/
|
|
410
|
-
│ ├── Login.tsx ❌ Eliminar
|
|
411
|
-
│ └── Auth.tsx ❌ Eliminar
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
---
|
|
415
|
-
|
|
416
|
-
## 🎯 Resumen para Lovable
|
|
417
|
-
|
|
418
|
-
**Cuando Lovable necesite implementar autenticación:**
|
|
419
|
-
|
|
420
|
-
1. ✅ Instalar: `npm install tgtone-auth-client`
|
|
421
|
-
2. ✅ Copiar `AuthContext.tsx` de este documento (sin cambios)
|
|
422
|
-
3. ✅ Usar `ProtectedRoute` para rutas privadas
|
|
423
|
-
4. ✅ Usar `useAuth()` hook en componentes
|
|
424
|
-
5. ❌ NO crear sistema mock
|
|
425
|
-
6. ❌ NO crear páginas de login
|
|
426
|
-
7. ❌ NO agregar variables de entorno personalizadas
|
|
427
|
-
8. ❌ NO manejar tokens manualmente
|
|
428
|
-
|
|
429
|
-
**El SDK hace TODO el trabajo de autenticación.**
|
|
430
|
-
|
|
431
|
-
---
|
|
432
|
-
|
|
433
|
-
**Última actualización:** 2025-10-17
|