How to fix “TypeError: Cannot read property 'g

2020-03-27 17:49发布

问题:

I am using firebase in angular 8 to authenticate the user both in frontend and backend. To authenticate the user in the backend I need to send user Id token.

I am using firebase getIdToken to get the token and it works but partially. The error "TypeError: Cannot read property 'getIdToken' of null" occurs when I refresh the page.

I have tried to hard code the token to the getToken() method and it works even on refresh but that is not feasible, so I made the getToken method to return Observable.

That observable is fetched in Http interceptor TokenInterceptorService, to add the token to all the requests.

export class AuthService {

  constructor(
    public afs: AngularFirestore,   // Inject Firestore service
    public afAuth: AngularFireAuth, // Inject Firebase auth service
    public router: Router,
    public ngZone: NgZone // NgZone service to remove outside scope warning
  ) {}


  // Other authentication methods for sign up etc.
  // removed here for readability


  getToken(): Observable<string> {

    // getIdToken() returns promise so using 'from' to 
    // convert it to an Observable
    const result = from(this.afAuth.auth.currentUser.getIdToken()
      .then( token => {
          console.log(token);
          return token;
      })
    );

    return result;
  }


}
export class TokenInterceptorService implements HttpInterceptor {


  constructor(private authService: AuthService) { }


  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    return this.authService.getToken()
      .pipe(
        switchMap(token => {

          const newRequest = request.clone({
            setHeaders: {Authorization: `JWT ${token}`}
          });

          return next.handle(newRequest);

        })
      );

 }

}

I have seen similar questions and I am using that solution with some modification.

I have even tried returning promise from getToken() method but that too doesn't work.

回答1:

getting the currentUser is asynchronous, so it would be expected that initially it will be null. Like mentioned in documentation:

Note: currentUser might also be null because the auth object has not finished initializing.

Instead you should be using authState to get the current user. Then from the user you get from there, you can call getIdToken(), which returns a promise. You can use from there to convert it to an observable:

getToken(): Observable<string | null> {
  return this.afAuth.authState.pipe(
    take(1),
    switchMap((user) => {
      if (user) {
        return from(user.getIdToken())
      }
      return of(null);
    })
  )
}

Then in your interceptor, also do a check if token exists. If there is no user, it will return null