AWS Amplify Auth & Angular RxJS Simple State Management
Tags: angular, rxjs, awsamplify
Published: 2019-04-02 15:54:25

---
title: AWS Amplify Auth & Angular RxJS Simple State Management
published: true
description: We use RxJS in an Angular Injectable to manage our authentication state from AWS Amplify in a simple way
tags: angular, rxjs, awsamplify
cover_image: https://thepracticaldev.s3.amazonaws.com/i/qu14dg2eneac0skm9d22.jpg
---

Recent updates to the [aws-amplify hub](https://aws-amplify.github.io/docs/js/hub#working-with-the-api) make it easier to listen to (AWS Cognito) auth state changes without using the [aws-amplify-angular](https://github.com/aws-amplify/amplify-js/tree/master/packages/aws-amplify-angular) package. If we are not using the prebuild UI-components of that package, we might save a few hundred KB by just importing the modules we need.

But even if we use [aws-amplify-angular](https://github.com/aws-amplify/amplify-js/tree/master/packages/aws-amplify-angular) package, we can take a look at how we can manage our authentication and user state in a very Angular'esque way without any state management libraries, like [Redux](https://github.com/reduxjs/redux), [NGRX](https://ngrx.io), [apollo-link-state](https://www.apollographql.com/docs/link/links/state), [MobX](https://github.com/mobxjs/mobx), [Akita](https://github.com/datorama/akita) or [NGXS](https://ngxs.gitbook.io/ngxs)

One of the features of Angular is their dependency injection system. We can define tree-shakable injectables to use in any of our components or other injectables. It's a good place to store the state of feature modules in one or more services.

If our app grows more complex there might come a time, where we feel the need to use a single store to manage our state. We might be interested in better developer ergonomics with tools like [Redux DevTools](https://github.com/reduxjs/redux-devtools) or reap the benefits of clearly defined patterns of the library of our choice.

But until this time comes or if we decide, that we want more than one store, this might be a good pattern to use in our Angular apps.

## RxJS BehaviorSubject

Angular has a peer dependency on [RxJS](https://angular.io/guide/rx-library) and uses it in some of their packages. Learning Angular usually entails learning RxJS as well. RxJS offers a Subject, which is an Observable and an Observer in one. The BehaviorSubject additionally stores the last state and can't be created without an initial state. This is pretty ideal for our state management needs, because the subscriber gets the last emitted value immediately after subscribing to the observable. Having an initial state is common to many state management libraries and removes the need to check against null or undefined.

If we want to protect the state against changes from outside, we have a way to make it private and only expose the observable to the public.

## Auth-Service

To create a service using the Angular CLI type

```
$ ng g s auth
```

This will generate a service `auth.service.ts` in our app.

We need to import our service somewhere, so the bundler includes it. Since this is a app-wide service that should run all the time, we can simply inject the service in `app.module.ts`:

```typescript
@NgModule({
  /*...*/
})
export class AppModule {
  constructor(_auth: AuthService) {
    console.log('starting AppModule');
  }
}
```

This will instantiate the AuthService as a singleton on the start of the app.

> In a real-world app, we would probably want to move the service in a separate module (ie. `auth.module.ts`) and inject it in its constructor. Importing that module in `app.module.ts` would have the same effect.

This is our implementation of `auth.service.ts`:

```typescript
import { Injectable } from '@angular/core';
import Auth from '@aws-amplify/auth';
import { Hub } from '@aws-amplify/core';
import { BehaviorSubject } from 'rxjs';
import { map } from 'rxjs/operators';

export interface AuthState {
  isLoggedIn: boolean;
  username: string | null;
  id: string | null;
  email: string | null;
}

const initialAuthState = {
  isLoggedIn: false,
  username: null,
  id: null,
  email: null
};

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private readonly _authState = new BehaviorSubject<AuthState>(
    initialAuthState
  );

  /** AuthState as an Observable */
  readonly auth$ = this._authState.asObservable();

  /** Observe the isLoggedIn slice of the auth state */
  readonly isLoggedIn$ = this.auth$.pipe(map(state => state.isLoggedIn));

  constructor() {
    // Get the user on creation of this service
    Auth.currentAuthenticatedUser().then(
      (user: any) => this.setUser(user),
      _err => this._authState.next(initialAuthState)
    );

    // Use Hub channel 'auth' to get notified on changes
    Hub.listen('auth', ({ payload: { event, data, message } }) => {
      if (event === 'signIn') {
        // On 'signIn' event, the data is a CognitoUser object
        this.setUser(data);
      } else {
        this._authState.next(initialAuthState);
      }
    });
  }

  private setUser(user: any) {
    if (!user) {
      return;
    }

    const {
      attributes: { sub: id, email },
      username
    } = user;

    this._authState.next({ isLoggedIn: true, id, username, email });
  }
}
```

1. We define our public interface of our state in `AuthState` and create an initial object `initialAuthState`.
2. We decided to use the default providedIn property to make this service tree-shakable (optional)
3. We define and initialize a private BehaviorSubject and expose it as an observable
4. We write a private function `setUser(user: any)` to take a cognito user object, desctructure it and create the next state out of it
5. We create an optional way of exposing a slice of the state, the `isLoggedIn` boolean value of our state object.
6. In the constructor we request the current user by writing `Auth.currentAuthenticatedUser()` on instantiation of this injectable and emitting either the user object or the `initialAuthState`
7. We start listening on the hub channel named 'auth' and construct the next state on whether the user is signed in or not

That's it! We can subscribe to state changes anywhere in our where we inject this service.

## Using our auth state service

To access our state, we inject the service and subscribe to any of the exposed observables.

```typescript
// ... imports
@Component({
  selector: 'my-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  isLoggedIn = false;
  user: { id: string; username: string; email: string };

  constructor(private authService: AuthService) {}

  ngOnInit(): void {
    this.authService.isLoggedIn$.subscribe(
      isLoggedIn => (this.isLoggedIn = isLoggedIn)
    );

    this.authService.auth$.subscribe(({ id, username, email }) => {
      this.user = { id, username, email };
    });
  }
}
```

I hope this helps to see how to handle state in Angular in a modular and simple way by moving it into a service and informing anyone interested of changes with the help of a RxJS BehaviorSubject.