0

I am trying to add Firebase Authentication to my Angular app.

Here is the signUp() method in my AuthService:

signUp(email: string, password: string, name: string) {
    const userCredential = from(
      firebase.auth().createUserWithEmailAndPassword(email, password)
        .then(
          (data) => {

            let newUser: firebase.User = data.user;

            newUser.updateProfile({
              displayName: name,
              photoURL: ''
            }).then(() => {
              firebase.firestore().collection('users').add({
                userId: firebase.auth().currentUser.uid,
                userName: firebase.auth().currentUser.displayName,
                created: firebase.firestore.FieldValue.serverTimestamp()
              });
            });
            Plugins.Storage.set({
              key: 'userCredential',
              value: newUser.uid
            });
          }
        )
    );
    return userCredential;
  }

With this method, I'm able to store newUser.uid in local storage using Capacitor's Storage plugin.

But I want to be able to store the same information as what is being stored below (namely localId, email, idToken & expirationTime:

 login(email: string, password: string) {
    return this.http.post<AuthResponseData>(
      `firebaseUrl/v1/accounts:signInWithPassword?key=${
      environment.firebaseAPIKey
      }`,
      { email: email, password: password, returnSecureToken: true }
    ).pipe(tap(this.setUserData.bind(this)));
  }

private setUserData(userData: AuthResponseData) {
    const expirationTime = new Date(
      new Date().getTime() + (+userData.expiresIn * 1000)
    );
    this._user.next(
      new User(
        userData.localId,
        userData.email,
        userData.idToken,
        expirationTime
      )
    );
    this.storeAuthData(userData.localId, userData.idToken, expirationTime.toISOString(), userData.email);
  }

  private storeAuthData(userId: string, token: string, tokenExpirationDate: string, email: string) {
    const data = JSON.stringify({
      userId: userId,
      token: token,
      tokenExpirationDate: tokenExpirationDate,
      email: email
    });
    Plugins.Storage.set({ key: 'authData', value: data });
  }

Can someone please tell me how I can get these 4 values in my signUp() method?

4

2 回答 2

1

According to the docs, .signInWithEmailAndPassword returns a Promise<UserCredential>. This means that there is some kind of result that you can keep in your applications state to determine who the current user is.

Simple example using localStorage:

login(email: string, password: string) {
  return from(firebase.auth().signInWithEmailAndPassword(email, password));
}

After you have called login(..., ...), you can store the success result in localStorage:

localStorage.createItem('auth', userCredential)

Then, in your guard, you can just check what's in localStorage:

canLoad(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    const userCredential = localStorage.getItem('auth');
    // TODO: some validation step
    return !!userCredential; // or the result of your validation
  }

Another way to do it would be to leverage your onAuthStateChanged callback:

firebase.auth().onAuthStateChanged(user => {
      if (user) {
        localStorage.setItem('auth', user);
        console.log('Logged In User:', user);
      } else {
        console.log('Not Logged In');
        this.router.navigateByUrl('login');
      }
});

And then obviously, when a user logs out or something happens that should invalidate the session, you want to remove the item from localStorage:

localStorage.removeItem('auth');

I would recommend adopting something like NgRx or some state management to assist you with managing your user state, but you could get away with local storage if you wanted. As far as security goes, I'm assuming that you are passing in some sort of API key or JWT with every API request to validate the user.

Depending on how performant you want the guard to be, you may also look into the firebase API to see if it has any sort of validation check you can call in place in localstorage.

An example would be that within the UserCredential, you have some sort of auth token. Then, you can call firebase with said auth token which should return a true/false if that is a valid token or not. You could bake that into your guard as a more "direct" way to validate user access. But, this would also degrade the performance of the guard since it has to make an API call each time the guard is used. Local Storage won't present that problem.

于 2020-05-05T20:15:31.770 回答
0

AuthService Login():

login(email: string, password: string) {
    const userCredential = from(firebase.auth().signInWithEmailAndPassword(email, password).then(loggedInUser => {
      Plugins.Storage.set({
        key: 'userCredential',
        value: loggedInUser.user.displayName
      });
    }));
    return userCredential;
  }

Auth Guard:

userCredential;

canLoad(
    route: Route,
    segments: UrlSegment[]): Observable<boolean> | Promise<boolean> | boolean {
    return Plugins.Storage.get({ key: 'userCredential' }).then(userCredential => {
      this.userCredential = userCredential;
      return this.autoLogin();
    });
  }

  autoLogin() {
    if (!this.userCredential || this.userCredential.value === null) {
      this.router.navigateByUrl('login');
      return false;
    } else {
      return true;
    }
  }
于 2020-05-06T18:44:18.457 回答