@tgtone/auth-sdk 1.0.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.
@@ -0,0 +1,536 @@
1
+ # 🔧 Ejemplos de Integración por Framework
2
+
3
+ Guía paso a paso para integrar TGT One Auth en diferentes frameworks.
4
+
5
+ ---
6
+
7
+ ## 📘 React (Vite)
8
+
9
+ ### **1. Copiar archivos**
10
+
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/
16
+ ```
17
+
18
+ ### **2. Crear contexto de autenticación**
19
+
20
+ ```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);
26
+
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
32
+ });
33
+
34
+ return (
35
+ <AuthContext.Provider value={auth}>
36
+ {children}
37
+ </AuthContext.Provider>
38
+ );
39
+ }
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
+ ```
49
+
50
+ ### **3. Envolver tu app**
51
+
52
+ ```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
+ }
84
+
85
+ if (!user) {
86
+ // Ya está redirigiendo al login
87
+ return null;
88
+ }
89
+
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>
105
+
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
+ )}
111
+
112
+ <main>
113
+ {/* Tu contenido aquí */}
114
+ </main>
115
+ </div>
116
+ );
117
+ }
118
+
119
+ export default App;
120
+ ```
121
+
122
+ ---
123
+
124
+ ## 📗 Vue 3 (Vite)
125
+
126
+ ### **1. Copiar archivo**
127
+
128
+ ```bash
129
+ mkdir -p src/lib/auth
130
+ cp tgtone-auth-client.ts src/lib/auth/
131
+ ```
132
+
133
+ ### **2. Crear composable**
134
+
135
+ ```typescript
136
+ // src/composables/useAuth.ts
137
+ import { ref, onMounted } from 'vue';
138
+ import { TGTAuthClient, type TGTUser } from '@/lib/auth/tgtone-auth-client';
139
+
140
+ 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
144
+ });
145
+
146
+ const user = ref<TGTUser | null>(null);
147
+ const loading = ref(true);
148
+
149
+ export function useAuth() {
150
+ onMounted(async () => {
151
+ user.value = await authClient.checkSession();
152
+ loading.value = false;
153
+ });
154
+
155
+ const logout = async () => {
156
+ await authClient.logout();
157
+ user.value = null;
158
+ };
159
+
160
+ const hasRole = (appName: string, roleName: string) => {
161
+ return authClient.hasRole(appName, roleName);
162
+ };
163
+
164
+ return {
165
+ user,
166
+ loading,
167
+ logout,
168
+ hasRole,
169
+ authClient
170
+ };
171
+ }
172
+ ```
173
+
174
+ ### **3. Usar en App.vue**
175
+
176
+ ```vue
177
+ <!-- src/App.vue -->
178
+ <template>
179
+ <div v-if="loading" class="loading">
180
+ <div class="spinner"></div>
181
+ </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>
193
+ </nav>
194
+
195
+ <div v-if="hasRole('baco', 'admin')" class="admin-panel">
196
+ Panel de Administración
197
+ </div>
198
+
199
+ <main>
200
+ <!-- Tu contenido aquí -->
201
+ </main>
202
+ </div>
203
+ </template>
204
+
205
+ <script setup lang="ts">
206
+ import { useAuth } from './composables/useAuth';
207
+
208
+ const { user, loading, logout, hasRole } = useAuth();
209
+ </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
+ ```
234
+
235
+ ---
236
+
237
+ ## 📙 Next.js 14 (App Router)
238
+
239
+ ### **1. Copiar archivo**
240
+
241
+ ```bash
242
+ mkdir -p lib/auth
243
+ cp tgtone-auth-client.ts lib/auth/
244
+ ```
245
+
246
+ ### **2. Crear Provider**
247
+
248
+ ```tsx
249
+ // app/providers/auth-provider.tsx
250
+ 'use client';
251
+
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
+ }
261
+
262
+ const AuthContext = createContext<AuthContextType | null>(null);
263
+
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'
272
+ });
273
+
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
+ );
297
+ }
298
+
299
+ export const useAuth = () => {
300
+ const context = useContext(AuthContext);
301
+ if (!context) {
302
+ throw new Error('useAuth debe usarse dentro de AuthProvider');
303
+ }
304
+ return context;
305
+ };
306
+ ```
307
+
308
+ ### **3. Envolver en layout**
309
+
310
+ ```tsx
311
+ // app/layout.tsx
312
+ import { AuthProvider } from './providers/auth-provider';
313
+ import './globals.css';
314
+
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
+ }) {
325
+ return (
326
+ <html lang="es">
327
+ <body>
328
+ <AuthProvider>
329
+ {children}
330
+ </AuthProvider>
331
+ </body>
332
+ </html>
333
+ );
334
+ }
335
+ ```
336
+
337
+ ### **4. Usar en páginas**
338
+
339
+ ```tsx
340
+ // app/page.tsx
341
+ 'use client';
342
+
343
+ import { useAuth } from './providers/auth-provider';
344
+
345
+ export default function HomePage() {
346
+ const { user, loading, logout, hasRole } = useAuth();
347
+
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
+ }
357
+
358
+ 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>
385
+ </div>
386
+ );
387
+ }
388
+ ```
389
+
390
+ ---
391
+
392
+ ## 📕 Angular 17+
393
+
394
+ ### **1. Copiar archivo**
395
+
396
+ ```bash
397
+ mkdir -p src/app/services/auth
398
+ cp tgtone-auth-client.ts src/app/services/auth/
399
+ ```
400
+
401
+ ### **2. Crear servicio**
402
+
403
+ ```typescript
404
+ // src/app/services/auth/auth.service.ts
405
+ import { Injectable } from '@angular/core';
406
+ import { BehaviorSubject, Observable } from 'rxjs';
407
+ import { TGTAuthClient, TGTUser } from './tgtone-auth-client';
408
+
409
+ @Injectable({
410
+ providedIn: 'root'
411
+ })
412
+ export class AuthService {
413
+ private authClient: TGTAuthClient;
414
+ private userSubject = new BehaviorSubject<TGTUser | null>(null);
415
+ private loadingSubject = new BehaviorSubject<boolean>(true);
416
+
417
+ public user$: Observable<TGTUser | null> = this.userSubject.asObservable();
418
+ public loading$: Observable<boolean> = this.loadingSubject.asObservable();
419
+
420
+ constructor() {
421
+ this.authClient = new TGTAuthClient({
422
+ identityUrl: 'https://identity.tgtone.cl',
423
+ appDomain: 'zenith.tgtone.cl', // Cambiar según tu app
424
+ debug: !environment.production
425
+ });
426
+
427
+ this.checkSession();
428
+ }
429
+
430
+ private async checkSession() {
431
+ try {
432
+ const user = await this.authClient.checkSession();
433
+ this.userSubject.next(user);
434
+ } catch (error) {
435
+ console.error('Error checking session:', error);
436
+ this.userSubject.next(null);
437
+ } finally {
438
+ this.loadingSubject.next(false);
439
+ }
440
+ }
441
+
442
+ async logout() {
443
+ await this.authClient.logout();
444
+ this.userSubject.next(null);
445
+ }
446
+
447
+ hasRole(appName: string, roleName: string): boolean {
448
+ return this.authClient.hasRole(appName, roleName);
449
+ }
450
+
451
+ get currentUser(): TGTUser | null {
452
+ return this.userSubject.value;
453
+ }
454
+ }
455
+ ```
456
+
457
+ ### **3. Usar en componentes**
458
+
459
+ ```typescript
460
+ // src/app/app.component.ts
461
+ import { Component } from '@angular/core';
462
+ import { AuthService } from './services/auth/auth.service';
463
+
464
+ @Component({
465
+ selector: 'app-root',
466
+ template: `
467
+ <div *ngIf="authService.loading$ | async" class="loading">
468
+ <div class="spinner"></div>
469
+ </div>
470
+
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>
486
+
487
+ <router-outlet></router-outlet>
488
+ </div>
489
+ `,
490
+ styleUrls: ['./app.component.css']
491
+ })
492
+ export class AppComponent {
493
+ constructor(public authService: AuthService) {}
494
+
495
+ logout() {
496
+ this.authService.logout();
497
+ }
498
+ }
499
+ ```
500
+
501
+ ---
502
+
503
+ ## 🔑 Variables de Entorno por App
504
+
505
+ ### **Zenith (CRM)**
506
+ ```env
507
+ VITE_APP_DOMAIN=zenith.tgtone.cl
508
+ VITE_IDENTITY_URL=https://identity.tgtone.cl
509
+ ```
510
+
511
+ ### **Baco (Wine Management)**
512
+ ```env
513
+ VITE_APP_DOMAIN=baco.tgtone.cl
514
+ VITE_IDENTITY_URL=https://identity.tgtone.cl
515
+ ```
516
+
517
+ ### **Console (Admin)**
518
+ ```env
519
+ VITE_APP_DOMAIN=console.tgtone.cl
520
+ VITE_IDENTITY_URL=https://identity.tgtone.cl
521
+ ```
522
+
523
+ ---
524
+
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