@allanfsouza/aether-sdk 2.4.6 → 2.4.9

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,333 @@
1
+ // src/tenant-auth.ts
2
+ // [FEATURE] Módulo de Autenticação de Tenants para SDK
3
+ // Permite autenticação de end-users em projetos Aether
4
+ // Compatible with Firebase Auth-style usage
5
+
6
+ import type { AxiosInstance, AxiosError } from "axios";
7
+ import type { PlataformaClient } from "./index.js";
8
+
9
+ // ============================================================================
10
+ // TYPES
11
+ // ============================================================================
12
+
13
+ /**
14
+ * Representa um usuário tenant (end-user de um projeto)
15
+ */
16
+ export interface TenantUser {
17
+ id: string;
18
+ email: string;
19
+ name: string;
20
+ data: Record<string, any>;
21
+ emailVerified: boolean;
22
+ status: "active" | "suspended" | string;
23
+ createdAt: string;
24
+ }
25
+
26
+ /**
27
+ * Resposta de login do tenant
28
+ */
29
+ export interface TenantLoginResponse {
30
+ accessToken: string;
31
+ user: TenantUser;
32
+ }
33
+
34
+ /**
35
+ * Credenciais para registro de tenant
36
+ */
37
+ export interface TenantRegisterCredentials {
38
+ email: string;
39
+ password: string;
40
+ name?: string;
41
+ data?: Record<string, any>;
42
+ }
43
+
44
+ /**
45
+ * Credenciais para login de tenant
46
+ */
47
+ export interface TenantLoginCredentials {
48
+ email: string;
49
+ password: string;
50
+ }
51
+
52
+ // ============================================================================
53
+ // MODULE
54
+ // ============================================================================
55
+
56
+ /**
57
+ * Módulo de Autenticação de Tenants
58
+ *
59
+ * Permite que usuários finais (clientes) de projetos Aether
60
+ * se registrem, façam login e gerenciem seus perfis.
61
+ *
62
+ * Os dados são isolados por projeto (tabela prj_{id}_users).
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * const aether = new PlataformaClient({ ... });
67
+ *
68
+ * // Registrar usuário no projeto
69
+ * const { user, error } = await aether.tenantAuth.signUp('project-id', {
70
+ * email: 'user@example.com',
71
+ * password: '123456',
72
+ * name: 'John Doe',
73
+ * });
74
+ *
75
+ * // Login
76
+ * const { accessToken, user } = await aether.tenantAuth.signIn('project-id', {
77
+ * email: 'user@example.com',
78
+ * password: '123456',
79
+ * });
80
+ *
81
+ * // Obter perfil
82
+ * const profile = await aether.tenantAuth.getProfile('project-id');
83
+ * ```
84
+ */
85
+ export class TenantAuthModule {
86
+ private client: PlataformaClient;
87
+ private http: AxiosInstance;
88
+
89
+ // Armazena o token do tenant atual em memória
90
+ private currentToken: string | null = null;
91
+ private currentUser: TenantUser | null = null;
92
+
93
+ constructor(client: PlataformaClient, http: AxiosInstance) {
94
+ this.client = client;
95
+ this.http = http;
96
+ }
97
+
98
+ // ==========================================================================
99
+ // MÉTODOS PRINCIPAIS
100
+ // ==========================================================================
101
+
102
+ /**
103
+ * Registra um novo usuário tenant no projeto.
104
+ * A tabela de usuários é criada automaticamente se não existir.
105
+ *
106
+ * @param projectId - ID do projeto
107
+ * @param credentials - Email, senha, nome e dados opcionais
108
+ */
109
+ async register(
110
+ projectId: string,
111
+ credentials: TenantRegisterCredentials
112
+ ): Promise<{ user: TenantUser; message: string }> {
113
+ const { data } = await this.http.post("/auth/tenant/register", {
114
+ projectId,
115
+ ...credentials,
116
+ });
117
+ return data;
118
+ }
119
+
120
+ /**
121
+ * Realiza login de usuário tenant.
122
+ * Armazena o token para uso em requisições subsequentes.
123
+ *
124
+ * @param projectId - ID do projeto
125
+ * @param credentials - Email e senha
126
+ */
127
+ async login(
128
+ projectId: string,
129
+ credentials: TenantLoginCredentials
130
+ ): Promise<TenantLoginResponse> {
131
+ const { data } = await this.http.post<TenantLoginResponse>(
132
+ "/auth/tenant/login",
133
+ {
134
+ projectId,
135
+ ...credentials,
136
+ }
137
+ );
138
+
139
+ // Armazena token e usuário
140
+ this.currentToken = data.accessToken;
141
+ this.currentUser = data.user;
142
+
143
+ return data;
144
+ }
145
+
146
+ /**
147
+ * Solicita reset de senha.
148
+ * Retorna mensagem genérica para prevenir enumeração de usuários.
149
+ *
150
+ * @param projectId - ID do projeto
151
+ * @param email - Email do usuário
152
+ */
153
+ async forgotPassword(
154
+ projectId: string,
155
+ email: string
156
+ ): Promise<{ message: string }> {
157
+ const { data } = await this.http.post("/auth/tenant/forgot-password", {
158
+ projectId,
159
+ email,
160
+ });
161
+ return data;
162
+ }
163
+
164
+ /**
165
+ * Redefine a senha usando o token recebido por email.
166
+ *
167
+ * @param projectId - ID do projeto
168
+ * @param email - Email do usuário
169
+ * @param token - Token de reset (recebido por email)
170
+ * @param newPassword - Nova senha
171
+ */
172
+ async resetPassword(
173
+ projectId: string,
174
+ email: string,
175
+ token: string,
176
+ newPassword: string
177
+ ): Promise<{ message: string }> {
178
+ const { data } = await this.http.post("/auth/tenant/reset-password", {
179
+ projectId,
180
+ email,
181
+ token,
182
+ newPassword,
183
+ });
184
+ return data;
185
+ }
186
+
187
+ /**
188
+ * Obtém o perfil do usuário autenticado.
189
+ * Requer que login() tenha sido chamado antes.
190
+ *
191
+ * @param projectId - ID do projeto (opcional, usa do token se não fornecido)
192
+ */
193
+ async getProfile(projectId?: string): Promise<TenantUser> {
194
+ const headers = this.currentToken
195
+ ? { Authorization: `Bearer ${this.currentToken}` }
196
+ : {};
197
+
198
+ const { data } = await this.http.get<{ user: TenantUser }>(
199
+ "/auth/tenant/me",
200
+ { headers }
201
+ );
202
+
203
+ this.currentUser = data.user;
204
+ return data.user;
205
+ }
206
+
207
+ /**
208
+ * Atualiza o perfil do usuário autenticado.
209
+ *
210
+ * @param projectId - ID do projeto
211
+ * @param updates - Campos a atualizar (name, data)
212
+ */
213
+ async updateProfile(
214
+ projectId: string,
215
+ updates: { name?: string; data?: Record<string, any> }
216
+ ): Promise<{ user: TenantUser; message: string }> {
217
+ const headers = this.currentToken
218
+ ? { Authorization: `Bearer ${this.currentToken}` }
219
+ : {};
220
+
221
+ const { data } = await this.http.put(
222
+ "/auth/tenant/profile",
223
+ {
224
+ projectId,
225
+ ...updates,
226
+ },
227
+ { headers }
228
+ );
229
+
230
+ this.currentUser = data.user;
231
+ return data;
232
+ }
233
+
234
+ /**
235
+ * Faz logout do tenant (limpa token local).
236
+ */
237
+ logout(): void {
238
+ this.currentToken = null;
239
+ this.currentUser = null;
240
+ }
241
+
242
+ // ==========================================================================
243
+ // MÉTODOS ESTILO SUPABASE/FIREBASE
244
+ // ==========================================================================
245
+
246
+ /**
247
+ * Alias para register com retorno { user, error }
248
+ */
249
+ async signUp(
250
+ projectId: string,
251
+ credentials: TenantRegisterCredentials
252
+ ): Promise<{ user: TenantUser | null; error: string | null }> {
253
+ try {
254
+ const { user } = await this.register(projectId, credentials);
255
+ return { user, error: null };
256
+ } catch (err: any) {
257
+ const axiosError = err as AxiosError<{ error?: string; message?: string }>;
258
+ return {
259
+ user: null,
260
+ error:
261
+ axiosError.response?.data?.error ||
262
+ axiosError.response?.data?.message ||
263
+ err.message,
264
+ };
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Alias para login com retorno { user, accessToken, error }
270
+ */
271
+ async signIn(
272
+ projectId: string,
273
+ credentials: TenantLoginCredentials
274
+ ): Promise<{
275
+ user: TenantUser | null;
276
+ accessToken: string | null;
277
+ error: string | null;
278
+ }> {
279
+ try {
280
+ const { user, accessToken } = await this.login(projectId, credentials);
281
+ return { user, accessToken, error: null };
282
+ } catch (err: any) {
283
+ const axiosError = err as AxiosError<{ error?: string; message?: string }>;
284
+ return {
285
+ user: null,
286
+ accessToken: null,
287
+ error:
288
+ axiosError.response?.data?.error ||
289
+ axiosError.response?.data?.message ||
290
+ err.message,
291
+ };
292
+ }
293
+ }
294
+
295
+ /**
296
+ * Alias para logout
297
+ */
298
+ signOut(): void {
299
+ this.logout();
300
+ }
301
+
302
+ // ==========================================================================
303
+ // ACCESSORS
304
+ // ==========================================================================
305
+
306
+ /**
307
+ * Retorna o token atual do tenant
308
+ */
309
+ getToken(): string | null {
310
+ return this.currentToken;
311
+ }
312
+
313
+ /**
314
+ * Define o token do tenant (útil para restaurar sessão)
315
+ */
316
+ setToken(token: string | null): void {
317
+ this.currentToken = token;
318
+ }
319
+
320
+ /**
321
+ * Retorna o usuário atual do tenant
322
+ */
323
+ getCurrentUser(): TenantUser | null {
324
+ return this.currentUser;
325
+ }
326
+
327
+ /**
328
+ * Verifica se há um usuário autenticado
329
+ */
330
+ isAuthenticated(): boolean {
331
+ return this.currentToken !== null;
332
+ }
333
+ }