@edgedev/firebase 1.9.18 → 1.9.19

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/edgeFirebase.ts CHANGED
@@ -47,10 +47,13 @@ import {
47
47
  browserPopupRedirectResolver,
48
48
  signInWithPopup,
49
49
  signInWithPhoneNumber,
50
+ sendSignInLinkToEmail,
50
51
  updateEmail,
51
52
  RecaptchaVerifier,
52
53
  ConfirmationResult,
53
54
  PhoneAuthProvider,
55
+ signInWithCustomToken,
56
+ updateProfile,
54
57
  } from "firebase/auth";
55
58
 
56
59
  import { getFunctions, httpsCallable, connectFunctionsEmulator } from "firebase/functions";
@@ -89,7 +92,7 @@ interface permissions {
89
92
 
90
93
  type action = "assign" | "read" | "write" | "delete";
91
94
 
92
- type authProviders = "email" | "microsoft" | "google" | "facebook" | "github" | "twitter" | "apple" | "phone";
95
+ type authProviders = "email" | "microsoft" | "google" | "facebook" | "github" | "twitter" | "apple" | "phone" | "emailLink" | "customToken";
93
96
 
94
97
  interface role {
95
98
  collectionPath: "-" | string; // - is root
@@ -133,8 +136,10 @@ interface newUser {
133
136
  interface userRegister {
134
137
  uid?: string;
135
138
  email?: string;
139
+ token?: string;
140
+ identifier?: string;
136
141
  phoneCode?: string;
137
- confirmationResult?: ConfirmationResult;
142
+ phoneNumber?: string;
138
143
  password?: string;
139
144
  meta: object;
140
145
  registrationCode: string;
@@ -441,19 +446,15 @@ export const EdgeFirebase = class {
441
446
  });
442
447
  };
443
448
 
444
- public logInWithPhone = async (confirmationResult: ConfirmationResult, phoneCode: string): Promise<void> => {
445
- const oldDiv = document.getElementById("recaptcha-container");
446
- if (oldDiv) oldDiv.remove();
449
+ public loginWithCustomToken = async (token: string): Promise<void> => {
447
450
  try {
448
- console.log(confirmationResult)
449
- const result = await confirmationResult.confirm(phoneCode);
451
+ const result = await signInWithCustomToken(this.auth, token);
450
452
  if (!Object.prototype.hasOwnProperty.call(result, "user")) {
451
453
  this.user.logInError = true;
452
454
  this.user.logInErrorMessage = JSON.stringify(result)
453
455
  this.logOut();
454
456
  return;
455
457
  }
456
- console.log(result.user.uid);
457
458
  const userRef = doc(this.db, "users", result.user.uid);
458
459
  const userSnap = await getDoc(userRef);
459
460
  if (!userSnap.exists()) {
@@ -468,6 +469,39 @@ export const EdgeFirebase = class {
468
469
  this.logOut();
469
470
  return;
470
471
  }
472
+ }
473
+
474
+ public logInWithPhone = async (phoneNumber: string, phoneCode: string): Promise<void> => {
475
+ try {
476
+ const verifyCode: any = await this.runFunction("verifyPhoneNumber", {phone: phoneNumber, code: phoneCode});
477
+ if (verifyCode.data.success) {
478
+ const result = await signInWithCustomToken(this.auth, verifyCode.data.token);
479
+ if (!Object.prototype.hasOwnProperty.call(result, "user")) {
480
+ this.user.logInError = true;
481
+ this.user.logInErrorMessage = JSON.stringify(result)
482
+ this.logOut();
483
+ return;
484
+ }
485
+ const userRef = doc(this.db, "users", result.user.uid);
486
+ const userSnap = await getDoc(userRef);
487
+ if (!userSnap.exists()) {
488
+ this.user.logInError = true;
489
+ this.user.logInErrorMessage = "User does not exist";
490
+ this.logOut();
491
+ return;
492
+ }
493
+ } else {
494
+ this.user.logInError = true;
495
+ this.user.logInErrorMessage = verifyCode.data.error;
496
+ this.logOut();
497
+ return;
498
+ }
499
+ } catch (error) {
500
+ this.user.logInError = true;
501
+ this.user.logInErrorMessage = error.message;
502
+ this.logOut();
503
+ return;
504
+ }
471
505
  };
472
506
 
473
507
  public logInWithMicrosoft = async (providerScopes: string[] = []): Promise<void> => {
@@ -535,24 +569,37 @@ export const EdgeFirebase = class {
535
569
  }
536
570
  }
537
571
 
538
- public sendPhoneCode = async (phone: string): Promise<any> => {
539
- const oldDiv = document.getElementById("recaptcha-container");
540
- if (oldDiv) oldDiv.remove();
541
-
542
- const newDiv = document.createElement("div");
543
- newDiv.setAttribute("id", "recaptcha-container");
544
- document.body.appendChild(newDiv);
545
-
546
- const recaptchaVerifier = new RecaptchaVerifier("recaptcha-container", {
547
- 'size': 'invisible',
548
- 'callback': (response) => {
549
- console.log(response)
550
- }
551
- }, this.auth);
572
+ public sendEmailLink = async (email: string): Promise<any> => {
573
+ const actionCodeSettings = {
574
+ url: window.location.href,
575
+ handleCodeInApp: true,
576
+ };
577
+ try {
578
+ await sendSignInLinkToEmail(this.auth, email, actionCodeSettings);
579
+ return true;
580
+ } catch (error) {
581
+ this.user.logInError = true;
582
+ this.user.logInErrorMessage = error.message;
583
+ this.logOut();
584
+ return false;
585
+ }
586
+ };
552
587
 
588
+ public sendPhoneCode = async (phone: string): Promise<any> => {
553
589
  try {
554
- const confirmationResult = await signInWithPhoneNumber(this.auth, phone, recaptchaVerifier);
555
- return confirmationResult
590
+ const callable = httpsCallable(this.functions, "sendVerificationCode");
591
+ const result: any = await callable({phone: phone});
592
+
593
+ if (result.data.success !== false) {
594
+ console.log('good')
595
+ return result.data;
596
+ } else {
597
+ console.log('bad')
598
+ this.user.logInError = true;
599
+ this.user.logInErrorMessage = result.data.error;
600
+ this.logOut();
601
+ return false;
602
+ }
556
603
  } catch (error) {
557
604
  this.user.logInError = true;
558
605
  this.user.logInErrorMessage = error.message;
@@ -608,17 +655,50 @@ export const EdgeFirebase = class {
608
655
  }
609
656
  } else if (authProvider === "microsoft") {
610
657
  response = await this.registerUserWithMicrosoft(providerScopes);
658
+ } else if (authProvider === "customToken") {
659
+ if (!Object.prototype.hasOwnProperty.call(userRegister, 'token')) {
660
+ return this.sendResponse({
661
+ success: false,
662
+ message: "Token is required for registration when authProvider is customToken.",
663
+ meta: {}
664
+ });
665
+ }
666
+ if (!Object.prototype.hasOwnProperty.call(userRegister, 'identifier')) {
667
+ return this.sendResponse({
668
+ success: false,
669
+ message: "Identifier is required for registration when authProvider is customToken.",
670
+ meta: {}
671
+ });
672
+ }
673
+ try {
674
+ response = await signInWithCustomToken(this.auth, userRegister.token);
675
+ await updateProfile(response.user, {
676
+ displayName: userRegister.identifier
677
+ });
678
+ } catch (error) {
679
+ response = error;
680
+ }
611
681
  } else if (authProvider === "phone") {
612
- const oldDiv = document.getElementById("recaptcha-container");
613
- if (oldDiv) oldDiv.remove();
614
682
  try {
615
- response = await userRegister.confirmationResult.confirm(userRegister.phoneCode);
683
+ const verifyCode: any = await this.runFunction("verifyPhoneNumber", {phone: userRegister.phoneNumber, code: userRegister.phoneCode});
684
+ if (verifyCode.data.success) {
685
+ response = await signInWithCustomToken(this.auth, verifyCode.data.token);
686
+ await updateProfile(response.user, {
687
+ displayName: userRegister.phoneNumber
688
+ });
689
+ } else {
690
+ return this.sendResponse({
691
+ success: false,
692
+ message: verifyCode.data.error,
693
+ meta: {}
694
+ });
695
+ }
616
696
  } catch (error) {
617
697
  // Handle the case where the code is incorrect or expired
618
698
  response = error;
619
699
  }
620
-
621
-
700
+ } else if (authProvider === "emailLink") {
701
+ // TOOD: emailLink stuff
622
702
  }
623
703
  if (!Object.prototype.hasOwnProperty.call(response, "user")) {
624
704
  return this.sendResponse({
@@ -627,14 +707,13 @@ export const EdgeFirebase = class {
627
707
  meta: {}
628
708
  });
629
709
  }
630
-
710
+
631
711
  let metaUpdate = {};
632
712
  if (Object.prototype.hasOwnProperty.call(userRegister, 'meta')) {
633
713
  metaUpdate = userRegister.meta;
634
714
  }else{
635
715
  metaUpdate = user.meta;
636
716
  }
637
-
638
717
  let stagedUserUpdate: {userId?: string, templateUserId?: string, dynamicDocumentFieldValue?: string, uid: string, meta: unknown, templateMeta?: unknown} = {userId: response.user.uid, uid: response.user.uid, meta: metaUpdate}
639
718
  if (user.isTemplate) {
640
719
  stagedUserUpdate = {templateUserId: response.user.uid, uid: response.user.uid, meta: user.meta, templateMeta: metaUpdate}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edgedev/firebase",
3
- "version": "1.9.18",
3
+ "version": "1.9.19",
4
4
  "description": "Vue 3 / Nuxt 3 Plugin or Nuxt 3 plugin for firebase authentication and firestore.",
5
5
  "main": "index.ts",
6
6
  "scripts": {
package/src/functions.js CHANGED
@@ -1,5 +1,87 @@
1
1
  // START @edge/firebase functions
2
2
 
3
+ const twilio = require('twilio')
4
+ const authToken = functions.config().twilio.auth_token
5
+ const accountSid = functions.config().twilio.sid
6
+ const systemNumber = functions.config().twilio.system_number
7
+
8
+ function formatPhoneNumber(phone) {
9
+ // Remove non-numeric characters from the phone number
10
+ const numericPhone = phone.replace(/\D/g, '')
11
+ // Return the formatted number
12
+ return `+1${numericPhone}`
13
+ }
14
+
15
+ exports.sendVerificationCode = functions.https.onCall(async (data, context) => {
16
+ const code = (Math.floor(Math.random() * 1000000) + 1000000).toString().substring(1)
17
+ const phone = formatPhoneNumber(data.phone)
18
+
19
+ try {
20
+ const client = twilio(accountSid, authToken)
21
+ await client.messages.create({
22
+ body: `Your verification code is: ${code}`,
23
+ to: phone, // the user's phone number
24
+ from: systemNumber, // your Twilio phone number from the configuration
25
+ })
26
+ }
27
+ catch (error) {
28
+ console.log(error)
29
+ return { success: false, error: 'Invalid Phone #' }
30
+ }
31
+
32
+ try {
33
+ // Use the formatted phone number as the document ID for Firestore
34
+ await db.collection('phone-auth').doc(phone).set({
35
+ phone,
36
+ code,
37
+ })
38
+ return phone
39
+ }
40
+ catch (error) {
41
+ return { success: false, error }
42
+ }
43
+ })
44
+
45
+ exports.verifyPhoneNumber = functions.https.onCall(async (data, context) => {
46
+ const phone = data.phone
47
+ const code = data.code
48
+
49
+ // Get the phone-auth document with the given phone number
50
+ const phoneDoc = await db.collection('phone-auth').doc(phone).get()
51
+
52
+ if (!phoneDoc.exists) {
53
+ return { success: false, error: 'Phone number not found.' }
54
+ }
55
+
56
+ const storedCode = phoneDoc.data().code
57
+
58
+ if (storedCode !== code) {
59
+ return { success: false, error: 'Invalid verification code.' }
60
+ }
61
+
62
+ // If the code matches, authenticate the user with Firebase Custom Auth
63
+ try {
64
+ // You would typically generate a UID based on the phone number or another system
65
+ const uid = phone
66
+
67
+ // Create a custom token (this can be used on the client to sign in)
68
+ const customToken = await admin.auth().createCustomToken(uid)
69
+
70
+ return { success: true, token: customToken }
71
+ }
72
+ catch (error) {
73
+ console.error('Error creating custom token:', error)
74
+ return { success: false, error: 'Failed to authenticate.' }
75
+ }
76
+ })
77
+
78
+ // // Generate custom token example:
79
+ // exports.generateCustomToken = functions.https.onCall(async (data, context) => {
80
+ // // You would want to have some sort of validation here
81
+ // const token = await admin.auth().createCustomToken(data.customUid)
82
+ // return { token }
83
+ // })
84
+
3
85
  exports.initFirestore = functions.https.onCall(async (data, context) => {
4
86
  // checks to see of the collections 'collection-data' and 'staged-users' exist if not will seed them with data
5
87
  const collectionData = await db.collection('collection-data').get()