0

I'm building an authentication app using the PEAN stack (i.e., PostgreSQL - ExpressJS - Angular - NodeJS).

I have 2 auth guards: if-signed-out.guard.ts and if-signed-in.guard.ts.

if-signed-out.guard.ts

import { CanActivateFn } from '@angular/router';
import { AuthService } from '../services/auth/auth.service';
import { inject } from '@angular/core';
import { map } from 'rxjs/operators';
import { Router } from '@angular/router';

export const IfSignedOut: CanActivateFn = (route, state) => {
  const auth = inject(AuthService);
  const router = inject(Router);

  return auth.getSignInStatusObserver().pipe(
    map((status) => {
      if (status) {
        if (status.success === false) {
          return true;
        } else {
          router.navigate(['/dashboard']);
          return false;
        }
      } else {
        router.navigate(['/dashboard']);
        return false;
      }
    })
  );
};

if-signed-in.guard.ts

import { CanActivateFn } from '@angular/router';
import { AuthService } from '../services/auth/auth.service';
import { inject } from '@angular/core';
import { map } from 'rxjs/operators';
import { Router } from '@angular/router';

export const IfSignedIn: CanActivateFn = (route, state) => {
  const auth = inject(AuthService);
  const router = inject(Router);

  return auth.getSignInStatusObserver().pipe(
    map((status) => {
      if (status) {
        if (status.success === true) {
          return true;
        } else {
          router.navigate(['/dashboard']);
          return false;
        }
      } else if (status === undefined) {
        return true;
      } else {
        router.navigate(['/dashboard']);
        return false;
      }
    })
  );
};

app-routing.module.ts

const routes: Routes = [
  { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
  { path: 'sign-up', component: SignUpComponent, canActivate: [IfSignedOut] },
  { path: 'sign-in', component: SignInComponent, canActivate: [IfSignedOut] },
  { path: 'dashboard', component: DashboardComponent },
  {
    path: 'profile',
    component: MainProfileComponent,
    canActivate: [IfSignedIn],
    children: [
      { path: 'my-profile', component: MyProfileComponent, canActivate: [IfSignedIn] },
      { path: 'edit-profile', component: EditProfileComponent, canActivate: [IfSignedIn] },
      { path: 'change-password', component: ChangePasswordComponent, canActivate: [IfSignedIn] },
      { path: 'delete-profile', component: DeleteProfileComponent, canActivate: [IfSignedIn] },
    ],
  },
];

Problem

The problem was that even if I was signed out, I was still able to access profile components, although all profile components should be accessible only to signed-in users. After a while, I figured out what was causing the problem. Initially, I set the sign-in status to undefined using BehaviorSubject in auth.service.ts. Consequently, sign-in status can be undefined if the user is signed in, or sign-in status can be undefined if the user is signed out.

So, the following code in if-signed-in.guard.ts was problematic:

if-signed-in.guard.ts

/* ... */

else if (status === undefined) {
  return true;
}

/* ... */

I changed the code as follows:

if-signed-in.guard.ts

/* ... */

else if (status === undefined) {
  router.navigate(['/dashboard']);
  return false;
}

/* ... */

Now I'm not able to access profile components if I'm signed out. I solved this problem. This is what I want. But I created another problem. Now I'm not able to access profile components when I'm signed in!

Question

How do I set up my auth guards to achieve the following flow:

1. Signed-out user:

  • Can access the Sign in component.
  • Can access the Sign up component.
  • Can access the Dashboard component.
  • Cannot access profile components. If they try to access profile components, they will be redirected to the Sign in component.

2. Signed-in user:

  • Cannot access the Sign in component. If they try to access the Sign in component, they will be redirected to the Dashboard component.
  • Cannot access the Sign up component. If they try to access the Sign up component, they will be redirected to the Dashboard component.
  • Can access the Dashboard component.
  • Can access profile components.
Rok Benko
  • 14,265
  • 2
  • 24
  • 49
  • I would recommend changing guard names to `IsUserAuthenticated` and `IsUserNotAuthenticated` – hawks Aug 10 '23 at 09:05
  • @hawks Why do you recommend that? – Rok Benko Aug 10 '23 at 09:15
  • the process to check user credentials its called authentication, just to be in line with that. Its also a standard practice I have seen. And you are using 2 different words for the same "process". Its confusing translating signedin and signedout to authenticated and not authenticated – hawks Aug 10 '23 at 09:33
  • @hawks Thanks for the advice. I'll rename my auth guards. By the way, would you mind taking a look at my [current problem](https://stackoverflow.com/questions/76869824/why-is-ngoninit-not-triggered-on-page-refresh-in-one-component-but-it-is-trig)? – Rok Benko Aug 10 '23 at 09:36

0 Answers0