angular / angularfire

Angular + Firebase = ❤️
https://firebaseopensource.com/projects/angular/angularfire2
MIT License
7.66k stars 2.19k forks source link

sendEmailVerification() - Cannot assign to read only property '_canInitEmulator #3091

Open JakubRoszkowski opened 2 years ago

JakubRoszkowski commented 2 years ago

Hi, I've spent so much time without finding the answer so I assume it could be a bug. After creation of a new user by createUserWithEmailAndPassword() I'm calling current user from AngularFireAuth instance. Then I call sendEmailVerification() which causes an error:

TypeError: Cannot assign to read only property '_canInitEmulator' of object '[object Object]'
    at index-839de510.js:937
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (asyncToGenerator.js:3)
    at _next (asyncToGenerator.js:25)
    at asyncToGenerator.js:32
    at new ZoneAwarePromise (zone.js:1387)
    at asyncToGenerator.js:21
    at _performFetchWithErrorHandling (index-839de510.js:936)
    at index-839de510.js:913
    at Generator.next (<anonymous>)

It happens for angular 12 and 13. Angular: 13.1.0 Firebase: 9.4.0 Angularfire: 7.2.0

google-oss-bot commented 2 years ago

This issue does not seem to follow the issue template. Make sure you provide all the required information.

Sapython commented 2 years ago

give a stackblitz example or share the code repo or at least show all the parts of code related to this issue to create it and debug it.

JakubRoszkowski commented 2 years ago

https://stackblitz.com/edit/angular-ivy-gb7ygv?file=src%2Fapp%2Fapp.component.ts

Unfortunately stackblitz throws an error but you can see the logic. When I handle it like a promise then everything works fine. I wanted to convert it to observable due to NgRx effects' logic but it fails - I got an error mentioned in the first post. In that scenario successful response from addUser() triggers sendEmail().

Sapython commented 2 years ago

Firebase is now modular so it requires initialization for every module.

You have not initialized fireAuth so do it like this. add any of these required lines in your imports code to initialize it.

image

artask commented 2 years ago

I may have the same issue when I try to refresh a user token after updating custom claims in my ngrx effect:

refreshUserToken$ = createEffect( () => this.actions$.pipe(
  ofType( SignupActions.refreshUserToken ),
  withLatestFrom( authState(this.auth) ),
  switchMap( ([action, user]: [Action, User])  => {
    return from(        
      getIdTokenResult(user, true) 
    ).pipe(
      mergeMap( () => [
        SignupActions.refreshUserTokenSuccess(),
        SignupActions.signupRedirect() 
      ]),
      catchError( error => of( SignupActions.refreshUserTokenFailure({ error }) ) )
    );    
  })    
));

The error I'm getting is:

TypeError: Cannot assign to read only property '_canInitEmulator' of object '[object Object]'
    at index-839de510.js:937
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (asyncToGenerator.js:3)
    at _next (asyncToGenerator.js:25)
    at asyncToGenerator.js:32
    at new ZoneAwarePromise (zone.js:1387)
    at asyncToGenerator.js:21
    at _performFetchWithErrorHandling (index-839de510.js:936)
    at index-839de510.js:1448
    at Generator.next (<anonymous>)

@Sapython provideAuth initialization is in the app.module. I tried adding it to the same module as the ngrx effect, but that did not help.

artask commented 2 years ago

@JakubRoszkowski were you able to figure it out?

I think this bug might be somehow related to the fact that sendEmailVerification and getIdTokenResult don't take an Auth obj as a parameter unlike createUserWithEmailAndPassword.

JakubRoszkowski commented 2 years ago

@artask @Sapython provideAuth was added during automatically installation so there's no thing I could change at my code.

dilpazir-whizpool commented 2 years ago

did anyone manage to fix the issue? I am having same issue while calling afAuth.signOut() method

mikalcallahan commented 1 year ago

Just ran into the same issue.

Using Angular 14 with Ionic and NgRx.

After successful createUserWithEmailAndPassword, a signupSuccess action is dispatched which is listened for and dispatches sendEmailVerification. An effect listens for sendEmailVerification and triggers the service call which errors with Cannot assign to read only property '_canInitEmulator

createUserWithEmailAndPassword and sendEmailVerification are both converted to Observables using RxJs.from

@JakubRoszkowski @artask either of you ever figure out a solution?

signout function in ngrx effect with() #3251 seems to be related

MarcinBorkowski03 commented 1 year ago

@mikalcallahan

I didn’t see your code, but probably the error occurs when you pass User object (which you get from calling createUserWithEmailAndPassword) to signupSucces. I had the same problem and I solved it as suggested in this thread: signout function in ngrx effect with() #3251 

So I created an interface for user details like this to avoid using User interface from firebase:

interface UserInfo {
  displayName: string | null;
  email: string | null;
  phoneNumber: string | null;
  photoURL: string | null;
  providerId: string;
  uid: string;
}

I added also User parsing method

const parseUser = ({
  displayName,
  email,
  phoneNumber,
  photoURL,
  providerId,
  uid,
}: User): UserInfo => ({
  displayName,
  email,
  phoneNumber,
  photoURL,
  providerId,
  uid,
});

And finally my signup effects look like this:

signup$ = createEffect(() =>
  this.actions$.pipe(
    ofType(AuthActions.authSignupActions.start_signup),
    switchMap(({ signupCredentials }) =>
      this.authService.signupUser$(signupCredentials).pipe(map(parseUser))
    ),
    map(() => AuthActions.authSignupActions.done_signup({ user }))
  )
);
mikalcallahan commented 1 year ago

@MarcinBorkowski03 that works, thank you!

faridulreza commented 3 months ago

I faced the similar issue. Do not modify/destruct the firebase user object anywhere else in your code.

I had this code to listen for user login:

 const unsubscribe = onAuthStateChanged(getAuth(), (user) => {
      if (user) {
        setUser({
          loginStateChecked: true,
          loggedIn: true,
          ...user  //<<<<<<<<< this should not happend
        });
      } else {
        setUser({
          loginStateChecked: true,
          loggedIn: false,
        });
      }
    });

sendEmailVerification() worked fine once i changed it to

 const unsubscribe = onAuthStateChanged(getAuth(), (user) => {
      if (user) {
        setUser({
          loginStateChecked: true,
          loggedIn: true,
          emailVerified: user.emailVerified //<< i only needed this field
        });
      } else {
        setUser({
          loginStateChecked: true,
          loggedIn: false,
        });
      }
    });