Resetting Firebase Auth Passwords in Angular (Email Based Accounts)
Tags: angular, firebase, beginners, webdev
Published: 2019-09-16 19:04:20

In this post, we are going to cover the following:

1. In Firebase Auth, we will customize:
   - Password Reset Email Content/Message
   - Add a Custom Domain for use by email address
   - And Password Reset/Email Verification URL

2. In Angular:
   - Send Password Reset Request Emails
   - Confirm Password Reset Code and Set New Password
   - Confirm Email Address

## Prerequisite

1. [A Firebase Project](https://codinglatte.com/how-to/how-to-create-a-firebase-project/) – How to create a new Firebase Project.
2. [Create a new Angular App](https://codinglatte.com/posts/how-to/how-to-set-up-angular-for-beginners/) – How to Install and Create a new Angular App.
3. [Install and Setup @angular/fire (AngularFire2)](https://codinglatte.com/posts/how-to/how-to-setup-firebase-for-angular/) – How to Install and Setup Firebase in Angular.

## Firebase Auth

### Enable Email/Password Verification

First, we are going to enable email password authentication.

- In your Firebase project home page, go to Authentication, on the side nav under Develop menu item. And then, select the `sign-in method` tab.
  ![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/wfvt5kuh154bh5dpxj8z.jpg)

- And then, under sign-in providers, under the `Email/Password` provider, hover to reveal the edit icon.
  ![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/dayd7wk5995r6ivg7npc.jpg)

- And finally, click toggle enable switch and save the changes.
  ![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/ugncp6smfjipz89bsk68.jpg)

### Customize Email Address Domain

Next, let’s customize the email domain Firebase uses to send password reset requests and send user email verification requests to our users.

- Under authentication, select the `Templates` tab.
  ![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/9hh80tb78xzsn3nmmpxy.jpg)

- Then, under `Email address Verification`, click on the edit icon next to the `from` email address.
  ![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/6gek8sf7t6lymxnz00zj.jpg)

- And then, click on the `customize domain` link.
  ![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/wzkrwtyvm8gaqjgu467p.jpg)

- After that, you will be prompted for the domain name, enter the name and click next.
  ![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/phge68b87fcepi7qkgkz.jpg)

- Here, you will be prompted to verify your domain name. This is done by adding the DNS records provided by firebase on the verification window to your domains DNS records. This process varies for different domain registrars but it’s also straightforward.

- Once you have added the DNS records, click on the verify button. This can take time to verify the records – up to 48 hours on rare occasions – so don’t panic. Once the verification is done, all email sent by Firebase auth will use your domain name.
![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/k5duypgpw2v9snccrds0.jpg)

### Customize the Password Reset Action URL

Next, let's customize the password reset URL. By default, Firebase provides a preset URL with a generic UI that handles password resets. We are going to change this so that we can point it to our angular app which will handle confirmation and setting of the new password. This will be configured under the `Templates` Tab in Firebase Authentication.

- On the sidebar, select `Password Reset` and scroll to the bottom.
 ![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/flihym1ohikqf1w83104.jpg)

- Click on the `Customize Action URL` link, below the Action URL Text Field and Just Above the Save Button.
  ![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/yv8ik4pn030kcyvs7ylv.jpg)

- A modal window titled Action URL will pop up, prompting you to enter a Custom Action URL. Enter a Custom Action URL of your choice – something like `http://localhost:4200/auth/email/action` and save.
  ![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/0oelm8wzfqf0cpuymh6b.jpg)

> **NB:** Keep in mind that both Password Reset, Email address change and Verification will share the same Action URL. A query parameter `mode`, alongside the `oobCode` parameter is appended to the action URL. The `mode` parameter holds the action type the user is performing i.e. password reset etc.

### Customize Password Reset Message

This is not entirely necessary, but you might want to have a custom message for your app’s users. From the same page as above, you should see the message field, you can customize that by using HTML. Use the following placeholder strings, to inject dynamic data into the template.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/368srk7kdtbthrz27src.jpg)

- %DISPLAY_NAME% - The recipient's display name.

- %APP_NAME% - The name of your app. You can set this value by editing the Public-facing name field on the Settings page.

- %LINK%  - The URL that the recipient must visit to complete the account management task. See Customize the action link URL.

- %EMAIL% - The recipient's email address.

- %NEW_EMAIL% - The new email address to set as the recipient's primary address. *Used only in the Email Address Change template*.

> **NB:** You can also customize the Subject of the email too on the same page.

## Angular Application

Assuming you already have created an Angular App and installed `@angular/fire` – check the prerequisite section above if you haven’t – let’s dive into sending a password reset request.

### Sending Password Resets Requests Component

This component will collect user email addresses and request Firebase to send a password request if the email exists. Firebase authentication servers are going to determine is an Email exists or not.

We are going to start by creating a Reactive Form, with one `FormControl`. First, we are going to inject both `FormBuilder` and `AngularFireAuth` services into our component.

```typescript
constructor(private afAuth: AngularFireAuth, private fb: FormBuilder) {}
```

And then, we are going to declare `frmResetPassword` property of type `FormGroup`, then set it to a new instance of `FormGroup`, with one form field named `email`. The form control is going to have validators for both required and obviously email.

```typescript
frmPasswordReset: FormGroup = this.fb.group({
  email: [null, [Validators.required, Validators.email]]
});
```

Next, let's add a method to be called on from submit, which is going to send a request to Firebase to Send A Password Reset Request to the provided email if it exists.

```typescript
const email = this.frmPasswordReset.controls['email'].value;

this.afAuth.auth.sendPasswordResetEmail(email).then(
  () => {
    // success, show some message
  },
  err => {
    // handle errors
  }
);
```

When the above process completes successfully, an email address is sent to the user with the password reset link.

To handle errors, please check for a code property in the returned error. You can find a list of error codes for Firebase Auth [here](https://firebase.google.com/docs/reference/js/firebase.auth.Error). I have attached a simple Firebase error parser at the bottom of this post.

And finally, here is the template for this component:

```html
<form [formGroup]="frmPasswordReset" (submit)="sendPasswordResetRequest()">
  <div class="field has-text-left">
    <label class="label">Email Address: </label>
    <div class="control has-icons-left">
     <input formControlName="email" class="input is-focused" type="email">
      <span class="icon is-small is-left">
        <fa-icon [icon]="faEnvelope"></fa-icon>
      </span>
    </div>

    <div class="has-text-danger">
      Email is required!
    </div>

    <div class="has-text-danger" >
      A valid email is required!
    </div>
  </div>

  <div class="field">
    <button [disabled]="frmPasswordReset.invalid" class="button is-block is-primary has-text-white is-fullwidth ">
      <fa-icon [icon]="faSigninIcon"></fa-icon>
      Send Request
    </button>
  </div>
</form>
```

### Confirming Email and Setting a New Password for the User

The password reset link and email confirmation/verification link are the same, with the only variants being `oobCode` and `mode`. The mode will determine whether a user is resetting a password or confirming their email address. It can either be `resetPassword` or `verifyEmail`.

This means that these two actions are going to be redirected to the same route in our Angular Application. As such, we are going to need 3 components – One for switching between the modes, one for resetting password and another for confirming/verifying email addresses.

#### Switching Between Reset Password and Verify Email Address

Let’s start with the Component for switching between **Reset Password** and **Confirm Email Address**. We will start by injecting `ActivatedRoute` service into our Component.

```typescript
constructor(private activatedActivated: ActivatedRoute) {}
```

And then, let's declare a property named mode and assign it the value of Query Parameter `mode` from the URL.

```typescript
mode = this.activatedActivated.snapshot.queryParams['mode'];
```

And finally, in our template, we can use the value of the mode property to either display the **Reset Password Component** or the **Verify Email Address Component** using [`ng-switch`](https://angular.io/api/common/NgSwitch).

```html
<ng-container [ngSwitch]="action">
  <!-- password reset -->
  <ng-container *ngSwitchCase="'resetPassword'">
    <app-confirm-password-reset></app-confirm-password-reset>
  </ng-container>

  <!-- verify email address -->
  <ng-container *ngSwitchCase="'verifyEmail'">
    <app-confirm-email-address></app-confirm-email-address>
  </ng-container>

  <!-- default action -->
  <ng-container *ngSwitchDefault>
    <!—show an error message -->
  </ng-container>
</ng-container>
```

#### Confirm Password Reset Code Component

In this component, we will do two things, first is to request the user to provide enter a new password. And then we will use this password and `oobCode` to save the new password. If the `oobCode` is valid, the password will be updated successfully, otherwise, it will fail.

We will start by injecting `FormBuilder`, `ActivatedRoute`, `Router` and AngularFireAuth services into our Component.

```typescript
constructor(private afAuth: AngularFireAuth, private fb: FormBuilder, private route: ActivatedRoute, private router: Router) {}
```

And then we will declare a `FormGroup` property named `frmSetNewPassword`, with new and confirm password form fields:

```typescript
frmSetNewPassword = this.fb.group({
  password: [null, [Validators.required]],
  confirmPassword: [null, [Validators.required]]
});
```

And finally, inside our submit method, we will check if the two passwords match:

```typescript
const password = this.frmSetNewPassword.controls['password'].value;
const confirmPassword = this.frmSetNewPassword.controls['confirmPassword'].value;

if (password !== confirmPassword) {
  // react to error
  return;
}
```

And then get the `oobCode` from the URL, using the `ActivateRoute` service.

```typescript
const code = this.route.snapshot.queryParams['oobCode'];
```

And then finally send call the `confirmPasswordReset`, passing both the code and new password to update the users password.

```typescript
this.afAuth.auth
  .confirmPasswordReset(code, password)
  .then(() => this.router.navigate(['signin']))
  .catch(err => {
   const errorMessage = FirebaseErrors.Parse(err.code); // check this helper class at the bottom
  });
```

If successful, we will redirect the user to the sign-in page where they can sign in with their new password, otherwise we will show the error.

And here is the template for the above form:

```html
<form [formGroup]="frmSetNewPassword" (ngSubmit)="setPassword()">
  <h3 class="title is-5 has-text-black">
    Set a new password
  </h3>
  <h4 class="subtitle is-6 has-text-grey">Please enter and confirm your new password</h4>
  <div class="field">
    <label class="label">Password: </label>
    <div class="control">
      <input class="input " type="password" name="password" formControlName="password">
    </div>
  </div>
  <div class="field">
    <label class="label">Confirm Password: </label>
    <div class="control">
      <input class="input " type="password" name="confirmPassword" formControlName="confirmPassword">
     <div class="has-text-danger" >
        Confirm password is required!
      </div>
    </div>
  </div>

  <div class="field">
    <div class="control">
      <button class="button is-fullwidth is-primary" type="submit" [disabled]="!frmSetNewPassword.valid">
        <span class="icon">
          <fa-icon [icon]="saveIcon"></fa-icon>
        </span>
        <span>Reset</span>
      </button>
    </div>
  </div>
</form>
```

#### Verify Email Address

Verifying email addresses is out of the scope for this post, so I won’t go into details. Here is the code snippet to verify user email address:

```typescript
const code = this.activateRoute.snapshot.queryParams['oobCode'];

this.afAuth.auth
  .applyActionCode(code)
  .then(() => {
    // do something after successful verification
  })
  .catch(err => {
    // show error message
  });
```

Remember to inject both `AngularFireAuth` and `ActivatedRoute` services:

```typescript
constructor(private afAuth: AngularFireAuth, private activateRoute: ActivatedRoute) {}
```

> **NB:** If you don’t require any user action, you can run the above code `OnInit` and show a loading animation.

### Configure Router

And finally, all that is remaining is to configure our Angular Router:

```typescript
const routes: Routes = [
  {
    path: 'auth',
    children: [
      {
        //... Auth Guards For UnAuthenticated Users Here
        children: [
         // ...
          {
            path: 'forgot-password',
            component: PasswordResetRequestComponent,
            data: { title: 'Forgot Password' }
          }
        ]
      },
      {
        path: 'email/action',
        component: EmailConfirmationComponent,
        data: { title: 'Confirm Email Address' }
      }
    ]
  }
];
```

### Bonus - Firebase Auth Errors Parser

Here is a simple helper method for checking errors returned by Firebase Auth:

```typescript
export class FirebaseErrors {
  static Parse(errorCode: string): string {

    let message: string;

    switch (errorCode) {
      case 'auth/wrong-password':
        message = 'Invalid login credentials.';
        break;
      case 'auth/network-request-failed':
        message = 'Please check your internet connection';
        break;
      case 'auth/too-many-requests':
        message =
          'We have detected too many requests from your device. Take a break please!';
        break;
      case 'auth/user-disabled':
        message =
          'Your account has been disabled or deleted. Please contact the system administrator.';
        break;
      case 'auth/requires-recent-login':
        message = 'Please login again and try again!';
        break;
      case 'auth/email-already-exists':
        message = 'Email address is already in use by an existing user.';
        break;
      case 'auth/user-not-found':
        message =
          'We could not find user account associated with the email address or phone number.';
        break;
      case 'auth/phone-number-already-exists':
        message = 'The phone number is already in use by an existing user.';
        break;
      case 'auth/invalid-phone-number':
        message = 'The phone number is not a valid phone number!';
        break;
      case 'auth/invalid-email  ':
        message = 'The email address is not a valid email address!';
        break;
      case 'auth/cannot-delete-own-user-account':
        message = 'You cannot delete your own user account.';
        break;
       default:
        message = 'Oops! Something went wrong. Try again later.';
        break;
    }

    return message;
  }
}
```