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.