b5-api-client 0.0.25 → 0.0.27
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/P2PMarketplaceAPIClient.d.ts +13 -3
- package/dist/P2PMarketplaceAPIClient.js +91 -59
- package/dist/auth/FirebaseLoginService.d.ts +9 -0
- package/dist/auth/FirebaseLoginService.js +178 -46
- package/dist/auth/LoginService.d.ts +2 -0
- package/dist/types.d.ts +41 -1
- package/package.json +1 -1
- package/src/P2PMarketplaceAPIClient.ts +103 -58
- package/src/auth/FirebaseLoginService.ts +219 -49
- package/src/auth/LoginService.ts +2 -0
- package/src/types.ts +56 -1
|
@@ -14,15 +14,40 @@ import {
|
|
|
14
14
|
isSignInWithEmailLink,
|
|
15
15
|
sendSignInLinkToEmail,
|
|
16
16
|
signInWithEmailLink,
|
|
17
|
-
Auth
|
|
17
|
+
Auth,
|
|
18
|
+
applyActionCode
|
|
18
19
|
} from "firebase/auth";
|
|
19
|
-
import { getMessaging, getToken, onMessage
|
|
20
|
-
import { CreateUserRequest, KioscoinUser } from "../types";
|
|
21
|
-
import P2PMarketplaceAPIClient from "../P2PMarketplaceAPIClient";
|
|
20
|
+
import { getMessaging, getToken, onMessage } from "firebase/messaging";
|
|
21
|
+
import { CreateUserRequest, KioscoinUser, UpdateUserSettingsRequest } from "../types";
|
|
22
|
+
import P2PMarketplaceAPIClient, { AuthTokenProvider } from "../P2PMarketplaceAPIClient";
|
|
22
23
|
import { FirebaseLoginServiceConfig, LoginService } from "./LoginService";
|
|
23
24
|
import { userContext } from './UserContext';
|
|
24
25
|
|
|
25
26
|
|
|
27
|
+
const DEFAULT_EMAIL_VERIFICATION_PAGE =
|
|
28
|
+
process.env.NEXT_PUBLIC_EMAIL_VERIFICATION_URL ??
|
|
29
|
+
"http://localhost:3000/auth/email-verified";
|
|
30
|
+
|
|
31
|
+
const trimTrailingSlash = (value: string): string => value.replace(/\/+$/, "");
|
|
32
|
+
|
|
33
|
+
const appendQueryParams = (
|
|
34
|
+
base: string,
|
|
35
|
+
params: Record<string, string | undefined>
|
|
36
|
+
): string => {
|
|
37
|
+
const query = Object.entries(params)
|
|
38
|
+
.filter(([, value]) => value !== undefined && value !== null)
|
|
39
|
+
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value as string)}`)
|
|
40
|
+
.join("&");
|
|
41
|
+
|
|
42
|
+
if (!query) {
|
|
43
|
+
return base;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const separator = base.includes("?") ? "&" : "?";
|
|
47
|
+
return `${base}${separator}${query}`;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
|
|
26
51
|
// Auth provider type for better type safety
|
|
27
52
|
export enum AuthProvider {
|
|
28
53
|
EMAIL = 'EMAIL',
|
|
@@ -124,18 +149,35 @@ export class FirebaseUnifiedService implements LoginService {
|
|
|
124
149
|
private messaging: ReturnType<typeof getMessaging> | null = null;
|
|
125
150
|
private auth: Auth | null = null;
|
|
126
151
|
private readonly actionCodeSettings: { url: string; handleCodeInApp: boolean };
|
|
152
|
+
private readonly emailVerificationUrl: string;
|
|
153
|
+
private readonly emailVerificationContinueUrl?: string;
|
|
127
154
|
private client: P2PMarketplaceAPIClient;
|
|
155
|
+
private readonly initializationPromise: Promise<void>;
|
|
128
156
|
|
|
129
157
|
constructor(client: P2PMarketplaceAPIClient, config?: FirebaseLoginServiceConfig) {
|
|
130
158
|
this.client = client;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
159
|
+
const fallbackVerificationPage = config?.emailVerificationUrl ?? DEFAULT_EMAIL_VERIFICATION_PAGE;
|
|
160
|
+
this.emailVerificationUrl = trimTrailingSlash(fallbackVerificationPage);
|
|
161
|
+
this.emailVerificationContinueUrl = config?.emailVerificationContinueUrl;
|
|
162
|
+
const configuredActionSettings = config?.actionCodeSettings;
|
|
163
|
+
const defaultActionUrl =
|
|
164
|
+
configuredActionSettings?.url ??
|
|
165
|
+
process.env.REACT_APP_EMAIL_LINK_URL ??
|
|
166
|
+
process.env.NEXT_PUBLIC_EMAIL_LINK_URL ??
|
|
167
|
+
"http://localhost:3000/auth/verify-email";
|
|
168
|
+
|
|
169
|
+
this.actionCodeSettings = {
|
|
170
|
+
url: defaultActionUrl,
|
|
171
|
+
handleCodeInApp: configuredActionSettings?.handleCodeInApp ?? true,
|
|
134
172
|
};
|
|
135
|
-
this.initializeFirebase();
|
|
173
|
+
this.initializationPromise = this.initializeFirebase();
|
|
174
|
+
this.client.setAuthTokenProvider(this.buildAuthTokenProvider());
|
|
136
175
|
}
|
|
137
176
|
|
|
138
177
|
private async initializeFirebase() {
|
|
178
|
+
if (this.app) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
139
181
|
try {
|
|
140
182
|
const firebaseConfig = await this.fetchFirebaseConfig();
|
|
141
183
|
this.app = initializeApp(firebaseConfig);
|
|
@@ -145,6 +187,42 @@ export class FirebaseUnifiedService implements LoginService {
|
|
|
145
187
|
}
|
|
146
188
|
}
|
|
147
189
|
|
|
190
|
+
private async ensureAuthInstance(): Promise<Auth | null> {
|
|
191
|
+
try {
|
|
192
|
+
await this.initializationPromise;
|
|
193
|
+
} catch (_error) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return this.auth;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private buildAuthTokenProvider(): AuthTokenProvider {
|
|
201
|
+
return {
|
|
202
|
+
getToken: async () => {
|
|
203
|
+
const auth = await this.ensureAuthInstance();
|
|
204
|
+
const currentUser = auth?.currentUser;
|
|
205
|
+
if (!currentUser) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
return currentUser.getIdToken();
|
|
209
|
+
},
|
|
210
|
+
refreshToken: async () => {
|
|
211
|
+
const auth = await this.ensureAuthInstance();
|
|
212
|
+
const currentUser = auth?.currentUser;
|
|
213
|
+
if (!currentUser) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
const refreshedToken = await currentUser.getIdToken(true);
|
|
217
|
+
userContext.updateUser({ idToken: refreshedToken });
|
|
218
|
+
return refreshedToken;
|
|
219
|
+
},
|
|
220
|
+
onRefreshFailure: (error) => {
|
|
221
|
+
console.error("Failed to refresh Firebase ID token:", error);
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
148
226
|
private async fetchFirebaseConfig(): Promise<FirebaseConfig> {
|
|
149
227
|
try {
|
|
150
228
|
const response = await this.client.getConfig<{ firebase: FirebaseConfig }>();
|
|
@@ -155,6 +233,63 @@ export class FirebaseUnifiedService implements LoginService {
|
|
|
155
233
|
}
|
|
156
234
|
}
|
|
157
235
|
|
|
236
|
+
private buildEmailVerificationSettings(loginId: string) {
|
|
237
|
+
return {
|
|
238
|
+
url: this.buildEmailVerificationUrl(loginId),
|
|
239
|
+
handleCodeInApp: false,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private buildEmailVerificationUrl(loginId: string): string {
|
|
244
|
+
return appendQueryParams(this.emailVerificationUrl, {
|
|
245
|
+
loginId,
|
|
246
|
+
continueUrl: this.emailVerificationContinueUrl,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private async ensureBackendVerification(
|
|
251
|
+
firebaseUser: User,
|
|
252
|
+
backendUser: KioscoinUser,
|
|
253
|
+
idToken: string
|
|
254
|
+
): Promise<KioscoinUser> {
|
|
255
|
+
const firebaseVerified = firebaseUser.emailVerified;
|
|
256
|
+
const backendVerified = backendUser.settings?.hasVerified ?? false;
|
|
257
|
+
|
|
258
|
+
if (!firebaseVerified || backendVerified) {
|
|
259
|
+
return backendUser;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (!backendUser.id) {
|
|
263
|
+
console.warn("Missing backend user id when attempting to mark verification status.");
|
|
264
|
+
return backendUser;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const settingsUpdate: UpdateUserSettingsRequest["settings"] = {
|
|
268
|
+
paymentMethods: (backendUser.settings?.paymentMethods ?? []).map((method) => ({ ...method })),
|
|
269
|
+
hasVerified: true,
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const preferredToken = backendUser.settings?.preferredTokenCode;
|
|
273
|
+
if (preferredToken !== undefined && preferredToken !== null) {
|
|
274
|
+
settingsUpdate.preferredTokenCode = preferredToken;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const request: UpdateUserSettingsRequest = {
|
|
278
|
+
userId: backendUser.id,
|
|
279
|
+
settings: settingsUpdate,
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
const response = await this.client.updateUserSettings(request, {
|
|
284
|
+
Authorization: `Bearer ${idToken}`,
|
|
285
|
+
});
|
|
286
|
+
return response.users?.[0] ?? backendUser;
|
|
287
|
+
} catch (error) {
|
|
288
|
+
console.error("Failed to update backend verification status:", error);
|
|
289
|
+
return backendUser;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
158
293
|
// Messaging methods
|
|
159
294
|
public async requestNotificationPermission(): Promise<string | null> {
|
|
160
295
|
if (!this.app) {
|
|
@@ -210,23 +345,34 @@ export class FirebaseUnifiedService implements LoginService {
|
|
|
210
345
|
// Authentication methods
|
|
211
346
|
async signInWithEmail(email: string, password: string): Promise<AuthResult> {
|
|
212
347
|
try {
|
|
213
|
-
|
|
348
|
+
const auth = await this.ensureAuthInstance();
|
|
349
|
+
if (!auth) {
|
|
214
350
|
console.error("Firebase Auth not initialized");
|
|
215
351
|
return AuthResult.failure("Firebase Auth not initialized");
|
|
216
352
|
}
|
|
217
|
-
const userCredential = await signInWithEmailAndPassword(
|
|
353
|
+
const userCredential = await signInWithEmailAndPassword(auth, email, password);
|
|
218
354
|
const idToken = await userCredential.user.getIdToken();
|
|
219
355
|
const backendUser = await this.getUserFromBackend(userCredential.user.uid, idToken);
|
|
220
356
|
|
|
221
357
|
if (backendUser) {
|
|
222
|
-
const
|
|
358
|
+
const syncedBackendUser = await this.ensureBackendVerification(
|
|
359
|
+
userCredential.user,
|
|
360
|
+
backendUser,
|
|
361
|
+
idToken
|
|
362
|
+
);
|
|
363
|
+
const authResult = this._createSuccessAuthResult(
|
|
364
|
+
userCredential.user,
|
|
365
|
+
syncedBackendUser,
|
|
366
|
+
AuthProvider.EMAIL,
|
|
367
|
+
idToken
|
|
368
|
+
);
|
|
223
369
|
// Update UserContext
|
|
224
370
|
userContext.setUser({
|
|
225
371
|
id: userCredential.user.uid,
|
|
226
|
-
username:
|
|
372
|
+
username: syncedBackendUser.username || '',
|
|
227
373
|
email: userCredential.user.email || '',
|
|
228
|
-
idToken
|
|
229
|
-
isAdmin:
|
|
374
|
+
idToken,
|
|
375
|
+
isAdmin: syncedBackendUser.admin || false
|
|
230
376
|
});
|
|
231
377
|
return authResult;
|
|
232
378
|
} else {
|
|
@@ -244,16 +390,17 @@ export class FirebaseUnifiedService implements LoginService {
|
|
|
244
390
|
|
|
245
391
|
async createUserWithEmail(email: string, password: string, username: string): Promise<AuthResult> {
|
|
246
392
|
try {
|
|
247
|
-
|
|
393
|
+
const auth = await this.ensureAuthInstance();
|
|
394
|
+
if (!auth) {
|
|
248
395
|
return AuthResult.failure("Firebase Auth not initialized");
|
|
249
396
|
}
|
|
250
397
|
|
|
251
398
|
// First create the user in Firebase
|
|
252
|
-
const userCredential = await createUserWithEmailAndPassword(
|
|
399
|
+
const userCredential = await createUserWithEmailAndPassword(auth, email, password);
|
|
253
400
|
const user = userCredential.user;
|
|
254
401
|
const idToken = await user.getIdToken();
|
|
255
402
|
// Send email verification
|
|
256
|
-
await sendEmailVerification(user);
|
|
403
|
+
await sendEmailVerification(user, this.buildEmailVerificationSettings(user.uid));
|
|
257
404
|
|
|
258
405
|
const backendUser = await this.createUserInBackend(user.uid, username, idToken);
|
|
259
406
|
|
|
@@ -270,20 +417,22 @@ export class FirebaseUnifiedService implements LoginService {
|
|
|
270
417
|
|
|
271
418
|
async signInWithGoogle(): Promise<AuthResult> {
|
|
272
419
|
try {
|
|
273
|
-
|
|
420
|
+
const auth = await this.ensureAuthInstance();
|
|
421
|
+
if (!auth) {
|
|
274
422
|
return AuthResult.failure("Firebase Auth not initialized");
|
|
275
423
|
}
|
|
276
424
|
|
|
277
425
|
const provider = new GoogleAuthProvider();
|
|
278
|
-
const userCredential = await signInWithPopup(
|
|
426
|
+
const userCredential = await signInWithPopup(auth, provider);
|
|
279
427
|
const user = userCredential.user;
|
|
428
|
+
const idToken = await user.getIdToken();
|
|
280
429
|
|
|
281
430
|
// Get or create user in your Kotlin backend
|
|
282
|
-
const backendUser = await this.getUserFromBackend(user.uid,
|
|
283
|
-
const idToken = await user.getIdToken();
|
|
431
|
+
const backendUser = await this.getUserFromBackend(user.uid, idToken);
|
|
284
432
|
if (backendUser) {
|
|
433
|
+
const syncedBackendUser = await this.ensureBackendVerification(user, backendUser, idToken);
|
|
285
434
|
// Use the new helper method
|
|
286
|
-
return this._createSuccessAuthResult(user,
|
|
435
|
+
return this._createSuccessAuthResult(user, syncedBackendUser, AuthProvider.GOOGLE, idToken);
|
|
287
436
|
} else {
|
|
288
437
|
return AuthResult.failure('Failed to get or create user in backend');
|
|
289
438
|
}
|
|
@@ -295,20 +444,22 @@ export class FirebaseUnifiedService implements LoginService {
|
|
|
295
444
|
|
|
296
445
|
async signInWithFacebook(): Promise<AuthResult> {
|
|
297
446
|
try {
|
|
298
|
-
|
|
447
|
+
const auth = await this.ensureAuthInstance();
|
|
448
|
+
if (!auth) {
|
|
299
449
|
return AuthResult.failure("Firebase Auth not initialized");
|
|
300
450
|
}
|
|
301
451
|
|
|
302
452
|
const provider = new FacebookAuthProvider();
|
|
303
|
-
const userCredential = await signInWithPopup(
|
|
453
|
+
const userCredential = await signInWithPopup(auth, provider);
|
|
304
454
|
const user = userCredential.user;
|
|
455
|
+
const idToken = await user.getIdToken();
|
|
305
456
|
|
|
306
457
|
// Get or create user in your Kotlin backend
|
|
307
|
-
const backendUser = await this.getUserFromBackend(user.uid,
|
|
308
|
-
const idToken = await user.getIdToken();
|
|
458
|
+
const backendUser = await this.getUserFromBackend(user.uid, idToken);
|
|
309
459
|
if (backendUser) {
|
|
460
|
+
const syncedBackendUser = await this.ensureBackendVerification(user, backendUser, idToken);
|
|
310
461
|
// Use the new helper method
|
|
311
|
-
return this._createSuccessAuthResult(user,
|
|
462
|
+
return this._createSuccessAuthResult(user, syncedBackendUser, AuthProvider.FACEBOOK, idToken);
|
|
312
463
|
} else {
|
|
313
464
|
return AuthResult.failure('Failed to get or create user in backend');
|
|
314
465
|
}
|
|
@@ -320,20 +471,22 @@ export class FirebaseUnifiedService implements LoginService {
|
|
|
320
471
|
|
|
321
472
|
async signInWithTwitter(): Promise<AuthResult> {
|
|
322
473
|
try {
|
|
323
|
-
|
|
474
|
+
const auth = await this.ensureAuthInstance();
|
|
475
|
+
if (!auth) {
|
|
324
476
|
return AuthResult.failure("Firebase Auth not initialized");
|
|
325
477
|
}
|
|
326
478
|
|
|
327
479
|
const provider = new TwitterAuthProvider();
|
|
328
|
-
const userCredential = await signInWithPopup(
|
|
480
|
+
const userCredential = await signInWithPopup(auth, provider);
|
|
329
481
|
const user = userCredential.user;
|
|
482
|
+
const idToken = await user.getIdToken();
|
|
330
483
|
|
|
331
484
|
// Get or create user in your Kotlin backend
|
|
332
|
-
const backendUser = await this.getUserFromBackend(user.uid,
|
|
333
|
-
const idToken = await user.getIdToken();
|
|
485
|
+
const backendUser = await this.getUserFromBackend(user.uid, idToken);
|
|
334
486
|
if (backendUser) {
|
|
487
|
+
const syncedBackendUser = await this.ensureBackendVerification(user, backendUser, idToken);
|
|
335
488
|
// Use the new helper method
|
|
336
|
-
return this._createSuccessAuthResult(user,
|
|
489
|
+
return this._createSuccessAuthResult(user, syncedBackendUser, AuthProvider.TWITTER, idToken);
|
|
337
490
|
} else {
|
|
338
491
|
return AuthResult.failure('Failed to get or create user in backend');
|
|
339
492
|
}
|
|
@@ -345,13 +498,14 @@ export class FirebaseUnifiedService implements LoginService {
|
|
|
345
498
|
|
|
346
499
|
async sendEmailVerification(): Promise<boolean> {
|
|
347
500
|
try {
|
|
348
|
-
|
|
501
|
+
const auth = await this.ensureAuthInstance();
|
|
502
|
+
if (!auth) {
|
|
349
503
|
return false;
|
|
350
504
|
}
|
|
351
505
|
|
|
352
|
-
const user =
|
|
506
|
+
const user = auth.currentUser;
|
|
353
507
|
if (user) {
|
|
354
|
-
await sendEmailVerification(user);
|
|
508
|
+
await sendEmailVerification(user, this.buildEmailVerificationSettings(user.uid));
|
|
355
509
|
return true;
|
|
356
510
|
}
|
|
357
511
|
return false;
|
|
@@ -363,11 +517,12 @@ export class FirebaseUnifiedService implements LoginService {
|
|
|
363
517
|
|
|
364
518
|
async sendPasswordResetEmail(email: string): Promise<boolean> {
|
|
365
519
|
try {
|
|
366
|
-
|
|
520
|
+
const auth = await this.ensureAuthInstance();
|
|
521
|
+
if (!auth) {
|
|
367
522
|
return false;
|
|
368
523
|
}
|
|
369
524
|
|
|
370
|
-
await sendPasswordResetEmail(
|
|
525
|
+
await sendPasswordResetEmail(auth, email);
|
|
371
526
|
return true;
|
|
372
527
|
} catch (error) {
|
|
373
528
|
console.error('Error sending password reset email:', error);
|
|
@@ -377,11 +532,13 @@ export class FirebaseUnifiedService implements LoginService {
|
|
|
377
532
|
|
|
378
533
|
async signOut(): Promise<void> {
|
|
379
534
|
try {
|
|
380
|
-
|
|
535
|
+
const auth = await this.ensureAuthInstance();
|
|
536
|
+
if (!auth) {
|
|
537
|
+
userContext.clearUser();
|
|
381
538
|
return;
|
|
382
539
|
}
|
|
383
540
|
|
|
384
|
-
await signOut(
|
|
541
|
+
await signOut(auth);
|
|
385
542
|
// Clear UserContext
|
|
386
543
|
userContext.clearUser();
|
|
387
544
|
} catch (error) {
|
|
@@ -398,11 +555,12 @@ export class FirebaseUnifiedService implements LoginService {
|
|
|
398
555
|
|
|
399
556
|
async sendSignInLinkToEmail(email: string): Promise<boolean> {
|
|
400
557
|
try {
|
|
401
|
-
|
|
558
|
+
const auth = await this.ensureAuthInstance();
|
|
559
|
+
if (!auth) {
|
|
402
560
|
return false;
|
|
403
561
|
}
|
|
404
562
|
|
|
405
|
-
await sendSignInLinkToEmail(
|
|
563
|
+
await sendSignInLinkToEmail(auth, email, this.actionCodeSettings);
|
|
406
564
|
// Save the email locally to use it later when the user clicks the link
|
|
407
565
|
window.localStorage.setItem('emailForSignIn', email);
|
|
408
566
|
return true;
|
|
@@ -421,26 +579,29 @@ export class FirebaseUnifiedService implements LoginService {
|
|
|
421
579
|
|
|
422
580
|
async signInWithEmailLink(email: string): Promise<AuthResult> {
|
|
423
581
|
try {
|
|
424
|
-
|
|
582
|
+
const auth = await this.ensureAuthInstance();
|
|
583
|
+
if (!auth) {
|
|
425
584
|
return AuthResult.failure("Firebase Auth not initialized");
|
|
426
585
|
}
|
|
427
586
|
|
|
428
|
-
if (!
|
|
587
|
+
if (!isSignInWithEmailLink(auth, window.location.href)) {
|
|
429
588
|
return AuthResult.failure("Invalid sign-in link");
|
|
430
589
|
}
|
|
431
590
|
|
|
432
|
-
const userCredential = await signInWithEmailLink(
|
|
591
|
+
const userCredential = await signInWithEmailLink(auth, email, window.location.href);
|
|
433
592
|
const user = userCredential.user;
|
|
434
593
|
|
|
435
594
|
// Clear the email from localStorage
|
|
436
595
|
window.localStorage.removeItem('emailForSignIn');
|
|
437
596
|
|
|
438
|
-
// Get or create user in your Kotlin backend
|
|
439
|
-
const backendUser = await this.getUserFromBackend(user.uid, user.displayName || '');
|
|
440
597
|
const idToken = await user.getIdToken();
|
|
598
|
+
|
|
599
|
+
// Get or create user in your Kotlin backend
|
|
600
|
+
const backendUser = await this.getUserFromBackend(user.uid, idToken);
|
|
441
601
|
if (backendUser) {
|
|
602
|
+
const syncedBackendUser = await this.ensureBackendVerification(user, backendUser, idToken);
|
|
442
603
|
// Use the new helper method
|
|
443
|
-
return this._createSuccessAuthResult(user,
|
|
604
|
+
return this._createSuccessAuthResult(user, syncedBackendUser, AuthProvider.EMAIL, idToken);
|
|
444
605
|
} else {
|
|
445
606
|
return AuthResult.failure('Failed to get or create user in backend');
|
|
446
607
|
}
|
|
@@ -454,6 +615,15 @@ export class FirebaseUnifiedService implements LoginService {
|
|
|
454
615
|
return window.localStorage.getItem('emailForSignIn');
|
|
455
616
|
}
|
|
456
617
|
|
|
618
|
+
async applyEmailVerificationCode(oobCode: string): Promise<void> {
|
|
619
|
+
const auth = await this.ensureAuthInstance();
|
|
620
|
+
if (!auth) {
|
|
621
|
+
throw new Error("Firebase Auth not initialized");
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
await applyActionCode(auth, oobCode);
|
|
625
|
+
}
|
|
626
|
+
|
|
457
627
|
// Helper method to create success AuthResult
|
|
458
628
|
private _createSuccessAuthResult(
|
|
459
629
|
user: User,
|
|
@@ -473,10 +643,10 @@ export class FirebaseUnifiedService implements LoginService {
|
|
|
473
643
|
}
|
|
474
644
|
|
|
475
645
|
// Backend integration methods
|
|
476
|
-
private async getUserFromBackend(userId: string,
|
|
646
|
+
private async getUserFromBackend(userId: string, authToken: string): Promise<KioscoinUser | null> {
|
|
477
647
|
try {
|
|
478
648
|
const response = await this.client.getUser(userId, {
|
|
479
|
-
Authorization: `Bearer ${
|
|
649
|
+
Authorization: `Bearer ${authToken}`,
|
|
480
650
|
});
|
|
481
651
|
return response.users[0];
|
|
482
652
|
} catch (error) {
|
package/src/auth/LoginService.ts
CHANGED
|
@@ -21,6 +21,8 @@ export interface FirebaseLoginServiceConfig {
|
|
|
21
21
|
url: string;
|
|
22
22
|
handleCodeInApp: boolean;
|
|
23
23
|
};
|
|
24
|
+
emailVerificationUrl?: string;
|
|
25
|
+
emailVerificationContinueUrl?: string;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
export function createLoginService(config: LoginServiceConfig): LoginService {
|
package/src/types.ts
CHANGED
|
@@ -76,8 +76,9 @@ export interface UsersResponse {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
export interface UserSettings {
|
|
79
|
-
preferredTokenCode?: string;
|
|
79
|
+
preferredTokenCode?: string | null;
|
|
80
80
|
paymentMethods: PaymentMethod[];
|
|
81
|
+
hasVerified?: boolean;
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
export interface UpdateUserSettingsRequest {
|
|
@@ -85,6 +86,10 @@ export interface UpdateUserSettingsRequest {
|
|
|
85
86
|
settings: UserSettings;
|
|
86
87
|
}
|
|
87
88
|
|
|
89
|
+
export interface VerifyEmailRequest {
|
|
90
|
+
loginId: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
88
93
|
export interface PaymentMethod {
|
|
89
94
|
type: string;
|
|
90
95
|
alias?: string;
|
|
@@ -406,3 +411,53 @@ export interface KioscoinOperationResponse {
|
|
|
406
411
|
error?: OperationError;
|
|
407
412
|
order?: Order;
|
|
408
413
|
}
|
|
414
|
+
|
|
415
|
+
export type MessageContentType = 'TEXT' | 'ACTION' | 'EVENT' | 'NOTIFICATION';
|
|
416
|
+
|
|
417
|
+
export interface MessageContentBase {
|
|
418
|
+
type: MessageContentType;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export interface MessageContentText extends MessageContentBase {
|
|
422
|
+
type: 'TEXT';
|
|
423
|
+
text: string;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export interface MessageContentAction extends MessageContentBase {
|
|
427
|
+
type: 'ACTION';
|
|
428
|
+
content: string;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export interface MessageContentEvent extends MessageContentBase {
|
|
432
|
+
type: 'EVENT';
|
|
433
|
+
event: OrderEvent;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export interface MessageContentNotification extends MessageContentBase {
|
|
437
|
+
type: 'NOTIFICATION';
|
|
438
|
+
notification: NotificationDto;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
export type MessageContent =
|
|
442
|
+
| MessageContentText
|
|
443
|
+
| MessageContentAction
|
|
444
|
+
| MessageContentEvent
|
|
445
|
+
| MessageContentNotification;
|
|
446
|
+
|
|
447
|
+
export interface Message {
|
|
448
|
+
sender: string;
|
|
449
|
+
timestamp: number;
|
|
450
|
+
content: MessageContent;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
export interface NotificationDto {
|
|
454
|
+
id: string;
|
|
455
|
+
userId: string;
|
|
456
|
+
message: Message;
|
|
457
|
+
createdAt: string;
|
|
458
|
+
readAt?: string | null;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export interface NotificationsResponse {
|
|
462
|
+
notifications: NotificationDto[];
|
|
463
|
+
}
|