4

I want to use a guard to decide whether or not a user can navigate to the login pag, but I know my logic is faulty because of a Promise. See my code below.

  canActivate(): boolean | Observable<boolean> | Promise<boolean> {
    if (!this.localStorage.getObject('isInitialized')) {
      this.router.navigate(['/locaties']);
      return true;
    }
    return false;
  }

I know what I'm doing is wrong, but I'm lacking the knowledge about promises to go around this. What do I need to do to make this work?

This is my localstorage.getObject():

  // Returns object
  async getObject(key: string) {
    const ret = await Storage.get({ key: key });
    return JSON.parse(ret.value);
  }
Furkan Öztürk
  • 441
  • 4
  • 21

3 Answers3

3

If you want to use an async result based condition in your can activate method then you can use Promise. If you intend to use a direct value from the local storage then you need not use promise. You can do something like below to use promise...

canActivate(): Promise<boolean> {
  return new Promise((resolve) => {
    if (!this.localStorage.getObject('isInitialized')) {
      this.router.navigate(['/locaties']);
      // Do not allow the route
      resolve(false);
    } else {
      // Allow the route
      resolve(true);
    }
  });
}
Raphaël Balet
  • 6,334
  • 6
  • 41
  • 78
0

So there a couple things wrong here but I don't think the promise is one of them. Your function signature says that you have the option to return one of three types,

canActivate(): boolean | Observable<boolean> | Promise<boolean>

But you only ever return a boolean, so really you could re-write this as just,

canActivate(): boolean

But that isn't the issue. It is hard to say without seeing your route setup, but it looks like you are re-routing the user if the route they are requesting is allowed and that isn't necessary. Route guards run when the user is already trying to navigate to a page. If the route guard returns true, that navigation is allowed to happen and the user will proceed to whatever page the route guard is protecting.

But you should be specifying the re-direct page when the route guard returns false. In other words, when the user cannot access the page behind the guard, where do you want to send them?

In general, that would look something like this,

@Injectable()
export class AuthGuard implements CanActivate {
    constructor(private router: Router) {}

    canActivate(state: RouterStateSnapshot): boolean {
        if (!this.localStorage.getObject('isInitialized')) {
            //No need to route here, user will be taken to the route they were trying access
            return true;
        } else {
            //Send the user back to the '/anotherpage' path if they are not logged in
            this.router.navigate(['/anotherpage']);
            return false;
        }
    }
}

Then define your routes somewhere like this,

export const appRoutes: Routes = [
    {
        path: 'pageProtectedByAuthGuard',
        component: YourComponent,
        canActivate: [AuthGuard]
    }
];

Then in your module you need to import those routes,

@NgModule({
    imports: [RouterModule.forRoot(appRoutes, { enableTracing: false })],
    ...
})

More on CanActivate here: https://angular.io/api/router/CanActivate

David Kidwell
  • 600
  • 1
  • 6
  • 15
  • I've implemented it like you said. I did it before with a couple of other pages (to prevent a user from leaving while filling in a form). I need it to redirect the user to a different page if localstorage.getObject() returns true (which means the user is authenticated). So even though the user is not authenticated, it still does the else part of the statement and redirects the user. I can't seem to wrap my head around this – Furkan Öztürk Jun 18 '21 at 20:33
0

The answer was

    this.localStorage.getObject('isInitialized').then((res) => {
      if (res == true) {
        this.router.navigate(['/locaties']);
        return false;
      }
    });
    return true;
  }

Got to remember the the auth guard gets triggered when the user wants to navigate to the page. No need to make it more complicated than that

Furkan Öztürk
  • 441
  • 4
  • 21
  • This is still wrong. You are returning true always, because the last return true statement is executed before the promise resolves. – JanBrus Jun 12 '23 at 08:01
  • @JanBrus So then what is the solution here? I have this exact issue. I have to call an async function from `canActivate` that returns a string promise with information I need to access from within `canActivate`. If the string exists then I let the user through, and if it doesn't, I do not. I could go back to just using simple booleans, but I need that string specifically from within canActivate (I have to append it to the request). This is for NestJS, but this system is pretty much the exact same between the two. – said Jun 29 '23 at 19:52
  • The answer that is marked correct is the one you are looking for (you wait for the promise and call resolve(false); or resolve(true); accordingly) – JanBrus Jun 29 '23 at 20:57