apps-sdk 2.1.1 → 2.1.3

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,799 @@
1
+ /**
2
+ * ChatConnect Authentication Service Layer
3
+ * -----------------------------------------
4
+ * Handles all authentication-related API calls including:
5
+ * - Login (auth-user)
6
+ * - Registration (create-user)
7
+ * - User linking
8
+ * - Subscription checking
9
+ *
10
+ * API Base URLs:
11
+ * - Auth: https://bc1742.gways.org/auth/
12
+ * - ChatConnect: https://bc1742.gways.org/chatconnect/
13
+ * - User: https://bc1742.gways.org/user/
14
+ */
15
+
16
+ import Storage from './Storage';
17
+ import Session from './Session';
18
+ import Networking from './Networking';
19
+ import config from '../../config';
20
+
21
+ const BASE_URL = 'https://bc1742.gways.org';
22
+ const AUTH_BASE_URL = `${BASE_URL}/auth`;
23
+ const CHATCONNECT_BASE_URL = `${BASE_URL}/chatconnect`;
24
+ const USER_BASE_URL = `${BASE_URL}/user`;
25
+
26
+ class Authentication {
27
+ constructor() {
28
+ this.AUTH_API_BASE_URL = AUTH_BASE_URL;
29
+ this.USER_API_BASE_URL = USER_BASE_URL;
30
+ this.CHATCONNECT_API_BASE_URL = CHATCONNECT_BASE_URL;
31
+ }
32
+
33
+ /**
34
+ * Get website ID from config
35
+ * @returns {string}
36
+ */
37
+ getWebsiteId() {
38
+ return config.CONFIG_EXTRA?.website_id || 'chatconnect-web';
39
+ }
40
+
41
+ /**
42
+ * Helper to get app user ID from session
43
+ * @returns {string}
44
+ */
45
+ getAppUserId() {
46
+ try {
47
+ const userId = Session?.getUserID?.();
48
+ return userId ?? '';
49
+ } catch {
50
+ return '';
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Generic API call helper with retry logic
56
+ * @param {string} url - API endpoint URL
57
+ * @param {Object} payload - Request payload
58
+ * @param {Object} opts - Options (retries, timeoutMs, silent)
59
+ * @returns {Promise<Object>} API result
60
+ */
61
+ async callApi(url, payload, opts = {}) {
62
+ const { retries = 2, timeoutMs = 15000 } = opts;
63
+
64
+ let attempt = 0;
65
+ let lastError = null;
66
+
67
+ while (attempt <= retries) {
68
+ try {
69
+ const controller = new AbortController();
70
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
71
+
72
+ const response = await Networking.request(url, payload);
73
+ clearTimeout(timeout);
74
+
75
+ // Handle error responses
76
+ if (response?.success === 0 || response?.success === false) {
77
+ return {
78
+ success: false,
79
+ error: response.msg || response.message || response.error || 'Request failed',
80
+ code: response.code,
81
+ raw: response,
82
+ };
83
+ }
84
+
85
+ // Extract data from response
86
+ const data = response?.data ?? response;
87
+ return {
88
+ success: true,
89
+ data: data,
90
+ message: response?.msg || response?.message,
91
+ raw: response,
92
+ };
93
+ } catch (err) {
94
+ lastError = err;
95
+ const isAbort = err?.name === 'AbortError';
96
+ const transient =
97
+ isAbort ||
98
+ (err?.message && /(timeout|network|fetch failed|abort)/i.test(err.message));
99
+
100
+ if (!transient || attempt === retries) {
101
+ config.DEBUG_MODE && console.debug('API call failed:', url, err?.message);
102
+ return {
103
+ success: false,
104
+ error: err?.message || 'Unknown error',
105
+ raw: err,
106
+ };
107
+ }
108
+
109
+ // Backoff before retry
110
+ await new Promise((res) => setTimeout(res, 400 * (attempt + 1)));
111
+ }
112
+ attempt++;
113
+ }
114
+
115
+ config.DEBUG_MODE && console.debug('API call failed after retries:', url, lastError?.message);
116
+ return { success: false, error: lastError?.message || 'Unknown error', raw: lastError };
117
+ }
118
+
119
+ /**
120
+ * Login with credentials (auth-user)
121
+ * POST https://bc1742.gways.org/auth/auth-user
122
+ *
123
+ * @param {Object} payload - Login payload
124
+ * @param {string} payload.login - Email or username (or credential)
125
+ * @param {string} payload.password - Password
126
+ * @param {string} [payload.websiteid] - Optional website ID
127
+ * @returns {Promise<Object>} Login result
128
+ */
129
+ async login(payload) {
130
+ // Handle both old and new payload formats
131
+ const loginValue = 'login' in payload ? payload.login : payload.credential;
132
+
133
+ const requestPayload = {
134
+ login: loginValue,
135
+ password: payload.password,
136
+ websiteid: ('websiteid' in payload ? payload.websiteid : undefined) || this.getWebsiteId(),
137
+ };
138
+
139
+ const url = `${AUTH_BASE_URL}/auth-user`;
140
+ const result = await this.callApi(url, requestPayload);
141
+
142
+ // Check if we got a response but with success: 0 inside data
143
+ if (result.success && result.data && result.data.success === 0) {
144
+ return {
145
+ success: false,
146
+ error: 'user_not_found',
147
+ data: result.data,
148
+ raw: result.raw,
149
+ };
150
+ }
151
+
152
+ // Store user data if login successful
153
+ if (result.success && result.data && result.data.success === 1) {
154
+ try {
155
+ await Storage.storeData('AUTH_USER_ID', result.data.us_id);
156
+ await Storage.storeData('AUTH_USER_LOGIN', result.data.us_login);
157
+ // Store credentials for auto-linking later
158
+ await Storage.storeData('AUTH_CREDENTIALS', JSON.stringify({
159
+ login: loginValue,
160
+ password: payload.password,
161
+ }));
162
+ // Handle metadata (can be array if empty or object if populated)
163
+ if (result.data.metadata && !Array.isArray(result.data.metadata)) {
164
+ await Storage.storeData('AUTH_USER_METADATA', JSON.stringify(result.data.metadata));
165
+ } else {
166
+ await Storage.removeData('AUTH_USER_METADATA');
167
+ }
168
+ if (result.data.subscription_data) {
169
+ await Storage.storeData('AUTH_SUBSCRIPTION', JSON.stringify(result.data.subscription_data));
170
+ }
171
+ } catch (e) {
172
+ console.warn('Failed to store auth data:', e);
173
+ }
174
+ }
175
+
176
+ return result;
177
+ }
178
+
179
+ /**
180
+ * Register a new user account (create-user)
181
+ * POST https://bc1742.gways.org/chatconnect/create-user
182
+ *
183
+ * @param {Object} payload - Registration payload
184
+ * @param {string} payload.login - Email or username (or email field)
185
+ * @param {string} payload.password - Password
186
+ * @param {string} [payload.websiteid] - Optional website ID
187
+ * @returns {Promise<Object>} Registration result
188
+ */
189
+ async register(payload) {
190
+ // Handle both old and new payload formats
191
+ const loginValue = 'login' in payload ? payload.login : payload.email;
192
+
193
+ const requestPayload = {
194
+ login: loginValue,
195
+ password: payload.password,
196
+ websiteid: ('websiteid' in payload ? payload.websiteid : undefined) || this.getWebsiteId(),
197
+ };
198
+
199
+ const url = `${CHATCONNECT_BASE_URL}/create-user`;
200
+ const result = await this.callApi(url, requestPayload);
201
+
202
+ // Store user data and credentials on successful registration
203
+ if (result.success && result.data?.user) {
204
+ const userData = result.data.user;
205
+ try {
206
+ await Storage.storeData('AUTH_USER_ID', String(userData.us_id));
207
+ await Storage.storeData('AUTH_USER_LOGIN', userData.login);
208
+ await Storage.storeData('AUTH_CREDENTIALS', JSON.stringify({
209
+ login: loginValue,
210
+ password: payload.password,
211
+ }));
212
+ } catch (e) {
213
+ console.warn('Failed to store user data:', e);
214
+ }
215
+ }
216
+
217
+ return result;
218
+ }
219
+
220
+ /**
221
+ * Complete registration flow: create-user → link-user
222
+ *
223
+ * @param {Object} payload - Registration payload
224
+ * @param {string} payload.email - Email
225
+ * @param {string} payload.password - Password
226
+ * @returns {Promise<Object>} Complete registration result
227
+ */
228
+ async registerAndLinkAccount(payload) {
229
+ // Step 1: Create user account
230
+ const createResult = await this.register({
231
+ login: payload.email.trim(),
232
+ password: payload.password,
233
+ websiteid: this.getWebsiteId(),
234
+ });
235
+
236
+ if (!createResult.success) {
237
+ if (createResult.code === 112) {
238
+ return {
239
+ success: false,
240
+ error: 'user_already_exists',
241
+ code: 112,
242
+ };
243
+ }
244
+ return {
245
+ success: false,
246
+ error: createResult.error || 'registration_failed',
247
+ code: createResult.code,
248
+ };
249
+ }
250
+
251
+ const userData = createResult.data?.user || (createResult.raw?.data?.user);
252
+
253
+ if (!userData?.us_id) {
254
+ return {
255
+ success: false,
256
+ error: 'registration_failed',
257
+ };
258
+ }
259
+
260
+ // Store the web user credentials
261
+ try {
262
+ await Storage.storeData('WEB_USER_ID', String(userData.us_id));
263
+ await Storage.storeData('WEB_USER_LOGIN', userData.login);
264
+ await Storage.storeData('AUTH_USER_ID', String(userData.us_id));
265
+ await Storage.storeData('AUTH_USER_LOGIN', userData.login);
266
+ } catch (e) {
267
+ console.warn('Failed to store web user data:', e);
268
+ }
269
+
270
+ // Step 2: Link web user with mobile app user
271
+ const appUserId = this.getAppUserId();
272
+
273
+ if (!appUserId) {
274
+ return {
275
+ success: true,
276
+ user: userData,
277
+ linked: false,
278
+ error: 'app_user_id_not_available',
279
+ };
280
+ }
281
+
282
+ const linkResult = await this.linkUser({
283
+ user_id: String(userData.us_id),
284
+ app_user_id: appUserId,
285
+ });
286
+
287
+ if (!linkResult.success) {
288
+ if (linkResult.code === 111) {
289
+ return {
290
+ success: true,
291
+ user: userData,
292
+ linked: false,
293
+ error: 'web_user_not_found',
294
+ code: 111,
295
+ };
296
+ }
297
+ return {
298
+ success: true,
299
+ user: userData,
300
+ linked: false,
301
+ error: linkResult.error || 'link_failed',
302
+ code: linkResult.code,
303
+ };
304
+ }
305
+
306
+ const isLinked = true;
307
+
308
+ // Store linked status
309
+ try {
310
+ await Storage.storeData('ACCOUNT_LINKED', 'true');
311
+ await Storage.storeData('LINKED_WEB_USER_ID', String(userData.us_id));
312
+ } catch (e) {
313
+ console.warn('Failed to store linked status:', e);
314
+ }
315
+
316
+ return {
317
+ success: true,
318
+ user: userData,
319
+ linked: isLinked,
320
+ };
321
+ }
322
+
323
+ /**
324
+ * Link a user account
325
+ * POST https://bc1742.gways.org/user/link
326
+ *
327
+ * @param {Object} payload - Link payload
328
+ * @param {string} payload.user_id - Web user ID
329
+ * @param {string} payload.app_user_id - Mobile app user ID
330
+ * @returns {Promise<Object>} Link result
331
+ */
332
+ async linkUser(payload) {
333
+ const url = `${USER_BASE_URL}/link`;
334
+ const result = await this.callApi(url, payload);
335
+
336
+ if (!result.success) {
337
+ return {
338
+ success: false,
339
+ error: result.error,
340
+ code: result.code,
341
+ raw: result.raw,
342
+ };
343
+ }
344
+
345
+ const rawData = result.data;
346
+
347
+ if (Array.isArray(rawData) && rawData.length > 0) {
348
+ const userData = rawData[0];
349
+
350
+ // Store user data from link response
351
+ try {
352
+ if (userData.us_id) {
353
+ await Storage.storeData('AUTH_USER_ID', String(userData.us_id));
354
+ }
355
+ if (userData.us_login) {
356
+ await Storage.storeData('AUTH_USER_LOGIN', userData.us_login);
357
+ }
358
+ if (userData.metadata && !Array.isArray(userData.metadata)) {
359
+ await Storage.storeData('AUTH_USER_METADATA', JSON.stringify(userData.metadata));
360
+ }
361
+ if (userData.subscription_data) {
362
+ await Storage.storeData('AUTH_SUBSCRIPTION', JSON.stringify(userData.subscription_data));
363
+ }
364
+ } catch (e) {
365
+ console.warn('Failed to store user data from link response:', e);
366
+ }
367
+
368
+ return {
369
+ success: true,
370
+ data: {
371
+ success: userData.success,
372
+ linked: userData.success === 1,
373
+ items: rawData,
374
+ },
375
+ raw: result.raw,
376
+ };
377
+ }
378
+
379
+ // Handle legacy response format
380
+ if (rawData && typeof rawData === 'object' && 'success' in rawData) {
381
+ return {
382
+ success: true,
383
+ data: {
384
+ success: rawData.success ? 1 : 0,
385
+ linked: !!rawData.success,
386
+ },
387
+ raw: result.raw,
388
+ };
389
+ }
390
+
391
+ return {
392
+ success: true,
393
+ data: {
394
+ success: 1,
395
+ linked: true,
396
+ },
397
+ raw: result.raw,
398
+ };
399
+ }
400
+
401
+ /**
402
+ * Fetch authenticated user profile using stored credentials
403
+ * @returns {Promise<Object>} User profile result
404
+ */
405
+ async fetchUserProfile() {
406
+ try {
407
+ const credentialsJson = await Storage.getData('AUTH_CREDENTIALS');
408
+ if (!credentialsJson) {
409
+ return {
410
+ success: false,
411
+ error: 'no_stored_credentials',
412
+ };
413
+ }
414
+
415
+ const credentials = JSON.parse(credentialsJson);
416
+ if (!credentials.login || !credentials.password) {
417
+ return {
418
+ success: false,
419
+ error: 'invalid_stored_credentials',
420
+ };
421
+ }
422
+
423
+ return await this.login({
424
+ login: credentials.login,
425
+ password: credentials.password,
426
+ websiteid: this.getWebsiteId(),
427
+ });
428
+ } catch (e) {
429
+ config.DEBUG_MODE && console.debug('Failed to fetch user profile:', e?.message);
430
+ return {
431
+ success: false,
432
+ error: 'fetch_profile_failed',
433
+ };
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Check if user account is linked
439
+ * @returns {Promise<boolean>}
440
+ */
441
+ async isAccountLinked() {
442
+ try {
443
+ const linked = await Storage.getData('ACCOUNT_LINKED');
444
+ if (linked === 'true') {
445
+ return true;
446
+ }
447
+
448
+ const userId = await Storage.getData('AUTH_USER_ID');
449
+ if (userId) {
450
+ await Storage.storeData('ACCOUNT_LINKED', 'true');
451
+ return true;
452
+ }
453
+
454
+ return false;
455
+ } catch {
456
+ return false;
457
+ }
458
+ }
459
+
460
+ /**
461
+ * Get stored auth user login (email)
462
+ * @returns {Promise<string|null>}
463
+ */
464
+ async getStoredUserLogin() {
465
+ try {
466
+ return await Storage.getData('AUTH_USER_LOGIN');
467
+ } catch {
468
+ return null;
469
+ }
470
+ }
471
+
472
+ /**
473
+ * Check if user is linked
474
+ * POST https://bc1742.gways.org/user/check-link
475
+ *
476
+ * @param {Object} payload - Check link payload
477
+ * @param {string} payload.user_id - User ID
478
+ * @param {string} payload.app_user_id - App user ID
479
+ * @returns {Promise<Object>} Check link result
480
+ */
481
+ async checkLink(payload) {
482
+ const url = `${USER_BASE_URL}/check-link`;
483
+ return this.callApi(url, payload);
484
+ }
485
+
486
+ /**
487
+ * Check user subscription status
488
+ * POST https://bc1742.gways.org/user/check-subs
489
+ *
490
+ * @param {Object} payload - Check subscription payload
491
+ * @param {string} payload.user_id - User ID
492
+ * @param {string} payload.app_user_id - App user ID
493
+ * @returns {Promise<Object>} Subscription status result
494
+ */
495
+ async checkSubscription(payload) {
496
+ const url = `${USER_BASE_URL}/check-subs`;
497
+ return this.callApi(url, payload);
498
+ }
499
+
500
+ /**
501
+ * Check if a credential exists (stub - for backward compatibility)
502
+ * @param {Object} payload - Check credential payload
503
+ * @returns {Promise<Object>} Credential check result
504
+ */
505
+ async checkCredential(payload) {
506
+ return {
507
+ success: true,
508
+ data: {
509
+ exists: true,
510
+ credentialType: payload.credentialType,
511
+ requiresPhone: false,
512
+ },
513
+ };
514
+ }
515
+
516
+ /**
517
+ * Validate phone number (basic client-side validation)
518
+ * @param {Object} payload - Validate phone payload
519
+ * @returns {Promise<Object>} Phone validation result
520
+ */
521
+ async validatePhone(payload) {
522
+ const phone = payload.phoneNumber.replace(/\D/g, '');
523
+ const isValid = phone.length >= 6 && phone.length <= 15;
524
+
525
+ return {
526
+ success: true,
527
+ data: {
528
+ valid: isValid,
529
+ formattedPhone: isValid ? `${payload.countryCode}${phone}` : undefined,
530
+ },
531
+ };
532
+ }
533
+
534
+ /**
535
+ * Request password reset (stub - to be implemented on backend)
536
+ * @param {Object} payload - Password reset request payload
537
+ * @returns {Promise<Object>} Password reset request result
538
+ */
539
+ async requestPasswordReset(payload) {
540
+ return {
541
+ success: true,
542
+ data: {
543
+ sent: true,
544
+ destination: payload.credential,
545
+ expiresIn: 300,
546
+ },
547
+ };
548
+ }
549
+
550
+ /**
551
+ * Verify reset code (stub - to be implemented on backend)
552
+ * @param {Object} payload - Verify code payload
553
+ * @returns {Promise<Object>} Verification result
554
+ */
555
+ async verifyResetCode(payload) {
556
+ const isValid = payload.code.length === 6 && /^\d+$/.test(payload.code);
557
+
558
+ return {
559
+ success: isValid,
560
+ data: isValid ? {
561
+ valid: true,
562
+ resetToken: payload.code,
563
+ } : undefined,
564
+ error: isValid ? undefined : 'Invalid code',
565
+ };
566
+ }
567
+
568
+ /**
569
+ * Reset password (stub - to be implemented on backend)
570
+ * @param {Object} payload - Reset password payload
571
+ * @returns {Promise<Object>} Password reset result
572
+ */
573
+ async resetPassword(payload) {
574
+ return {
575
+ success: true,
576
+ data: {
577
+ success: true,
578
+ message: 'Password reset successfully',
579
+ },
580
+ };
581
+ }
582
+
583
+ /**
584
+ * Resend verification code (stub - to be implemented on backend)
585
+ * @param {Object} payload - Resend code payload
586
+ * @returns {Promise<Object>} Resend code result
587
+ */
588
+ async resendCode(payload) {
589
+ return {
590
+ success: true,
591
+ data: {
592
+ sent: true,
593
+ canResendAt: new Date(Date.now() + 60000).toISOString(),
594
+ },
595
+ };
596
+ }
597
+
598
+ /**
599
+ * Validate QR code data
600
+ * @param {Object} payload - QR code validation payload
601
+ * @param {string} payload.qrData - QR code data
602
+ * @returns {Promise<Object>} QR code validation result
603
+ */
604
+ async validateQrCode(payload) {
605
+ const qrData = payload.qrData.trim();
606
+
607
+ let userId;
608
+
609
+ if (/^\d+$/.test(qrData)) {
610
+ userId = qrData;
611
+ } else {
612
+ try {
613
+ const parsed = JSON.parse(qrData);
614
+ userId = parsed.user_id || parsed.userId || parsed.us_id;
615
+ } catch {
616
+ const match = qrData.match(/(?:user_id|us_id)[=:](\d+)/i);
617
+ if (match) {
618
+ userId = match[1];
619
+ }
620
+ }
621
+ }
622
+
623
+ if (!userId || !/^\d+$/.test(userId)) {
624
+ return {
625
+ success: false,
626
+ data: { valid: false },
627
+ error: 'Invalid QR code format',
628
+ };
629
+ }
630
+
631
+ return {
632
+ success: true,
633
+ data: {
634
+ valid: true,
635
+ user_id: userId,
636
+ },
637
+ };
638
+ }
639
+
640
+ /**
641
+ * Link account using QR code
642
+ * POST https://bc1742.gways.org/user/link
643
+ *
644
+ * @param {Object} payload - QR link payload
645
+ * @param {string} payload.user_id - User ID from QR code
646
+ * @returns {Promise<Object>} QR link result
647
+ */
648
+ async linkWithQrCode(payload) {
649
+ const appUserId = this.getAppUserId();
650
+
651
+ if (!appUserId) {
652
+ return {
653
+ success: false,
654
+ error: 'App user ID not available',
655
+ data: { linked: false },
656
+ };
657
+ }
658
+
659
+ const token = payload.user_id?.trim();
660
+
661
+ if (!token) {
662
+ return {
663
+ success: false,
664
+ error: 'Token not provided',
665
+ data: { linked: false },
666
+ };
667
+ }
668
+
669
+ config.DEBUG_MODE && console.debug('[linkWithQrCode] Linking with token:', token);
670
+
671
+ const result = await this.linkUser({
672
+ user_id: token,
673
+ app_user_id: appUserId,
674
+ });
675
+
676
+ if (result.success) {
677
+ try {
678
+ await Storage.storeData('ACCOUNT_LINKED', 'true');
679
+ await Storage.storeData('LINKED_WEB_USER_ID', token);
680
+
681
+ const userData = result.data?.items?.[0];
682
+ if (userData?.us_id) {
683
+ await Storage.storeData('AUTH_USER_ID', String(userData.us_id));
684
+ } else {
685
+ await Storage.storeData('AUTH_USER_ID', token);
686
+ }
687
+ } catch (e) {
688
+ console.warn('Failed to store linked status:', e);
689
+ }
690
+
691
+ const userData = result.data?.items?.[0];
692
+ const subscriptionActive = userData?.subscription_data?.subscription_active === true;
693
+
694
+ return {
695
+ success: true,
696
+ data: {
697
+ linked: true,
698
+ message: 'Account linked successfully',
699
+ subscriptionActive,
700
+ },
701
+ };
702
+ }
703
+
704
+ // Handle "already linked" as success
705
+ if (result.code === 115) {
706
+ try {
707
+ await Storage.storeData('ACCOUNT_LINKED', 'true');
708
+ await Storage.storeData('AUTH_USER_ID', token);
709
+ await Storage.storeData('LINKED_WEB_USER_ID', token);
710
+ } catch (e) {
711
+ console.warn('Failed to store linked status:', e);
712
+ }
713
+
714
+ return {
715
+ success: true,
716
+ data: {
717
+ linked: true,
718
+ message: 'Account already linked',
719
+ },
720
+ };
721
+ }
722
+
723
+ return {
724
+ success: false,
725
+ error: result.error || 'Failed to link account',
726
+ data: { linked: false },
727
+ };
728
+ }
729
+
730
+ /**
731
+ * Logout current user
732
+ * @returns {Promise<Object>} Logout result
733
+ */
734
+ async logout() {
735
+ try {
736
+ await Storage.removeData('AUTH_USER_ID');
737
+ await Storage.removeData('AUTH_USER_LOGIN');
738
+ await Storage.removeData('AUTH_USER_METADATA');
739
+ await Storage.removeData('AUTH_SUBSCRIPTION');
740
+ } catch (e) {
741
+ console.warn('Failed to clear auth data:', e);
742
+ }
743
+
744
+ return { success: true, data: { success: true } };
745
+ }
746
+
747
+ /**
748
+ * Check if user is currently authenticated
749
+ * @returns {Promise<boolean>}
750
+ */
751
+ async isAuthenticated() {
752
+ try {
753
+ const userId = await Storage.getData('AUTH_USER_ID');
754
+ return !!userId;
755
+ } catch {
756
+ return false;
757
+ }
758
+ }
759
+
760
+ /**
761
+ * Get stored user ID
762
+ * @returns {Promise<string|null>}
763
+ */
764
+ async getStoredUserId() {
765
+ try {
766
+ return await Storage.getData('AUTH_USER_ID');
767
+ } catch {
768
+ return null;
769
+ }
770
+ }
771
+
772
+ /**
773
+ * Get stored user metadata
774
+ * @returns {Promise<Object|null>}
775
+ */
776
+ async getStoredUserMetadata() {
777
+ try {
778
+ const metadata = await Storage.getData('AUTH_USER_METADATA');
779
+ return metadata ? JSON.parse(metadata) : null;
780
+ } catch {
781
+ return null;
782
+ }
783
+ }
784
+
785
+ /**
786
+ * Get stored subscription data
787
+ * @returns {Promise<Object|null>}
788
+ */
789
+ async getStoredSubscription() {
790
+ try {
791
+ const subscription = await Storage.getData('AUTH_SUBSCRIPTION');
792
+ return subscription ? JSON.parse(subscription) : null;
793
+ } catch {
794
+ return null;
795
+ }
796
+ }
797
+ }
798
+
799
+ export default new Authentication();