DEV Community

Cover image for Implementing Auth0 in Angular with NgRx - iFour Technolab
Harshal Suthar
Harshal Suthar

Posted on • Originally published at ifourtechnolab.com

Implementing Auth0 in Angular with NgRx - iFour Technolab

What is Auth0?

Auth0 is a versatile, drop-in solution to add authentication and authorization services to the applications. Auth0 is simple to implement and an adaptable authentication and authorization platform.

Auth0 is a secure service that ensures authentication and authorization functionality when implemented in your application. It works based on tokens and uses different identity providers. It includes several platforms as well as social networks.

When you build your solution to authenticate and authorize users, it can cost you money, time, and risk. To avoid these, we should use Auth0 in our application.

What is NgRx in Angular?

NgRx is a framework for creating reactive applications in Angular. NgRx provides libraries for the following:

  • Managing global and local state.
  • Isolation of side effects to promote a cleaner component architecture.
  • Entity collection management.
  • Integration with the Angular Router.
  • Developer tooling that enhances developer experience when building many different types of applications.

NgRx is an open-source library that provides reactive state management for Angular applications. NgRx provides a way to preserve data in your Angular application as a single source of truth which is inspired by Redux.

NgRx uses streams to communicate with a data store, this data store connects to your components and services and ultimately simplifies the complete method of data management in your Angular application. Rather than injecting services every place and managing communication between them, NgRx manages your application from only one source. Instead of working with individual components, you can work with in terms of its overall state using NgRx.

Implement Auth0 in Angular application with NgRx:

Add the Angular app in Auth0:

The first step is to add the angular application in Auth0.

Go to https://auth0.com/ to create an account in Auth0. You will see the dashboard of Auth0 as shown in the following image.

Click on create an application to integrate auth0 in the Angular application.

Image description
Figure 1 Auth0 Dashboard

After clicking on create application from the dashboard, you will be navigated to the following page. On this Page Write the name of your application and click on Single Page Web Applications as we are creating an Angular application.

Image description
Figure 2 Create Application Page

Once this is created, you will see the basic information like Name, Client ID, Domain, Client Server as well as application properties, Application URLs, ID token, etc. As we know out Angular will run on the domain HTTP(s)://localhost:4200 locally, so add these URLs into the correct fields of auth0. We have added both http://localhost:4200 and https://localhost:4200 in the fields as shown in the below image, so in the case where we need to switch to HTTPS, we do not face any problems.

From the same page, we will need values of Client ID and Domain to place in our angular application.

Image description
Figure 3 Auth0 settings

Create an Angular app and Install the Dependencies

Now, we can create our Angular application with the following command:

ng new Auth0withNgrx

After creating an angular application, we will install Angular helpers from Auth0:

npm install @auth0/auth0-angular

Read More: The Complete Guide To Angular Security

Add the Auth service abstraction

Auth0 is a third-party library, so we will create abstraction for it. We will add a file called auth.service.ts.

auth.service.ts:


import { Injectable } from '@angular/core';
                    import { AuthService } from '@auth0/auth0-angular';
                    import { Observable } from 'rxjs';
                    @Injectable({
                      providedIn: 'root',
                    })
                    export class AuthenticationService {
                      constructor(public authService: AuthService) {}
                      get isLoggedIn$(): Observable<boolean> {
                        return this.authService.isAuthenticated$;
                      }
                      getToken$(): Observable<string> {
                        return this.authService.getAccessTokenSilently();
                      }
                      get user$(): Observable<any> {
                        return this.authService.user$;
                      }
                      login(): void {
                        this.authService.loginWithRedirect();
                      }
                      logout(): void {
                        this.authService.logout({ returnTo: document.location.origin });
                      }
                    }
                    </any></string></boolean>

Enter fullscreen mode Exit fullscreen mode

Include AuthModule

We must include AuthModule form @auth0/auth0-angular in the app.module.ts file. Here we will include the values of Client ID and Domain that we found while creating application in Basic Information in Auth0 dashboard.

app.module.ts:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AuthModule } from '@auth0/auth0-angular';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { authReducer } from './store/auth.reducer';
import { AuthEffects } from './store/auth.effects';
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    StoreModule.forRoot({ auth: authReducer }),
    EffectsModule.forRoot([AuthEffects]),
    AuthModule.forRoot({
      domain: '<your domain=""> ',
      clientId: '<your client="" id="">',
      redirectUri: window.location.origin,
    }),
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
</your></your>

Enter fullscreen mode Exit fullscreen mode

Add NgRx

To add NgRx in our application, we need to include dependencies @ngrx/store and @ngrx/effects by executing the following commands:

ng add @ngrx/store@latest

ng add @ngrx/effects@latest

After adding dependencies in our application, we will create a separate folder for placing NgRx related files in it.

We will create a folder named “store” under the app and place the following four NgRx related files in that folder.

Add the Actions

We will create a file called auth.actions.ts under the store folder for adding actions. We will add the action to trigger the login for calling login as well as a corresponding action for completeness called loginComplete. Same for logout and logoutComplete. We will create an action to trigger when the auth0 redirects back to our angular application for telling the state that it must be changed.

auth.actions.ts:

import { createAction, props } from '@ngrx/store';
                  export const checkAuth = createAction('[Auth] checkAuth');
                  export const login = createAction('[Auth] login');
                  export const loginComplete = createAction(
                    '[Auth] loginComplete',
                    props<{ profile: any; isLoggedIn: boolean }>()
                  );
                  export const logout = createAction('[Auth] logout');
                  export const logoutComplete = createAction('[Auth] logoutComplete');

Enter fullscreen mode Exit fullscreen mode

Add the reducer and the state

The state of our application will be stored in an object called auth with the values – userProfile and idLoggedIn.

{    auth: {
                    isLoggedIn,
                    userProfile }   }

Enter fullscreen mode Exit fullscreen mode

We will create a reducer file called auth.reducer.ts under the store folder to add our state as an interface.

auth.reducer.ts:

import { Action, createReducer, on } from '@ngrx/store';
                      import * as authActions from './auth.actions';
                      export interface AuthState {
                        userProfile: any;
                        isLoggedIn: boolean;
                      }
                      export const initialState: AuthState = {
                        userProfile: null,
                        isLoggedIn: false,
                      };
                      const authReducerInternal = createReducer(
                        initialState,
                        on(authActions.loginComplete, (state, { profile, isLoggedIn }) => {
                          return {
                            ...state,
                            userProfile: profile,
                            isLoggedIn,
                          };
                        }),
                        on(authActions.logoutComplete, (state, {}) => {
                          return {
                            ...state,
                            userProfile: null,
                            isLoggedIn: false,
                          };
                        })
                      );
                      export function authReducer(
                        state: AuthState | undefined,
                        action: Action
                      ): AuthState {
                        return authReducerInternal(state, action);
                      }

Enter fullscreen mode Exit fullscreen mode

The AuthState represents the value the auth property has in our state. The reducer only handles anything that goes on inside of the auth property.

We have set the initialState and create the reducer to pass the initialState. We must add state manipulation when a specific action comes in.

We will add the profile we received if the login is completed with the action loginComplete, and also set the isLoggedIn. We will reset the userProfile to null and isLoggedIn to false when the action logoutComplete is thrown.

Planning to Hire dedicated Angular Developer?
Your Search ends here.

Add the effects

We will use effects for the asynchronous work to do when we are trying to manipulate the state after async actions have finished. We will create auth.effects.ts file for implementing effects.

The following 3 are actions to listen to:

The login, the logout and the checkAuth action.

auth.effects.ts:

import { Injectable } from '@angular/core';
                  import { Actions, createEffect, ofType } from '@ngrx/effects';
                  import { combineLatest, of } from 'rxjs';
                  import { switchMap, tap } from 'rxjs/operators';
                  import { AuthenticationService } from '../auth.service';
                  import * as fromAuthActions from './auth.actions';
                  @Injectable()
                  export class AuthEffects {
                    constructor(
                      private actions$: Actions,
                      private authService: AuthenticationService
                    ) {}
                    login$ = createEffect(
                      () =>
                        this.actions$.pipe(
                          ofType(fromAuthActions.login),
                          tap(() => this.authService.login())
                        ),
                      { dispatch: false }
                    );
                    checkAuth$ = createEffect(() =>
                      this.actions$.pipe(
                        ofType(fromAuthActions.checkAuth),
                        switchMap(() =>
                          combineLatest([this.authService.isLoggedIn$, this.authService.user$])
                        ),
                        switchMap(([isLoggedIn, profile]) => {
                          if (isLoggedIn) {
                            return of(fromAuthActions.loginComplete({ profile, isLoggedIn }));
                          }
                          return of(fromAuthActions.logoutComplete());
                        })
                      )
                    );
                    logout$ = createEffect(() =>
                      this.actions$.pipe(
                        ofType(fromAuthActions.logout),
                        tap(() => this.authService.logout()),
                        switchMap(() => of(fromAuthActions.logoutComplete()))
                      )
                    );
                  }

Enter fullscreen mode Exit fullscreen mode

The login effect will call the authService.login() action and won’t dispatch any other actions then.

The logout action will call the authService.logout() method and will return the logoutComplete.

When we will get redirected from Auth0 to our app again, we will throw the checkAuth action. We will collect the latest information got updated by the service of Auth0 and add it to our state. We have collected isLoggedIn$ and user$ properties and update the state with it. If isLoggedIn is true-which should be the case after the redirect- then we can return a loginComplete action, otherwise, we will reset the state with a logoutComplete action

Add the selectors

We will build selectors to make the consumption in components clear which we want from the state and provide it.

We will create a selector for the isLoggedIn and the user-profile property as well as the auth property from the state object.

auth.selector.ts:

                  import { createFeatureSelector, createSelector } from '@ngrx/store';
                  import { AuthState } from './auth.reducer';
                  export const getAuthFeatureState = createFeatureSelector<authstate>('auth');
                  export const selectCurrentUserProfile = createSelector(
                    getAuthFeatureState,
                    (state: AuthState) => state.userProfile
                  );
                  export const selectIsLoggedIn = createSelector(
                    getAuthFeatureState,
                    (state: AuthState) => state.isLoggedIn
                  );
                  </authstate>

Enter fullscreen mode Exit fullscreen mode

Build component

To consume the values in the state of selectors, the component will consume the selectors. And it dispatches the action checkAuth() when the checkAuth() is loaded to update the information in the state.

It also provides two methods for login and logout.

app.component.ts:


import { Component, OnInit } from '@angular/core';
import { select, Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { checkAuth, login, logout } from './store/auth.actions';
import {
selectCurrentUserProfile,
selectIsLoggedIn
} from './store/auth.selectors';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
title = 'Auth0withNgrx';
loggedIn$: Observable<boolean> | undefined;
profile$: Observable<any> | undefined;
constructor(private store: Store<any>) {}
ngOnInit() {
this.loggedIn$ = this.store.pipe(select(selectIsLoggedIn));
this.profile$ = this.store.pipe(select(selectCurrentUserProfile));
this.store.dispatch(checkAuth());
}
logout() {
this.store.dispatch(logout());
}
login() {
this.store.dispatch(login());
}
}
/any></any></any></boolean>

Enter fullscreen mode Exit fullscreen mode

When we run the project, we will see the following output:

Image description
Figure 4 Output

When we click on this button, we will be redirected to the page provided by Auth0.

Image description
Figure 5 Output

Conclusion

In this blog, we have used the Auth0 service in our angular application for authentication and authorization.
We can say that it is more convenient to use the Auth0 service rather than creating our solution for authorization and authentication.

Top comments (0)