@tgtone/auth-sdk 1.4.3 → 1.4.4

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.
@@ -1,177 +1,131 @@
1
- # 🔧 Ejemplos de Integración por Framework
1
+ # Ejemplos de Integración por Framework
2
2
 
3
- Guía paso a paso para integrar TGT One Auth en diferentes frameworks.
3
+ Guía paso a paso para integrar TGT One Auth en diferentes frameworks usando el paquete npm.
4
4
 
5
5
  ---
6
6
 
7
- ## 📘 React (Vite)
7
+ ## React (Vite)
8
8
 
9
- ### **1. Copiar archivos**
9
+ ### 1. Instalar
10
10
 
11
11
  ```bash
12
- # En tu proyecto React
13
- mkdir -p src/lib/auth
14
- cp tgtone-auth-client.ts src/lib/auth/
15
- cp react-hook.tsx src/lib/auth/
12
+ npm install @tgtone/auth-sdk
16
13
  ```
17
14
 
18
- ### **2. Crear contexto de autenticación**
15
+ ### 2. Usar el hook `useTGTAuth`
19
16
 
20
17
  ```tsx
21
- // src/contexts/AuthContext.tsx
22
- import { createContext, useContext } from 'react';
23
- import { useTGTAuth, UseTGTAuthResult } from '@/lib/auth/react-hook';
24
-
25
- const AuthContext = createContext<UseTGTAuthResult | null>(null);
18
+ // src/App.tsx
19
+ import { useTGTAuth } from '@tgtone/auth-sdk/react';
26
20
 
27
- export function AuthProvider({ children }: { children: React.ReactNode }) {
28
- const auth = useTGTAuth({
29
- identityUrl: 'https://identity.tgtone.cl',
30
- appDomain: 'zenith.tgtone.cl', // Cambiar según tu app
31
- debug: import.meta.env.DEV
21
+ function App() {
22
+ const { session, loading, logout, hasRole, revokedError } = useTGTAuth({
23
+ identityUrl: import.meta.env.VITE_IDENTITY_URL || 'https://identity.tgtone.cl',
24
+ appDomain: window.location.host,
25
+ appKey: import.meta.env.VITE_APP_KEY, // Requerido en dev
26
+ enableHeartbeat: true,
27
+ debug: import.meta.env.DEV,
32
28
  });
33
29
 
30
+ if (loading) return <LoadingSpinner />;
31
+ if (revokedError) return <RevokedPage error={revokedError} />;
32
+ if (!session) return null;
33
+
34
34
  return (
35
- <AuthContext.Provider value={auth}>
36
- {children}
37
- </AuthContext.Provider>
35
+ <div>
36
+ <header>
37
+ <span>{session.user.name} - {session.tenantName}</span>
38
+ <button onClick={logout}>Cerrar sesión</button>
39
+ </header>
40
+ {hasRole('zenith', 'admin') && <AdminPanel />}
41
+ </div>
38
42
  );
39
43
  }
40
-
41
- export const useAuth = () => {
42
- const context = useContext(AuthContext);
43
- if (!context) {
44
- throw new Error('useAuth debe usarse dentro de AuthProvider');
45
- }
46
- return context;
47
- };
48
44
  ```
49
45
 
50
- ### **3. Envolver tu app**
46
+ ### 3. Interceptor Axios para API calls
51
47
 
52
48
  ```tsx
53
- // src/main.tsx
54
- import { StrictMode } from 'react';
55
- import { createRoot } from 'react-dom/client';
56
- import { AuthProvider } from './contexts/AuthContext';
57
- import App from './App';
58
-
59
- createRoot(document.getElementById('root')!).render(
60
- <StrictMode>
61
- <AuthProvider>
62
- <App />
63
- </AuthProvider>
64
- </StrictMode>,
65
- );
66
- ```
67
-
68
- ### **4. Usar en componentes**
69
-
70
- ```tsx
71
- // src/App.tsx
72
- import { useAuth } from './contexts/AuthContext';
73
-
74
- function App() {
75
- const { user, loading, logout, hasRole } = useAuth();
76
-
77
- if (loading) {
78
- return (
79
- <div className="flex items-center justify-center min-h-screen">
80
- <div className="animate-spin rounded-full h-32 w-32 border-b-2 border-gray-900"></div>
81
- </div>
82
- );
83
- }
49
+ import { createAxiosInterceptor } from '@tgtone/auth-sdk/interceptor';
50
+ import { useTGTAuth } from '@tgtone/auth-sdk/react';
84
51
 
85
- if (!user) {
86
- // Ya está redirigiendo al login
87
- return null;
88
- }
52
+ function Dashboard() {
53
+ const { authClient, session } = useTGTAuth({
54
+ identityUrl: 'https://identity.tgtone.cl',
55
+ appDomain: 'zenith.tgtone.cl',
56
+ });
89
57
 
90
- return (
91
- <div className="container mx-auto p-4">
92
- <nav className="flex justify-between items-center mb-8">
93
- <h1 className="text-2xl font-bold">Zenith CRM</h1>
94
- <div className="flex items-center gap-4">
95
- <span>{user.name}</span>
96
- <span className="text-sm text-gray-600">{user.tenant_name}</span>
97
- <button
98
- onClick={logout}
99
- className="px-4 py-2 bg-red-500 text-white rounded"
100
- >
101
- Cerrar sesión
102
- </button>
103
- </div>
104
- </nav>
58
+ useEffect(() => {
59
+ if (!session) return;
105
60
 
106
- {hasRole('zenith', 'admin') && (
107
- <div className="bg-yellow-100 p-4 rounded mb-4">
108
- <h2>Panel de Administración</h2>
109
- </div>
110
- )}
61
+ const api = axios.create({ baseURL: '/api' });
62
+ const cleanup = createAxiosInterceptor(api, authClient, {
63
+ handleRevoked: true,
64
+ excludeUrls: ['/public/'],
65
+ });
111
66
 
112
- <main>
113
- {/* Tu contenido aquí */}
114
- </main>
115
- </div>
116
- );
67
+ return cleanup;
68
+ }, [session, authClient]);
117
69
  }
118
-
119
- export default App;
120
70
  ```
121
71
 
122
72
  ---
123
73
 
124
- ## 📗 Vue 3 (Vite)
74
+ ## Vue 3 (Vite)
125
75
 
126
- ### **1. Copiar archivo**
76
+ ### 1. Instalar
127
77
 
128
78
  ```bash
129
- mkdir -p src/lib/auth
130
- cp tgtone-auth-client.ts src/lib/auth/
79
+ npm install @tgtone/auth-sdk
131
80
  ```
132
81
 
133
- ### **2. Crear composable**
82
+ ### 2. Crear composable
134
83
 
135
84
  ```typescript
136
85
  // src/composables/useAuth.ts
137
86
  import { ref, onMounted } from 'vue';
138
- import { TGTAuthClient, type TGTUser } from '@/lib/auth/tgtone-auth-client';
87
+ import { TGTAuthClient, type TGTSession } from '@tgtone/auth-sdk';
139
88
 
140
89
  const authClient = new TGTAuthClient({
141
- identityUrl: 'https://identity.tgtone.cl',
142
- appDomain: 'baco.tgtone.cl', // Cambiar según tu app
143
- debug: import.meta.env.DEV
90
+ identityUrl: import.meta.env.VITE_IDENTITY_URL || 'https://identity.tgtone.cl',
91
+ appDomain: window.location.host,
92
+ appKey: import.meta.env.VITE_APP_KEY, // Requerido en dev
93
+ heartbeatIntervalMs: 5 * 60 * 1000,
94
+ debug: import.meta.env.DEV,
144
95
  });
145
96
 
146
- const user = ref<TGTUser | null>(null);
97
+ const session = ref<TGTSession | null>(null);
147
98
  const loading = ref(true);
148
99
 
149
100
  export function useAuth() {
150
101
  onMounted(async () => {
151
- user.value = await authClient.checkSession();
102
+ session.value = await authClient.checkSession();
152
103
  loading.value = false;
104
+
105
+ if (session.value) {
106
+ authClient.startHeartbeat();
107
+ }
153
108
  });
154
109
 
155
110
  const logout = async () => {
111
+ authClient.stopHeartbeat();
156
112
  await authClient.logout();
157
- user.value = null;
113
+ session.value = null;
158
114
  };
159
115
 
160
116
  const hasRole = (appName: string, roleName: string) => {
161
117
  return authClient.hasRole(appName, roleName);
162
118
  };
163
119
 
164
- return {
165
- user,
166
- loading,
167
- logout,
168
- hasRole,
169
- authClient
120
+ const hasAccessToApp = (appName: string) => {
121
+ return authClient.hasAccessToApp(appName);
170
122
  };
123
+
124
+ return { session, loading, logout, hasRole, hasAccessToApp, authClient };
171
125
  }
172
126
  ```
173
127
 
174
- ### **3. Usar en App.vue**
128
+ ### 3. Usar en componente
175
129
 
176
130
  ```vue
177
131
  <!-- src/App.vue -->
@@ -179,162 +133,87 @@ export function useAuth() {
179
133
  <div v-if="loading" class="loading">
180
134
  <div class="spinner"></div>
181
135
  </div>
182
-
183
- <div v-else-if="user" class="app">
184
- <nav class="navbar">
185
- <h1>Baco Wine Management</h1>
186
- <div class="user-info">
187
- <span>{{ user.name }}</span>
188
- <span class="tenant">{{ user.tenant_name }}</span>
189
- <button @click="logout" class="logout-btn">
190
- Cerrar sesión
191
- </button>
192
- </div>
136
+
137
+ <div v-else-if="session" class="app">
138
+ <nav>
139
+ <h1>Mi App</h1>
140
+ <span>{{ session.user.name }} - {{ session.tenantName }}</span>
141
+ <button @click="logout">Cerrar sesión</button>
193
142
  </nav>
194
143
 
195
- <div v-if="hasRole('baco', 'admin')" class="admin-panel">
144
+ <div v-if="hasRole('baco', 'admin')">
196
145
  Panel de Administración
197
146
  </div>
198
147
 
199
- <main>
200
- <!-- Tu contenido aquí -->
201
- </main>
148
+ <router-view />
202
149
  </div>
203
150
  </template>
204
151
 
205
152
  <script setup lang="ts">
206
153
  import { useAuth } from './composables/useAuth';
207
-
208
- const { user, loading, logout, hasRole } = useAuth();
154
+ const { session, loading, logout, hasRole } = useAuth();
209
155
  </script>
210
-
211
- <style scoped>
212
- .loading {
213
- display: flex;
214
- justify-content: center;
215
- align-items: center;
216
- height: 100vh;
217
- }
218
-
219
- .spinner {
220
- border: 4px solid #f3f3f3;
221
- border-top: 4px solid #3498db;
222
- border-radius: 50%;
223
- width: 40px;
224
- height: 40px;
225
- animation: spin 1s linear infinite;
226
- }
227
-
228
- @keyframes spin {
229
- 0% { transform: rotate(0deg); }
230
- 100% { transform: rotate(360deg); }
231
- }
232
- </style>
233
156
  ```
234
157
 
235
158
  ---
236
159
 
237
- ## 📙 Next.js 14 (App Router)
160
+ ## Next.js 14 (App Router)
238
161
 
239
- ### **1. Copiar archivo**
162
+ ### 1. Instalar
240
163
 
241
164
  ```bash
242
- mkdir -p lib/auth
243
- cp tgtone-auth-client.ts lib/auth/
165
+ npm install @tgtone/auth-sdk
244
166
  ```
245
167
 
246
- ### **2. Crear Provider**
168
+ ### 2. Crear Provider (client component)
247
169
 
248
170
  ```tsx
249
171
  // app/providers/auth-provider.tsx
250
172
  'use client';
251
173
 
252
- import { createContext, useContext, useEffect, useState, ReactNode } from 'react';
253
- import { TGTAuthClient, type TGTUser } from '@/lib/auth/tgtone-auth-client';
254
-
255
- interface AuthContextType {
256
- user: TGTUser | null;
257
- loading: boolean;
258
- logout: () => Promise<void>;
259
- hasRole: (appName: string, roleName: string) => boolean;
260
- }
174
+ import { useTGTAuth } from '@tgtone/auth-sdk/react';
175
+ import { createContext, useContext } from 'react';
176
+ import type { UseTGTAuthResult } from '@tgtone/auth-sdk/react';
261
177
 
262
- const AuthContext = createContext<AuthContextType | null>(null);
178
+ const AuthContext = createContext<UseTGTAuthResult | null>(null);
263
179
 
264
- export function AuthProvider({ children }: { children: ReactNode }) {
265
- const [user, setUser] = useState<TGTUser | null>(null);
266
- const [loading, setLoading] = useState(true);
267
-
268
- const authClient = new TGTAuthClient({
269
- identityUrl: 'https://identity.tgtone.cl',
270
- appDomain: 'console.tgtone.cl', // Cambiar según tu app
271
- debug: process.env.NODE_ENV === 'development'
180
+ export function AuthProvider({ children }: { children: React.ReactNode }) {
181
+ const auth = useTGTAuth({
182
+ identityUrl: process.env.NEXT_PUBLIC_IDENTITY_URL || 'https://identity.tgtone.cl',
183
+ appDomain: typeof window !== 'undefined' ? window.location.host : '',
184
+ appKey: process.env.NEXT_PUBLIC_APP_KEY,
185
+ enableHeartbeat: true,
186
+ debug: process.env.NODE_ENV === 'development',
272
187
  });
273
188
 
274
- useEffect(() => {
275
- async function checkAuth() {
276
- const authenticatedUser = await authClient.checkSession();
277
- setUser(authenticatedUser);
278
- setLoading(false);
279
- }
280
- checkAuth();
281
- }, []);
282
-
283
- const logout = async () => {
284
- await authClient.logout();
285
- setUser(null);
286
- };
287
-
288
- const hasRole = (appName: string, roleName: string) => {
289
- return authClient.hasRole(appName, roleName);
290
- };
291
-
292
- return (
293
- <AuthContext.Provider value={{ user, loading, logout, hasRole }}>
294
- {children}
295
- </AuthContext.Provider>
296
- );
189
+ return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>;
297
190
  }
298
191
 
299
192
  export const useAuth = () => {
300
193
  const context = useContext(AuthContext);
301
- if (!context) {
302
- throw new Error('useAuth debe usarse dentro de AuthProvider');
303
- }
194
+ if (!context) throw new Error('useAuth debe usarse dentro de AuthProvider');
304
195
  return context;
305
196
  };
306
197
  ```
307
198
 
308
- ### **3. Envolver en layout**
199
+ ### 3. Envolver en layout
309
200
 
310
201
  ```tsx
311
202
  // app/layout.tsx
312
203
  import { AuthProvider } from './providers/auth-provider';
313
- import './globals.css';
314
204
 
315
- export const metadata = {
316
- title: 'TGT One Console',
317
- description: 'Console de administración TGT One',
318
- };
319
-
320
- export default function RootLayout({
321
- children,
322
- }: {
323
- children: React.ReactNode;
324
- }) {
205
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
325
206
  return (
326
207
  <html lang="es">
327
208
  <body>
328
- <AuthProvider>
329
- {children}
330
- </AuthProvider>
209
+ <AuthProvider>{children}</AuthProvider>
331
210
  </body>
332
211
  </html>
333
212
  );
334
213
  }
335
214
  ```
336
215
 
337
- ### **4. Usar en páginas**
216
+ ### 4. Usar en páginas
338
217
 
339
218
  ```tsx
340
219
  // app/page.tsx
@@ -343,45 +222,19 @@ export default function RootLayout({
343
222
  import { useAuth } from './providers/auth-provider';
344
223
 
345
224
  export default function HomePage() {
346
- const { user, loading, logout, hasRole } = useAuth();
225
+ const { session, loading, logout, hasRole, revokedError } = useAuth();
347
226
 
348
- if (loading) {
349
- return <div className="flex h-screen items-center justify-center">
350
- <div className="animate-spin rounded-full h-32 w-32 border-b-2 border-gray-900"></div>
351
- </div>;
352
- }
353
-
354
- if (!user) {
355
- return null; // Redirigiendo
356
- }
227
+ if (loading) return <Spinner />;
228
+ if (revokedError) return <RevokedPage error={revokedError} />;
229
+ if (!session) return null;
357
230
 
358
231
  return (
359
- <div className="container mx-auto p-8">
360
- <nav className="flex justify-between items-center mb-8">
361
- <h1 className="text-3xl font-bold">TGT One Console</h1>
362
- <div className="flex items-center gap-4">
363
- <div className="text-right">
364
- <p className="font-medium">{user.name}</p>
365
- <p className="text-sm text-gray-600">{user.tenant_name}</p>
366
- </div>
367
- <button
368
- onClick={logout}
369
- className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
370
- >
371
- Cerrar sesión
372
- </button>
373
- </div>
374
- </nav>
375
-
376
- {hasRole('console', 'owner') && (
377
- <div className="bg-blue-100 p-4 rounded mb-6">
378
- <h2 className="font-bold">Configuración de Owner</h2>
379
- </div>
380
- )}
381
-
382
- <main>
383
- {/* Tu contenido */}
384
- </main>
232
+ <div>
233
+ <header>
234
+ <span>{session.user.name}</span>
235
+ <button onClick={logout}>Cerrar sesión</button>
236
+ </header>
237
+ {hasRole('console', 'owner') && <OwnerSettings />}
385
238
  </div>
386
239
  );
387
240
  }
@@ -389,39 +242,43 @@ export default function HomePage() {
389
242
 
390
243
  ---
391
244
 
392
- ## 📕 Angular 17+
245
+ ## Angular 17+
393
246
 
394
- ### **1. Copiar archivo**
247
+ ### 1. Instalar
395
248
 
396
249
  ```bash
397
- mkdir -p src/app/services/auth
398
- cp tgtone-auth-client.ts src/app/services/auth/
250
+ npm install @tgtone/auth-sdk
399
251
  ```
400
252
 
401
- ### **2. Crear servicio**
253
+ ### 2. Crear servicio
402
254
 
403
255
  ```typescript
404
- // src/app/services/auth/auth.service.ts
256
+ // src/app/services/auth.service.ts
405
257
  import { Injectable } from '@angular/core';
406
- import { BehaviorSubject, Observable } from 'rxjs';
407
- import { TGTAuthClient, TGTUser } from './tgtone-auth-client';
258
+ import { BehaviorSubject } from 'rxjs';
259
+ import { TGTAuthClient, TGTSession, AuthError } from '@tgtone/auth-sdk';
408
260
 
409
- @Injectable({
410
- providedIn: 'root'
411
- })
261
+ @Injectable({ providedIn: 'root' })
412
262
  export class AuthService {
413
263
  private authClient: TGTAuthClient;
414
- private userSubject = new BehaviorSubject<TGTUser | null>(null);
264
+ private sessionSubject = new BehaviorSubject<TGTSession | null>(null);
415
265
  private loadingSubject = new BehaviorSubject<boolean>(true);
266
+ private revokedSubject = new BehaviorSubject<AuthError | null>(null);
416
267
 
417
- public user$: Observable<TGTUser | null> = this.userSubject.asObservable();
418
- public loading$: Observable<boolean> = this.loadingSubject.asObservable();
268
+ session$ = this.sessionSubject.asObservable();
269
+ loading$ = this.loadingSubject.asObservable();
270
+ revokedError$ = this.revokedSubject.asObservable();
419
271
 
420
272
  constructor() {
421
273
  this.authClient = new TGTAuthClient({
422
274
  identityUrl: 'https://identity.tgtone.cl',
423
- appDomain: 'zenith.tgtone.cl', // Cambiar según tu app
424
- debug: !environment.production
275
+ appDomain: window.location.host,
276
+ appKey: 'console', // Ajustar según la app
277
+ onSessionRevoked: (error) => {
278
+ this.revokedSubject.next(error);
279
+ this.sessionSubject.next(null);
280
+ },
281
+ debug: !environment.production,
425
282
  });
426
283
 
427
284
  this.checkSession();
@@ -429,108 +286,110 @@ export class AuthService {
429
286
 
430
287
  private async checkSession() {
431
288
  try {
432
- const user = await this.authClient.checkSession();
433
- this.userSubject.next(user);
289
+ const session = await this.authClient.checkSession();
290
+ this.sessionSubject.next(session);
291
+
292
+ if (session) {
293
+ this.authClient.startHeartbeat();
294
+ }
434
295
  } catch (error) {
435
296
  console.error('Error checking session:', error);
436
- this.userSubject.next(null);
437
297
  } finally {
438
298
  this.loadingSubject.next(false);
439
299
  }
440
300
  }
441
301
 
442
302
  async logout() {
303
+ this.authClient.stopHeartbeat();
443
304
  await this.authClient.logout();
444
- this.userSubject.next(null);
305
+ this.sessionSubject.next(null);
445
306
  }
446
307
 
447
308
  hasRole(appName: string, roleName: string): boolean {
448
309
  return this.authClient.hasRole(appName, roleName);
449
310
  }
450
311
 
451
- get currentUser(): TGTUser | null {
452
- return this.userSubject.value;
312
+ hasAccessToApp(appName: string): boolean {
313
+ return this.authClient.hasAccessToApp(appName);
453
314
  }
454
315
  }
455
316
  ```
456
317
 
457
- ### **3. Usar en componentes**
318
+ ### 3. Usar en componente
458
319
 
459
320
  ```typescript
460
321
  // src/app/app.component.ts
461
322
  import { Component } from '@angular/core';
462
- import { AuthService } from './services/auth/auth.service';
323
+ import { AuthService } from './services/auth.service';
463
324
 
464
325
  @Component({
465
326
  selector: 'app-root',
466
327
  template: `
467
- <div *ngIf="authService.loading$ | async" class="loading">
328
+ <div *ngIf="auth.loading$ | async" class="loading">
468
329
  <div class="spinner"></div>
469
330
  </div>
470
331
 
471
- <div *ngIf="(authService.user$ | async) as user" class="app">
472
- <nav class="navbar">
473
- <h1>Zenith CRM</h1>
474
- <div class="user-info">
475
- <span>{{ user.name }}</span>
476
- <span class="tenant">{{ user.tenant_name }}</span>
477
- <button (click)="logout()" class="logout-btn">
478
- Cerrar sesión
479
- </button>
480
- </div>
481
- </nav>
482
-
483
- <div *ngIf="authService.hasRole('zenith', 'admin')" class="admin-panel">
484
- Panel de Administración
485
- </div>
332
+ <div *ngIf="auth.revokedError$ | async as error" class="blocked">
333
+ <h2>Acceso bloqueado</h2>
334
+ <p>{{ error.message }}</p>
335
+ </div>
486
336
 
487
- <router-outlet></router-outlet>
337
+ <div *ngIf="(auth.session$ | async) as session">
338
+ <nav>
339
+ <h1>Mi App</h1>
340
+ <span>{{ session.user.name }}</span>
341
+ <button (click)="logout()">Cerrar sesión</button>
342
+ </nav>
343
+ <div *ngIf="auth.hasRole('zenith', 'admin')">Admin Panel</div>
344
+ <router-outlet />
488
345
  </div>
489
346
  `,
490
- styleUrls: ['./app.component.css']
491
347
  })
492
348
  export class AppComponent {
493
- constructor(public authService: AuthService) {}
349
+ constructor(public auth: AuthService) {}
494
350
 
495
351
  logout() {
496
- this.authService.logout();
352
+ this.auth.logout();
497
353
  }
498
354
  }
499
355
  ```
500
356
 
501
357
  ---
502
358
 
503
- ## 🔑 Variables de Entorno por App
359
+ ## Variables de Entorno por App
504
360
 
505
- ### **Zenith (CRM)**
506
- ```env
507
- VITE_APP_DOMAIN=zenith.tgtone.cl
508
- VITE_IDENTITY_URL=https://identity.tgtone.cl
509
- ```
361
+ | App | `VITE_APP_KEY` | `VITE_APP_DOMAIN` |
362
+ |-----|---------------|-------------------|
363
+ | Zenith (CRM) | `zenith` | `zenith.tgtone.cl` |
364
+ | Baco (Wine) | `baco` | `baco.tgtone.cl` |
365
+ | Console (Admin) | `console` | `console.tgtone.cl` |
510
366
 
511
- ### **Baco (Wine Management)**
512
367
  ```env
513
- VITE_APP_DOMAIN=baco.tgtone.cl
368
+ # .env.local (ejemplo para Baco en dev)
514
369
  VITE_IDENTITY_URL=https://identity.tgtone.cl
370
+ VITE_APP_KEY=baco
515
371
  ```
516
372
 
517
- ### **Console (Admin)**
518
- ```env
519
- VITE_APP_DOMAIN=console.tgtone.cl
520
- VITE_IDENTITY_URL=https://identity.tgtone.cl
521
- ```
373
+ > En producción, si el dominio es `baco.tgtone.cl`, el SDK extrae el app key automáticamente y `appKey` no es necesario.
522
374
 
523
375
  ---
524
376
 
525
- ## Checklist de Integración
526
-
527
- - [ ] Copiar `tgtone-auth-client.ts` al proyecto
528
- - [ ] Configurar variables de entorno
529
- - [ ] Crear wrapper/provider según framework
530
- - [ ] Implementar UI de loading
531
- - [ ] Implementar botón de logout
532
- - [ ] Probar flujo de login/logout
533
- - [ ] Probar SSO entre apps
534
- - [ ] Verificar roles y permisos
535
- - [ ] Manejar estados de error
536
- - [ ] Documentar para el equipo
377
+ ## Checklist de Integración
378
+
379
+ - [ ] `npm install @tgtone/auth-sdk`
380
+ - [ ] Configurar variables de entorno (`VITE_IDENTITY_URL`, `VITE_APP_KEY`)
381
+ - [ ] Implementar hook/composable/provider según framework
382
+ - [ ] Manejar estados: `loading`, `session`, `revokedError`
383
+ - [ ] Bloquear renderizado mientras `loading === true` (evita que Router limpie `?token=`)
384
+ - [ ] Configurar interceptor Axios/Fetch para API calls
385
+ - [ ] Probar flujo login/logout
386
+ - [ ] Probar detección de sesión revocada (desactivar usuario en Console)
387
+ - [ ] Verificar roles y permisos por app
388
+
389
+ ---
390
+
391
+ ## Referencia
392
+
393
+ - **[QUICKSTART_REACT.md](./QUICKSTART_REACT.md)** - Quick Start detallado para React
394
+ - **[API.md](./API.md)** - Referencia completa de API
395
+ - **[CHANGELOG.md](../CHANGELOG.md)** - Historial de cambios