@blackcode_sa/metaestetics-api 1.14.0 → 1.14.2

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/index.d.mts CHANGED
@@ -8443,7 +8443,7 @@ declare class AuthService extends BaseService {
8443
8443
  * @returns Object containing user, practitioner (if exists), and draft profiles (if any)
8444
8444
  */
8445
8445
  signUpPractitionerWithGoogle(idToken: string): Promise<{
8446
- user: User;
8446
+ user: User | null;
8447
8447
  practitioner: Practitioner | null;
8448
8448
  draftProfiles: Practitioner[];
8449
8449
  }>;
package/dist/index.d.ts CHANGED
@@ -8443,7 +8443,7 @@ declare class AuthService extends BaseService {
8443
8443
  * @returns Object containing user, practitioner (if exists), and draft profiles (if any)
8444
8444
  */
8445
8445
  signUpPractitionerWithGoogle(idToken: string): Promise<{
8446
- user: User;
8446
+ user: User | null;
8447
8447
  practitioner: Practitioner | null;
8448
8448
  draftProfiles: Practitioner[];
8449
8449
  }>;
package/dist/index.js CHANGED
@@ -11308,6 +11308,9 @@ var PractitionerService = class extends BaseService {
11308
11308
  */
11309
11309
  async processBasicInfo(basicInfo, practitionerId) {
11310
11310
  const processedBasicInfo = { ...basicInfo };
11311
+ if (processedBasicInfo.email) {
11312
+ processedBasicInfo.email = processedBasicInfo.email.toLowerCase().trim();
11313
+ }
11311
11314
  if (basicInfo.profileImageUrl) {
11312
11315
  const uploadedUrl = await this.handleProfilePhotoUpload(
11313
11316
  basicInfo.profileImageUrl,
@@ -11731,7 +11734,8 @@ var PractitionerService = class extends BaseService {
11731
11734
  try {
11732
11735
  const normalizedEmail = email.toLowerCase().trim();
11733
11736
  console.log("[PRACTITIONER] Searching for all draft practitioners by email", {
11734
- email: normalizedEmail
11737
+ email: normalizedEmail,
11738
+ originalEmail: email
11735
11739
  });
11736
11740
  const q = (0, import_firestore32.query)(
11737
11741
  (0, import_firestore32.collection)(this.db, PRACTITIONERS_COLLECTION),
@@ -11742,7 +11746,26 @@ var PractitionerService = class extends BaseService {
11742
11746
  const querySnapshot = await (0, import_firestore32.getDocs)(q);
11743
11747
  if (querySnapshot.empty) {
11744
11748
  console.log("[PRACTITIONER] No draft practitioners found for email", {
11745
- email: normalizedEmail
11749
+ email: normalizedEmail,
11750
+ originalEmail: email
11751
+ });
11752
+ const debugQ = (0, import_firestore32.query)(
11753
+ (0, import_firestore32.collection)(this.db, PRACTITIONERS_COLLECTION),
11754
+ (0, import_firestore32.where)("basicInfo.email", "==", normalizedEmail),
11755
+ (0, import_firestore32.limit)(5)
11756
+ );
11757
+ const debugSnapshot = await (0, import_firestore32.getDocs)(debugQ);
11758
+ console.log("[PRACTITIONER] Debug: Found practitioners with this email (any status):", {
11759
+ count: debugSnapshot.size,
11760
+ practitioners: debugSnapshot.docs.map((doc47) => {
11761
+ var _a;
11762
+ return {
11763
+ id: doc47.id,
11764
+ email: (_a = doc47.data().basicInfo) == null ? void 0 : _a.email,
11765
+ status: doc47.data().status,
11766
+ userRef: doc47.data().userRef
11767
+ };
11768
+ })
11746
11769
  });
11747
11770
  return [];
11748
11771
  }
@@ -16029,6 +16052,7 @@ var AuthService = class extends BaseService {
16029
16052
  * @returns Object containing user, practitioner (if exists), and draft profiles (if any)
16030
16053
  */
16031
16054
  async signUpPractitionerWithGoogle(idToken) {
16055
+ var _a, _b;
16032
16056
  try {
16033
16057
  console.log("[AUTH] Starting practitioner Google Sign-In/Sign-Up");
16034
16058
  let email;
@@ -16062,12 +16086,67 @@ var AuthService = class extends BaseService {
16062
16086
  console.log("[AUTH] User exists with Google provider, signing in");
16063
16087
  const credential2 = import_auth8.GoogleAuthProvider.credential(idToken);
16064
16088
  const { user: firebaseUser2 } = await (0, import_auth8.signInWithCredential)(this.auth, credential2);
16065
- const existingUser2 = await this.userService.getUserById(firebaseUser2.uid);
16089
+ let existingUser2 = null;
16090
+ try {
16091
+ existingUser2 = await this.userService.getUserById(firebaseUser2.uid);
16092
+ console.log("[AUTH] User document found:", existingUser2.uid);
16093
+ } catch (userError) {
16094
+ console.log("[AUTH] User document not found in Firestore, checking for draft profiles", {
16095
+ errorCode: userError == null ? void 0 : userError.code,
16096
+ errorMessage: userError == null ? void 0 : userError.message,
16097
+ errorType: (_a = userError == null ? void 0 : userError.constructor) == null ? void 0 : _a.name,
16098
+ isAuthError: userError instanceof AuthError
16099
+ });
16100
+ const practitionerService2 = new PractitionerService(this.db, this.auth, this.app);
16101
+ const draftProfiles3 = await practitionerService2.getDraftProfilesByEmail(normalizedEmail);
16102
+ console.log("[AUTH] Draft profiles check result:", {
16103
+ email: normalizedEmail,
16104
+ draftProfilesCount: draftProfiles3.length,
16105
+ draftProfileIds: draftProfiles3.map((p) => p.id)
16106
+ });
16107
+ if (draftProfiles3.length === 0) {
16108
+ console.log("[AUTH] No draft profiles found, signing out and throwing error");
16109
+ try {
16110
+ await (0, import_auth8.signOut)(this.auth);
16111
+ } catch (signOutError) {
16112
+ console.warn("[AUTH] Error signing out Firebase user (non-critical):", signOutError);
16113
+ }
16114
+ throw new AuthError(
16115
+ "No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.",
16116
+ "AUTH/NO_DRAFT_PROFILES",
16117
+ 404
16118
+ );
16119
+ }
16120
+ console.log("[AUTH] Draft profiles found, attempting to create user document");
16121
+ const draftProfilesFound = await practitionerService2.getDraftProfilesByEmail(normalizedEmail);
16122
+ try {
16123
+ existingUser2 = await this.userService.createUser(firebaseUser2, ["practitioner" /* PRACTITIONER */], {
16124
+ skipProfileCreation: true
16125
+ });
16126
+ console.log("[AUTH] Created user document for existing Firebase user with draft profiles:", existingUser2.uid);
16127
+ } catch (createError) {
16128
+ console.error("[AUTH] Error creating user document:", {
16129
+ error: createError,
16130
+ code: createError == null ? void 0 : createError.code,
16131
+ message: createError == null ? void 0 : createError.message
16132
+ });
16133
+ try {
16134
+ await (0, import_auth8.signOut)(this.auth);
16135
+ } catch (signOutError) {
16136
+ console.warn("[AUTH] Error signing out Firebase user (non-critical):", signOutError);
16137
+ }
16138
+ throw new AuthError(
16139
+ "Unable to create account. Please use the token provided by your clinic to register, or contact support.",
16140
+ "AUTH/USER_CREATION_FAILED",
16141
+ 500
16142
+ );
16143
+ }
16144
+ }
16066
16145
  if (!existingUser2) {
16067
16146
  await (0, import_auth8.signOut)(this.auth);
16068
16147
  throw new AuthError(
16069
- "No account found. Please contact support.",
16070
- "AUTH/USER_NOT_FOUND",
16148
+ "No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.",
16149
+ "AUTH/NO_DRAFT_PROFILES",
16071
16150
  404
16072
16151
  );
16073
16152
  }
@@ -16117,16 +16196,47 @@ var AuthService = class extends BaseService {
16117
16196
  console.error("[AUTH] Error checking for existing user:", error);
16118
16197
  }
16119
16198
  console.log("[AUTH] Checking for draft profiles");
16120
- const draftProfiles = await practitionerService.getDraftProfilesByEmail(normalizedEmail);
16199
+ let draftProfiles = [];
16200
+ try {
16201
+ draftProfiles = await practitionerService.getDraftProfilesByEmail(normalizedEmail);
16202
+ console.log("[AUTH] Draft profiles check complete", { count: draftProfiles.length });
16203
+ } catch (draftCheckError) {
16204
+ console.error("[AUTH] Error checking draft profiles:", draftCheckError);
16205
+ try {
16206
+ await (0, import_auth8.signOut)(this.auth);
16207
+ } catch (signOutError) {
16208
+ console.warn("[AUTH] Error signing out Firebase user (non-critical):", signOutError);
16209
+ }
16210
+ throw new AuthError(
16211
+ "No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.",
16212
+ "AUTH/NO_DRAFT_PROFILES",
16213
+ 404
16214
+ );
16215
+ }
16121
16216
  let user;
16122
16217
  if (existingUser) {
16123
16218
  user = existingUser;
16124
16219
  console.log("[AUTH] Using existing user account");
16125
16220
  } else {
16221
+ if (draftProfiles.length === 0) {
16222
+ console.log("[AUTH] No draft profiles found, signing out and throwing error");
16223
+ try {
16224
+ await (0, import_auth8.signOut)(this.auth);
16225
+ } catch (signOutError) {
16226
+ console.warn("[AUTH] Error signing out Firebase user (non-critical):", signOutError);
16227
+ }
16228
+ const noDraftError = new AuthError(
16229
+ "No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.",
16230
+ "AUTH/NO_DRAFT_PROFILES",
16231
+ 404
16232
+ );
16233
+ console.log("[AUTH] Throwing NO_DRAFT_PROFILES error:", noDraftError.code);
16234
+ throw noDraftError;
16235
+ }
16126
16236
  user = await this.userService.createUser(firebaseUser, ["practitioner" /* PRACTITIONER */], {
16127
16237
  skipProfileCreation: true
16128
16238
  });
16129
- console.log("[AUTH] Created new user account");
16239
+ console.log("[AUTH] Created new user account with draft profiles available");
16130
16240
  }
16131
16241
  let practitioner = null;
16132
16242
  if (user.practitionerProfile) {
@@ -16144,10 +16254,33 @@ var AuthService = class extends BaseService {
16144
16254
  };
16145
16255
  } catch (error) {
16146
16256
  console.error("[AUTH] Error in signUpPractitionerWithGoogle:", error);
16257
+ console.error("[AUTH] Error type:", (_b = error == null ? void 0 : error.constructor) == null ? void 0 : _b.name);
16258
+ console.error("[AUTH] Error instanceof AuthError:", error instanceof AuthError);
16259
+ console.error("[AUTH] Error code:", error == null ? void 0 : error.code);
16260
+ console.error("[AUTH] Error message:", error == null ? void 0 : error.message);
16147
16261
  if (error instanceof AuthError) {
16262
+ console.log("[AUTH] Preserving AuthError:", error.code);
16148
16263
  throw error;
16149
16264
  }
16150
- throw handleFirebaseError(error);
16265
+ const errorMessage = (error == null ? void 0 : error.message) || (error == null ? void 0 : error.toString()) || "";
16266
+ if (errorMessage.includes("NO_DRAFT_PROFILES") || errorMessage.includes("clinic invitation")) {
16267
+ console.log("[AUTH] Detected clinic invitation error in message, converting to AuthError");
16268
+ throw new AuthError(
16269
+ "No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.",
16270
+ "AUTH/NO_DRAFT_PROFILES",
16271
+ 404
16272
+ );
16273
+ }
16274
+ const wrappedError = handleFirebaseError(error);
16275
+ console.log("[AUTH] Wrapped error:", wrappedError.message);
16276
+ if (wrappedError.message.includes("permissions") || wrappedError.message.includes("Account creation failed")) {
16277
+ throw new AuthError(
16278
+ "No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.",
16279
+ "AUTH/NO_DRAFT_PROFILES",
16280
+ 404
16281
+ );
16282
+ }
16283
+ throw wrappedError;
16151
16284
  }
16152
16285
  }
16153
16286
  /**
package/dist/index.mjs CHANGED
@@ -11331,6 +11331,9 @@ var PractitionerService = class extends BaseService {
11331
11331
  */
11332
11332
  async processBasicInfo(basicInfo, practitionerId) {
11333
11333
  const processedBasicInfo = { ...basicInfo };
11334
+ if (processedBasicInfo.email) {
11335
+ processedBasicInfo.email = processedBasicInfo.email.toLowerCase().trim();
11336
+ }
11334
11337
  if (basicInfo.profileImageUrl) {
11335
11338
  const uploadedUrl = await this.handleProfilePhotoUpload(
11336
11339
  basicInfo.profileImageUrl,
@@ -11754,7 +11757,8 @@ var PractitionerService = class extends BaseService {
11754
11757
  try {
11755
11758
  const normalizedEmail = email.toLowerCase().trim();
11756
11759
  console.log("[PRACTITIONER] Searching for all draft practitioners by email", {
11757
- email: normalizedEmail
11760
+ email: normalizedEmail,
11761
+ originalEmail: email
11758
11762
  });
11759
11763
  const q = query13(
11760
11764
  collection13(this.db, PRACTITIONERS_COLLECTION),
@@ -11765,7 +11769,26 @@ var PractitionerService = class extends BaseService {
11765
11769
  const querySnapshot = await getDocs13(q);
11766
11770
  if (querySnapshot.empty) {
11767
11771
  console.log("[PRACTITIONER] No draft practitioners found for email", {
11768
- email: normalizedEmail
11772
+ email: normalizedEmail,
11773
+ originalEmail: email
11774
+ });
11775
+ const debugQ = query13(
11776
+ collection13(this.db, PRACTITIONERS_COLLECTION),
11777
+ where13("basicInfo.email", "==", normalizedEmail),
11778
+ limit7(5)
11779
+ );
11780
+ const debugSnapshot = await getDocs13(debugQ);
11781
+ console.log("[PRACTITIONER] Debug: Found practitioners with this email (any status):", {
11782
+ count: debugSnapshot.size,
11783
+ practitioners: debugSnapshot.docs.map((doc47) => {
11784
+ var _a;
11785
+ return {
11786
+ id: doc47.id,
11787
+ email: (_a = doc47.data().basicInfo) == null ? void 0 : _a.email,
11788
+ status: doc47.data().status,
11789
+ userRef: doc47.data().userRef
11790
+ };
11791
+ })
11769
11792
  });
11770
11793
  return [];
11771
11794
  }
@@ -16116,6 +16139,7 @@ var AuthService = class extends BaseService {
16116
16139
  * @returns Object containing user, practitioner (if exists), and draft profiles (if any)
16117
16140
  */
16118
16141
  async signUpPractitionerWithGoogle(idToken) {
16142
+ var _a, _b;
16119
16143
  try {
16120
16144
  console.log("[AUTH] Starting practitioner Google Sign-In/Sign-Up");
16121
16145
  let email;
@@ -16149,12 +16173,67 @@ var AuthService = class extends BaseService {
16149
16173
  console.log("[AUTH] User exists with Google provider, signing in");
16150
16174
  const credential2 = GoogleAuthProvider.credential(idToken);
16151
16175
  const { user: firebaseUser2 } = await signInWithCredential(this.auth, credential2);
16152
- const existingUser2 = await this.userService.getUserById(firebaseUser2.uid);
16176
+ let existingUser2 = null;
16177
+ try {
16178
+ existingUser2 = await this.userService.getUserById(firebaseUser2.uid);
16179
+ console.log("[AUTH] User document found:", existingUser2.uid);
16180
+ } catch (userError) {
16181
+ console.log("[AUTH] User document not found in Firestore, checking for draft profiles", {
16182
+ errorCode: userError == null ? void 0 : userError.code,
16183
+ errorMessage: userError == null ? void 0 : userError.message,
16184
+ errorType: (_a = userError == null ? void 0 : userError.constructor) == null ? void 0 : _a.name,
16185
+ isAuthError: userError instanceof AuthError
16186
+ });
16187
+ const practitionerService2 = new PractitionerService(this.db, this.auth, this.app);
16188
+ const draftProfiles3 = await practitionerService2.getDraftProfilesByEmail(normalizedEmail);
16189
+ console.log("[AUTH] Draft profiles check result:", {
16190
+ email: normalizedEmail,
16191
+ draftProfilesCount: draftProfiles3.length,
16192
+ draftProfileIds: draftProfiles3.map((p) => p.id)
16193
+ });
16194
+ if (draftProfiles3.length === 0) {
16195
+ console.log("[AUTH] No draft profiles found, signing out and throwing error");
16196
+ try {
16197
+ await firebaseSignOut(this.auth);
16198
+ } catch (signOutError) {
16199
+ console.warn("[AUTH] Error signing out Firebase user (non-critical):", signOutError);
16200
+ }
16201
+ throw new AuthError(
16202
+ "No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.",
16203
+ "AUTH/NO_DRAFT_PROFILES",
16204
+ 404
16205
+ );
16206
+ }
16207
+ console.log("[AUTH] Draft profiles found, attempting to create user document");
16208
+ const draftProfilesFound = await practitionerService2.getDraftProfilesByEmail(normalizedEmail);
16209
+ try {
16210
+ existingUser2 = await this.userService.createUser(firebaseUser2, ["practitioner" /* PRACTITIONER */], {
16211
+ skipProfileCreation: true
16212
+ });
16213
+ console.log("[AUTH] Created user document for existing Firebase user with draft profiles:", existingUser2.uid);
16214
+ } catch (createError) {
16215
+ console.error("[AUTH] Error creating user document:", {
16216
+ error: createError,
16217
+ code: createError == null ? void 0 : createError.code,
16218
+ message: createError == null ? void 0 : createError.message
16219
+ });
16220
+ try {
16221
+ await firebaseSignOut(this.auth);
16222
+ } catch (signOutError) {
16223
+ console.warn("[AUTH] Error signing out Firebase user (non-critical):", signOutError);
16224
+ }
16225
+ throw new AuthError(
16226
+ "Unable to create account. Please use the token provided by your clinic to register, or contact support.",
16227
+ "AUTH/USER_CREATION_FAILED",
16228
+ 500
16229
+ );
16230
+ }
16231
+ }
16153
16232
  if (!existingUser2) {
16154
16233
  await firebaseSignOut(this.auth);
16155
16234
  throw new AuthError(
16156
- "No account found. Please contact support.",
16157
- "AUTH/USER_NOT_FOUND",
16235
+ "No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.",
16236
+ "AUTH/NO_DRAFT_PROFILES",
16158
16237
  404
16159
16238
  );
16160
16239
  }
@@ -16204,16 +16283,47 @@ var AuthService = class extends BaseService {
16204
16283
  console.error("[AUTH] Error checking for existing user:", error);
16205
16284
  }
16206
16285
  console.log("[AUTH] Checking for draft profiles");
16207
- const draftProfiles = await practitionerService.getDraftProfilesByEmail(normalizedEmail);
16286
+ let draftProfiles = [];
16287
+ try {
16288
+ draftProfiles = await practitionerService.getDraftProfilesByEmail(normalizedEmail);
16289
+ console.log("[AUTH] Draft profiles check complete", { count: draftProfiles.length });
16290
+ } catch (draftCheckError) {
16291
+ console.error("[AUTH] Error checking draft profiles:", draftCheckError);
16292
+ try {
16293
+ await firebaseSignOut(this.auth);
16294
+ } catch (signOutError) {
16295
+ console.warn("[AUTH] Error signing out Firebase user (non-critical):", signOutError);
16296
+ }
16297
+ throw new AuthError(
16298
+ "No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.",
16299
+ "AUTH/NO_DRAFT_PROFILES",
16300
+ 404
16301
+ );
16302
+ }
16208
16303
  let user;
16209
16304
  if (existingUser) {
16210
16305
  user = existingUser;
16211
16306
  console.log("[AUTH] Using existing user account");
16212
16307
  } else {
16308
+ if (draftProfiles.length === 0) {
16309
+ console.log("[AUTH] No draft profiles found, signing out and throwing error");
16310
+ try {
16311
+ await firebaseSignOut(this.auth);
16312
+ } catch (signOutError) {
16313
+ console.warn("[AUTH] Error signing out Firebase user (non-critical):", signOutError);
16314
+ }
16315
+ const noDraftError = new AuthError(
16316
+ "No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.",
16317
+ "AUTH/NO_DRAFT_PROFILES",
16318
+ 404
16319
+ );
16320
+ console.log("[AUTH] Throwing NO_DRAFT_PROFILES error:", noDraftError.code);
16321
+ throw noDraftError;
16322
+ }
16213
16323
  user = await this.userService.createUser(firebaseUser, ["practitioner" /* PRACTITIONER */], {
16214
16324
  skipProfileCreation: true
16215
16325
  });
16216
- console.log("[AUTH] Created new user account");
16326
+ console.log("[AUTH] Created new user account with draft profiles available");
16217
16327
  }
16218
16328
  let practitioner = null;
16219
16329
  if (user.practitionerProfile) {
@@ -16231,10 +16341,33 @@ var AuthService = class extends BaseService {
16231
16341
  };
16232
16342
  } catch (error) {
16233
16343
  console.error("[AUTH] Error in signUpPractitionerWithGoogle:", error);
16344
+ console.error("[AUTH] Error type:", (_b = error == null ? void 0 : error.constructor) == null ? void 0 : _b.name);
16345
+ console.error("[AUTH] Error instanceof AuthError:", error instanceof AuthError);
16346
+ console.error("[AUTH] Error code:", error == null ? void 0 : error.code);
16347
+ console.error("[AUTH] Error message:", error == null ? void 0 : error.message);
16234
16348
  if (error instanceof AuthError) {
16349
+ console.log("[AUTH] Preserving AuthError:", error.code);
16235
16350
  throw error;
16236
16351
  }
16237
- throw handleFirebaseError(error);
16352
+ const errorMessage = (error == null ? void 0 : error.message) || (error == null ? void 0 : error.toString()) || "";
16353
+ if (errorMessage.includes("NO_DRAFT_PROFILES") || errorMessage.includes("clinic invitation")) {
16354
+ console.log("[AUTH] Detected clinic invitation error in message, converting to AuthError");
16355
+ throw new AuthError(
16356
+ "No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.",
16357
+ "AUTH/NO_DRAFT_PROFILES",
16358
+ 404
16359
+ );
16360
+ }
16361
+ const wrappedError = handleFirebaseError(error);
16362
+ console.log("[AUTH] Wrapped error:", wrappedError.message);
16363
+ if (wrappedError.message.includes("permissions") || wrappedError.message.includes("Account creation failed")) {
16364
+ throw new AuthError(
16365
+ "No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.",
16366
+ "AUTH/NO_DRAFT_PROFILES",
16367
+ 404
16368
+ );
16369
+ }
16370
+ throw wrappedError;
16238
16371
  }
16239
16372
  }
16240
16373
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.14.0",
4
+ "version": "1.14.2",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.mjs",
@@ -1012,7 +1012,7 @@ export class AuthService extends BaseService {
1012
1012
  async signUpPractitionerWithGoogle(
1013
1013
  idToken: string
1014
1014
  ): Promise<{
1015
- user: User;
1015
+ user: User | null;
1016
1016
  practitioner: Practitioner | null;
1017
1017
  draftProfiles: Practitioner[];
1018
1018
  }> {
@@ -1061,12 +1061,81 @@ export class AuthService extends BaseService {
1061
1061
  const credential = GoogleAuthProvider.credential(idToken);
1062
1062
  const { user: firebaseUser } = await signInWithCredential(this.auth, credential);
1063
1063
 
1064
- const existingUser = await this.userService.getUserById(firebaseUser.uid);
1064
+ let existingUser: User | null = null;
1065
+ try {
1066
+ existingUser = await this.userService.getUserById(firebaseUser.uid);
1067
+ console.log('[AUTH] User document found:', existingUser.uid);
1068
+ } catch (userError: any) {
1069
+ // User document doesn't exist in Firestore
1070
+ console.log('[AUTH] User document not found in Firestore, checking for draft profiles', {
1071
+ errorCode: userError?.code,
1072
+ errorMessage: userError?.message,
1073
+ errorType: userError?.constructor?.name,
1074
+ isAuthError: userError instanceof AuthError,
1075
+ });
1076
+
1077
+ // Check for draft profiles before signing out
1078
+ const practitionerService = new PractitionerService(this.db, this.auth, this.app);
1079
+ const draftProfiles = await practitionerService.getDraftProfilesByEmail(normalizedEmail);
1080
+
1081
+ console.log('[AUTH] Draft profiles check result:', {
1082
+ email: normalizedEmail,
1083
+ draftProfilesCount: draftProfiles.length,
1084
+ draftProfileIds: draftProfiles.map(p => p.id),
1085
+ });
1086
+
1087
+ if (draftProfiles.length === 0) {
1088
+ // No draft profiles - sign out and throw error
1089
+ console.log('[AUTH] No draft profiles found, signing out and throwing error');
1090
+ try {
1091
+ await firebaseSignOut(this.auth);
1092
+ } catch (signOutError) {
1093
+ console.warn('[AUTH] Error signing out Firebase user (non-critical):', signOutError);
1094
+ }
1095
+ throw new AuthError(
1096
+ 'No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.',
1097
+ 'AUTH/NO_DRAFT_PROFILES',
1098
+ 404,
1099
+ );
1100
+ }
1101
+
1102
+ // Draft profiles exist - try to create User document
1103
+ // Firestore rules should allow authenticated users to create their own User document
1104
+ console.log('[AUTH] Draft profiles found, attempting to create user document');
1105
+ const draftProfilesFound = await practitionerService.getDraftProfilesByEmail(normalizedEmail);
1106
+
1107
+ try {
1108
+ existingUser = await this.userService.createUser(firebaseUser, [UserRole.PRACTITIONER], {
1109
+ skipProfileCreation: true,
1110
+ });
1111
+ console.log('[AUTH] Created user document for existing Firebase user with draft profiles:', existingUser.uid);
1112
+ } catch (createError: any) {
1113
+ console.error('[AUTH] Error creating user document:', {
1114
+ error: createError,
1115
+ code: createError?.code,
1116
+ message: createError?.message,
1117
+ });
1118
+ // If creation fails, sign out and throw error
1119
+ // User will need to use token flow or contact support
1120
+ try {
1121
+ await firebaseSignOut(this.auth);
1122
+ } catch (signOutError) {
1123
+ console.warn('[AUTH] Error signing out Firebase user (non-critical):', signOutError);
1124
+ }
1125
+ throw new AuthError(
1126
+ 'Unable to create account. Please use the token provided by your clinic to register, or contact support.',
1127
+ 'AUTH/USER_CREATION_FAILED',
1128
+ 500,
1129
+ );
1130
+ }
1131
+ }
1132
+
1133
+ // User document exists - check for practitioner profile and draft profiles
1065
1134
  if (!existingUser) {
1066
1135
  await firebaseSignOut(this.auth);
1067
1136
  throw new AuthError(
1068
- 'No account found. Please contact support.',
1069
- 'AUTH/USER_NOT_FOUND',
1137
+ 'No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.',
1138
+ 'AUTH/NO_DRAFT_PROFILES',
1070
1139
  404,
1071
1140
  );
1072
1141
  }
@@ -1134,7 +1203,24 @@ export class AuthService extends BaseService {
1134
1203
 
1135
1204
  // Check for draft profiles
1136
1205
  console.log('[AUTH] Checking for draft profiles');
1137
- const draftProfiles = await practitionerService.getDraftProfilesByEmail(normalizedEmail);
1206
+ let draftProfiles: Practitioner[] = [];
1207
+ try {
1208
+ draftProfiles = await practitionerService.getDraftProfilesByEmail(normalizedEmail);
1209
+ console.log('[AUTH] Draft profiles check complete', { count: draftProfiles.length });
1210
+ } catch (draftCheckError: any) {
1211
+ console.error('[AUTH] Error checking draft profiles:', draftCheckError);
1212
+ // If checking draft profiles fails, sign out and throw appropriate error
1213
+ try {
1214
+ await firebaseSignOut(this.auth);
1215
+ } catch (signOutError) {
1216
+ console.warn('[AUTH] Error signing out Firebase user (non-critical):', signOutError);
1217
+ }
1218
+ throw new AuthError(
1219
+ 'No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.',
1220
+ 'AUTH/NO_DRAFT_PROFILES',
1221
+ 404,
1222
+ );
1223
+ }
1138
1224
 
1139
1225
  let user: User;
1140
1226
  if (existingUser) {
@@ -1142,11 +1228,32 @@ export class AuthService extends BaseService {
1142
1228
  user = existingUser;
1143
1229
  console.log('[AUTH] Using existing user account');
1144
1230
  } else {
1145
- // Create new user document
1231
+ // For new users: Only create account if there are draft profiles OR if user already has a practitioner profile
1232
+ // Since doctors can only join via clinic invitation, we should not create accounts without invitations
1233
+ if (draftProfiles.length === 0) {
1234
+ console.log('[AUTH] No draft profiles found, signing out and throwing error');
1235
+ // Sign out the Firebase user since we're not creating an account
1236
+ // Wrap in try-catch to handle any sign-out errors gracefully
1237
+ try {
1238
+ await firebaseSignOut(this.auth);
1239
+ } catch (signOutError) {
1240
+ console.warn('[AUTH] Error signing out Firebase user (non-critical):', signOutError);
1241
+ // Continue anyway - the important part is we're not creating the account
1242
+ }
1243
+ const noDraftError = new AuthError(
1244
+ 'No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.',
1245
+ 'AUTH/NO_DRAFT_PROFILES',
1246
+ 404,
1247
+ );
1248
+ console.log('[AUTH] Throwing NO_DRAFT_PROFILES error:', noDraftError.code);
1249
+ throw noDraftError;
1250
+ }
1251
+
1252
+ // Create new user document only if draft profiles exist
1146
1253
  user = await this.userService.createUser(firebaseUser, [UserRole.PRACTITIONER], {
1147
1254
  skipProfileCreation: true,
1148
1255
  });
1149
- console.log('[AUTH] Created new user account');
1256
+ console.log('[AUTH] Created new user account with draft profiles available');
1150
1257
  }
1151
1258
 
1152
1259
  // Check if user already has practitioner profile
@@ -1166,12 +1273,46 @@ export class AuthService extends BaseService {
1166
1273
  practitioner,
1167
1274
  draftProfiles,
1168
1275
  };
1169
- } catch (error) {
1276
+ } catch (error: any) {
1170
1277
  console.error('[AUTH] Error in signUpPractitionerWithGoogle:', error);
1278
+ console.error('[AUTH] Error type:', error?.constructor?.name);
1279
+ console.error('[AUTH] Error instanceof AuthError:', error instanceof AuthError);
1280
+ console.error('[AUTH] Error code:', error?.code);
1281
+ console.error('[AUTH] Error message:', error?.message);
1282
+
1283
+ // Preserve AuthError instances (like NO_DRAFT_PROFILES) without wrapping
1171
1284
  if (error instanceof AuthError) {
1285
+ console.log('[AUTH] Preserving AuthError:', error.code);
1172
1286
  throw error;
1173
1287
  }
1174
- throw handleFirebaseError(error);
1288
+
1289
+ // Check if error message contains NO_DRAFT_PROFILES before wrapping
1290
+ const errorMessage = error?.message || error?.toString() || '';
1291
+ if (errorMessage.includes('NO_DRAFT_PROFILES') || errorMessage.includes('clinic invitation')) {
1292
+ console.log('[AUTH] Detected clinic invitation error in message, converting to AuthError');
1293
+ throw new AuthError(
1294
+ 'No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.',
1295
+ 'AUTH/NO_DRAFT_PROFILES',
1296
+ 404,
1297
+ );
1298
+ }
1299
+
1300
+ // For other errors, wrap them but preserve the original message if it's helpful
1301
+ const wrappedError = handleFirebaseError(error);
1302
+ console.log('[AUTH] Wrapped error:', wrappedError.message);
1303
+
1304
+ // If the wrapped error message is generic, try to preserve more context
1305
+ if (wrappedError.message.includes('permissions') || wrappedError.message.includes('Account creation failed')) {
1306
+ // This might be a permissions error during sign-out or user creation
1307
+ // If we got here and there were no draft profiles, it's likely the same issue
1308
+ throw new AuthError(
1309
+ 'No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.',
1310
+ 'AUTH/NO_DRAFT_PROFILES',
1311
+ 404,
1312
+ );
1313
+ }
1314
+
1315
+ throw wrappedError;
1175
1316
  }
1176
1317
  }
1177
1318
 
@@ -158,6 +158,11 @@ export class PractitionerService extends BaseService {
158
158
  ): Promise<PractitionerBasicInfo> {
159
159
  const processedBasicInfo = { ...basicInfo };
160
160
 
161
+ // Normalize email to lowercase to ensure consistent matching
162
+ if (processedBasicInfo.email) {
163
+ processedBasicInfo.email = processedBasicInfo.email.toLowerCase().trim();
164
+ }
165
+
161
166
  // Handle profile photo upload if needed
162
167
  if (basicInfo.profileImageUrl) {
163
168
  const uploadedUrl = await this.handleProfilePhotoUpload(
@@ -734,6 +739,7 @@ export class PractitionerService extends BaseService {
734
739
 
735
740
  console.log("[PRACTITIONER] Searching for all draft practitioners by email", {
736
741
  email: normalizedEmail,
742
+ originalEmail: email,
737
743
  });
738
744
 
739
745
  const q = query(
@@ -748,7 +754,26 @@ export class PractitionerService extends BaseService {
748
754
  if (querySnapshot.empty) {
749
755
  console.log("[PRACTITIONER] No draft practitioners found for email", {
750
756
  email: normalizedEmail,
757
+ originalEmail: email,
751
758
  });
759
+
760
+ // Debug: Try to find ANY practitioners with this email (regardless of status)
761
+ const debugQ = query(
762
+ collection(this.db, PRACTITIONERS_COLLECTION),
763
+ where("basicInfo.email", "==", normalizedEmail),
764
+ limit(5)
765
+ );
766
+ const debugSnapshot = await getDocs(debugQ);
767
+ console.log("[PRACTITIONER] Debug: Found practitioners with this email (any status):", {
768
+ count: debugSnapshot.size,
769
+ practitioners: debugSnapshot.docs.map(doc => ({
770
+ id: doc.id,
771
+ email: doc.data().basicInfo?.email,
772
+ status: doc.data().status,
773
+ userRef: doc.data().userRef,
774
+ })),
775
+ });
776
+
752
777
  return [];
753
778
  }
754
779