Stephane
Stephane

Reputation: 12790

One infinite redirect loop in an Angular application

I'm doing an Angular application with the following routing:

const routes: Routes = [
  {
    path: '',
    component: LoginLayoutComponent,
    children: [
      {
        path: '',
        redirectTo: 'login',
        pathMatch: 'full'
      },
      {
        path: 'login',
        component: LoginComponent
      }
    ]
  },
  {
    path: '',
    component: HomeLayoutComponent,
    canActivate: [AuthGuardService],
    children: [
      {
        path: 'users',
        component: UsersComponent,
      },
      {
        path: 'detail/:id',
        component: UserComponent,
      },
      {
        path: 'dashboard',
        component: DashboardComponent,
        data: {
          expectedRole: 'admin'
        }
      },
      {
        path: 'home',
        loadChildren: './views/home/home.module#HomeModule',
        data: {
          preload: true,
          delay: false
        }
      },
      {
        path: 'error',
        component: ErrorComponent
      },
    ]
  },
];

If I'm not logged in and I request any secured url, like for example http://localhost:4200/users or http://localhost:4200/dashboard then there is a redirect to the http://localhost:4200/ and the application goes into an infinite loop. If I'm logged in then it works fine.

The browser console displays the following message Navigation triggered outside Angular zone.

Here is my auth guard service:

  canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
    const expectedRole = route.data.expectedRole ? route.data.expectedRole : null;
    const tokenPayload = this.tokenService.getDecodedAccessToken();
    return this.authService.isAuthenticated()
    .pipe(
      map(isAuth => {
        if (!isAuth) {
          this.router.navigate(['login']);
          return false;
        } else {
          return true;
        }
      }),
      catchError((error, caught) => {
        return of(false);
      })
    );
  }

  canLoad(): Observable<boolean> {
    if (this.authService.isAuthenticated()) {
      return of(true);
    } else {
      return of(false);
    }
  }

I'm on Angular 7

EDIT: The issue is now resolved with the following auth guard:

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
    return this.authService.isAuthenticated()
    .pipe(
      map(isAuthenticated => {
        if (!isAuthenticated) {
          this.authService.setPostLoginRedirectUrl(state.url);
          this.router.navigate(['login']);
          return false;
        } else {
          return true;
        }
      }),
      catchError((error, caught) => {
        return of(false);
      })
    );
  }

and the following routes:

const routes: Routes = [
  {
    path: '',
    component: LoginLayoutComponent,
    children: [
      {
        path: '',
        redirectTo: 'login',
        pathMatch: 'full'
      },
      {
        path: 'login',
        component: LoginComponent
      }
    ]
  },
  {
    path: '',
    component: HomeLayoutComponent,
    canActivateChild: [AuthGuardService],
    children: [
      {
        path: 'users',
        component: UsersComponent,
      },
      {
        path: 'detail/:id',
        component: UserComponent,
      },
      {
        path: 'dashboard',
        component: DashboardComponent,
        data: {
          expectedRole: 'admin'
        }
      },
      {
        path: 'home',
        loadChildren: './views/home/home.module#HomeModule',
        data: {
          preload: true,
          delay: false
        }
      },
      {
        path: 'error',
        component: ErrorComponent
      },
    ]
  },
];

Upvotes: 3

Views: 9239

Answers (5)

Mel
Mel

Reputation: 1512

EDIT: Found out the culprit! In app.module.ts you need to import AuthModule first. Doing this will allow you to use the path '**' in either 'auth-routing.module.ts' or 'app-routing.module.ts'

@NgModule({

  imports: [
    AuthModule,
    AppRoutingModule,
    BrowserModule,
    HttpClientModule,
    SharedModule
  ],
})

I was having an infinite loop issue as well. Not sure if this relates to yours but for me I had two routing files. 'app-routing.module.ts' and 'auth-routing.module.ts'. I moved the code below from ' app-routing.module.ts' into 'auth-routing.module.ts' and no more infinite loops! Hope this helps you in some way!

 {
    path: '**',
    redirectTo: 'my-route',
  }

Upvotes: 1

Marian Turchyn
Marian Turchyn

Reputation: 559

In my case (topic is similar) when I was getting loop it was AuthGuard on Login route.

{ path: 'login', component: LoginFormComponent, canActivate: [AuthGuard], }

To fix this I just removed guard from Login route.

Upvotes: 0

Steve Lam
Steve Lam

Reputation: 1059

For my problem, the order of import impacts on this error.

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    CommonModule,

    AccountModule, // Load other eager modules first
    AppRoutingModule // Move this AppRouting to the end like this
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

Upvotes: 0

Avin Kavish
Avin Kavish

Reputation: 8947

I don't have an exact soution for you, just a few this suggestions for debugging. Try expressing this as:

const routes: Routes = [
{
  path: '',
  component: LoginLayoutComponent,
  children: [
    {
      path: 'login',
      component: LoginComponent
    },
    {
      path: '',
      component: LoginComponent,
      pathMatch: 'full'
    },
  ]
}

to see if it is the route redirect.

Try replacing this with,

this.router.navigateByUrl('/login');

or

this.router.navigate(['', 'login']);

Upvotes: 0

SiddAjmera
SiddAjmera

Reputation: 39462

redirectTo value should always have a leading / as it represents the actual route the user should be navigated to.

Change redirectTo: 'login', to redirectTo: '/login', in your Route Config

Also, this:

this.router.navigate(['login']);

should be

this.router.navigate(['/login']);

Upvotes: 0

Related Questions