Storing user information in local storage with the Capacitor Storage Plugin: A Comprehensive Guide

I'm attempting to integrate Firebase Authentication into my Angular application.

Here's the signUp() function within 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;
  }

Using this function, I'm able to save newUser.uid in local storage via Capacitor's Storage plugin.

However, I'm looking to store the same details as shown below (specifically localId, email, idToken, and 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 });
  }

Could someone guide me on how to obtain these 4 values in my signUp() function?

Answer №1

As per the documentation, the method .signInWithEmailAndPassword from Firebase Auth returns a Promise<UserCredential>. This enables you to maintain a user's authentication status within your application.

Here is a simple example using localStorage:

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

Upon calling login(..., ...), you can store the successful result in localStorage:

localStorage.createItem('auth', userCredential)

In your guard, you can then check the contents of localStorage:

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

Another approach is to utilize the 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');
      }
});

Remember to remove the item from localStorage when the user logs out or the session is invalidated:

localStorage.removeItem('auth');

Consider using NgRx or another state management tool to handle user state management more effectively. To enhance security, ensure that an API key or JWT is passed with each API request for user validation.

If you require a more performant guard, explore Firebase's API for validation checks that can be used in place of local storage. For instance, authenticating a user using an auth token directly with Firebase could be a more efficient approach, although it would involve making an API call each time the guard is triggered. Local storage, on the other hand, does not pose this latency issue.

Answer №2

Authentication Service - Login Function:

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

Authentication Guard:

userToken;

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

  checkAutoLogin() {
    if (!this.userToken || this.userToken.value === null) {
      this.router.navigateByUrl('login');
      return false;
    } else {
      return true;
    }
  }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The Elusive Glitch: IOS Encounter with Ionic 2

VIEW PROBLEM</p> I am currently developing an Ionic 2 application using Angular 2. Interestingly, I have encountered a peculiar issue that only occurs on one specific page of the app, but specifically on IOS devices. Strangely enough, only the visib ...

Error: global not declared in the context of web3

I've been attempting to integrate Web3 into my Ionic v4 project for some time now. However, I keep encountering errors when I try to serve the project. Specifically, I receive an error message stating that Reference Error: global is not defined. Cre ...

Assign a specific value to the sub-component within the grid using Angular 2+

Incorporating Angular 8 and TypeScript into my project, I have a grid that consists of various internal components, one being <ng-select/>. The data binding takes place in the child component during onInit. Upon loading and initialization of the dat ...

The sequence of operations when assigning in Typescript with || and utilizing the array .find method

I need to ensure that the operations in my assignment are happening in a specific sequence. As far as I can tell, it should be following the order listed below. However, I have not been able to locate any documentation on TypeScript that definitively confi ...

Having trouble setting up mongodb-memory-server 8 to work with jest

I am currently working on integrating the latest version of mongodb-memory-server with jest on a node express server. While following the guide provided in the mongodb-memory-server documentation (), I encountered some gaps that I am struggling to fill in. ...

React and Enzyme are coming up empty-handed when trying to locate any elements within a functional component

After creating a simple functional component in React with Typescript, I encountered an issue while testing it. Every time I try to gather the divs, I keep receiving an empty object {}. Here is how the component is structured: export const TestComponent ...

How can we ensure that Protractor's ElementArrayFinder 'each' function pauses until the current action has finished before moving on to the next iteration?

Currently, I am facing an issue while trying to utilize an 'each' loop in my Angular 8 app's end-to-end tests using protractor. Within my page object, I have created a method that returns an ElementArrayFinder. public getCards(): ElementArr ...

Extract image file name and date information (day, month, year) from an HTML form using Angular

The content of the register.component.ts file can be found below: import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-register', templateUrl: './register.component.html', styleUrls: [&apo ...

The children prop in React Typescript is specified in the props type, but for some reason it is not being

I am currently developing a component library using a combination of React, TypeScript, Styled Components, and Rollup. One of the components I have created is a Button component, defined using a type interface. After rolling up the library and importing th ...

Setting checkbox values using patchValue in Angular programming

I'm facing an issue with reusing my create-form to edit the form values. The checkbox works fine when creating a form, but when I try to edit the form, the values don't get updated on the checkbox. Below is the code snippet that I have been worki ...

What is the reason behind Flow's reluctance to infer the function type from its return value?

I was anticipating the code to undergo type checking within Flow just like it does within TypeScript: var onClick : (() => void) | (() => boolean); onClick = () => { return true; } However, I encountered this error instead: 4: onClick = () => ...

Error encountered when implementing Angular Model Class within an array structure

In the current project, I have developed a class and am attempting to utilize the constructor format for certain content within the project. Here is my Angular class - import { Languages } from './temp-languages.enum'; export class Snippet { ...

How come the value assigned to the [(ngModel)] variable does not show up as the selected value in the PrimeNG drop-down menu?

When passing data from the parent to child component, the normal text input is populated with the data received from the parent, but the same does not work with dropdowns. The code block where data is passed to the variable enteredName functions as intend ...

The specified type argument is not compatible with the ObservableInput<any> type

Struggling with an issue where the argument type (key:string) => Observable | PayloadType | is causing problems when trying to assign it to a parameter of type '(value: string, index: number) => ObersvableInput' return action$.pipe( fil ...

What are some strategies for circumventing the need for two switches?

My LayerEditor class consists of two private methods: export class LayerEditor { public layerManager: LayerManager; constructor() { this.layerManager = new LayerManager(this); } private executeCommand() { ...

What is Prettier's reasoning for suggesting the use of `;` before a destructuring assignment declaration?

I am facing an issue with the if block within my Angular component: if (desc.length > 0) { [this.errorMsg] = desc } The problem arises as Prettier suggests adding a ; at the start of the destructuring assignment: if (desc.length > 0) { ;[thi ...

How can we verify if a React component successfully renders another custom component that we've created?

Consider this scenario: File ComponentA.tsx: const ComponentA = () => { return ( <> <ComponentB/> </> ) } In ComponentA.test.tsx: describe("ComponentA", () => { it("Verifies Compo ...

The exportAs attribute is not specified as "ngForm" for any directive

Encountered an error while attempting to test the LoginComponent PhantomJS 2.1.1 (Linux 0.0.0): Executed 3 of 55 (1 FAILED) (0 secs / 0.307 secs) PhantomJS 2.1.1 (Linux 0.0.0) LoginComponent should create FAILED Failed: Uncaught (in promise): Error: Templ ...

What is the best way to set up Storybook with Vue Cli 3?

I'm facing difficulties installing Storybook in a Vue Cli 3 project. Every time I try to npm run storybook, I encounter this error: Cannot find module '@storybook/vue/dist/server/config/defaults/webpack.config.js' I suspect that this i ...

The validation requirement in Angular prevents the form from being considered valid until the input has been modified

I have included the HTML5 required property in the below HTML code: <div class="col-sm-3"> <label>{{question.placeholder}}<span *ngIf="question.required">*</span></label> </div> <d ...