Angular-auth-oidc-client: Error: Invalid CanLoad guard

Created on 17 Mar 2021  路  10Comments  路  Source: damienbod/angular-auth-oidc-client

Describe the bug

I tried to follow the instructions given in this issue but it leads to this error:

core.js:6140 ERROR Error: Uncaught (in promise): Error: Invalid CanLoad guard
Error: Invalid CanLoad guard
    at router.js:2797
    at Array.map (<anonymous>)
    at ApplyRedirects.runCanLoadGuards (router.js:2787)
    at ApplyRedirects.getChildConfig (router.js:2769)
    at ApplyRedirects.matchSegmentAgainstRoute (router.js:2740)
    at ApplyRedirects.expandSegmentAgainstRoute (router.js:2689)
    at MergeMapSubscriber.project (router.js:2667)
    at MergeMapSubscriber._tryNext (mergeMap.js:44)
    at MergeMapSubscriber._next (mergeMap.js:34)
    at MergeMapSubscriber.next (Subscriber.js:49)
    at resolvePromise (zone-evergreen.js:798)
    at resolvePromise (zone-evergreen.js:750)
    at zone-evergreen.js:860
    at ZoneDelegate.invokeTask (zone-evergreen.js:399)
    at Object.onInvokeTask (core.js:28500)
    at ZoneDelegate.invokeTask (zone-evergreen.js:398)
    at Zone.runTask (zone-evergreen.js:167)
    at drainMicroTaskQueue (zone-evergreen.js:569)
    at ZoneTask.invokeTask [as invoke] (zone-evergreen.js:484)
    at invokeTask (zone-evergreen.js:1621)

To Reproduce

Check the MCVE here.

Expected behavior

Be able to protect my routes by a CanLoad guard.

Additional context

I open a new issue because the previous I raised was closed before I was able to confirm the instructions were working or not, I hope we will be able to only close this one when there is no error... :persevere:

investigate

Most helpful comment

Hey, alright: Here is a repo showing the AutoLoginGuard working repro1022. I did _not_ check your example @hadrien-toma because it is not a minimalistic reproducing project. Let us try to boil the error down with as less side effects as possible. Is this what you expect? What differs to your solution?

All 10 comments

@ColinLaws if you can not wait for the bug to be resolved, you can use the workaround I described in the source issue, creating your own CanLoad guard which use a quite dirty trick but which is working (try/catch/setTimeout 馃あ):

@Injectable()
export class IsAuthenticatedCanLoad implements CanLoad {
  constructor(private oidcSecurityService: OidcSecurityService) {}

  canLoad(route: Route, segments: UrlSegment[]) {
    return combineLatest([
      this.oidcSecurityService.isAuthenticated$,
    ]).pipe(
      map(([isAuthenticated]) => {
        if (!isAuthenticated) {
          try {
            this.oidcSecurityService.authorize();
          } catch (error) {
            setTimeout(() => {
              this.oidcSecurityService.authorize();
            }, 1000);
          }
          return false;
        } else {
          return true;
        }
      })
    );
  }
}

@FabianGosebrink on your side if you need help, do not hesitate to ask :persevere:, especially concerning the bug reproduction I put in the MCVE, using a local, self-contained SSO, thanks to Docker and Keycloak :pray:.

Hi @FabianGosebrink can you follow this up?

Gruss Damien

Hey, alright: Here is a repo showing the AutoLoginGuard working repro1022. I did _not_ check your example @hadrien-toma because it is not a minimalistic reproducing project. Let us try to boil the error down with as less side effects as possible. Is this what you expect? What differs to your solution?

@FabianGosebrink thanks for your investigations and feedbacks.

How can I test your repo? Without using Keycloak I don't see how to validate or not a config (and I suppose that adding keycloak is the part responsible of your take that it is not a MCVE (the C for Complete is important to me: you can test it standalone, whereas in your repo I don't understand how to set up the SSO and I suppose it is a remote/external setup)).


The error I have changed and is detailed here (expand this to see it)

angular-auth-oidc-client.js:178 Please provide a configuration before setting up the module
logError @ angular-auth-oidc-client.js:178
checkAuth @ angular-auth-oidc-client.js:3353
(anonymous) @ angular-auth-oidc-client.js:4329
_tryNext @ mergeMap.js:44
_next @ mergeMap.js:34
next @ Subscriber.js:49
_subscribe @ BehaviorSubject.js:14
_trySubscribe @ Observable.js:42
_trySubscribe @ Subject.js:81
subscribe @ Observable.js:28
_subscribe @ Observable.js:76
subscribe @ Observable.js:27
call @ mergeMap.js:19
subscribe @ Observable.js:23
call @ map.js:16
subscribe @ Observable.js:23
call @ take.js:22
subscribe @ Observable.js:23
innerSubscribe @ innerSubscribe.js:67
_innerSub @ mergeMap.js:57
_tryNext @ mergeMap.js:51
_next @ mergeMap.js:34
next @ Subscriber.js:49
(anonymous) @ subscribeToArray.js:3
_trySubscribe @ Observable.js:42
subscribe @ Observable.js:28
call @ mergeMap.js:19
subscribe @ Observable.js:23
subscribeToResult @ subscribeToResult.js:9
_complete @ combineLatest.js:52
complete @ Subscriber.js:61
(anonymous) @ subscribeToArray.js:5
_trySubscribe @ Observable.js:42
subscribe @ Observable.js:28
call @ combineLatest.js:26
subscribe @ Observable.js:23
call @ scan.js:18
subscribe @ Observable.js:23
call @ filter.js:13
subscribe @ Observable.js:23
call @ map.js:16
subscribe @ Observable.js:23
call @ take.js:22
subscribe @ Observable.js:23
innerSubscribe @ innerSubscribe.js:67
_innerSub @ switchMap.js:44
_next @ switchMap.js:34
next @ Subscriber.js:49
(anonymous) @ subscribeToArray.js:3
_trySubscribe @ Observable.js:42
subscribe @ Observable.js:28
call @ switchMap.js:15
subscribe @ Observable.js:23
call @ tap.js:16
subscribe @ Observable.js:23
call @ map.js:16
subscribe @ Observable.js:23
call @ mergeMap.js:19
subscribe @ Observable.js:23
call @ mergeMap.js:19
subscribe @ Observable.js:23
call @ catchError.js:14
subscribe @ Observable.js:23
innerSubscribe @ innerSubscribe.js:67
_innerSub @ mergeMap.js:57
_tryNext @ mergeMap.js:51
_next @ mergeMap.js:34
next @ Subscriber.js:49
(anonymous) @ subscribeToArray.js:3
_trySubscribe @ Observable.js:42
subscribe @ Observable.js:28
call @ mergeMap.js:19
subscribe @ Observable.js:23
call @ filter.js:13
subscribe @ Observable.js:23
call @ take.js:22
subscribe @ Observable.js:23
call @ throwIfEmpty.js:13
subscribe @ Observable.js:23
call @ catchError.js:14
subscribe @ Observable.js:23
call @ map.js:16
subscribe @ Observable.js:23
innerSubscribe @ innerSubscribe.js:67
_innerSub @ mergeMap.js:57
_tryNext @ mergeMap.js:51
_next @ mergeMap.js:34
next @ Subscriber.js:49
(anonymous) @ subscribeToArray.js:3
_trySubscribe @ Observable.js:42
subscribe @ Observable.js:28
call @ mergeMap.js:19
subscribe @ Observable.js:23
call @ scan.js:18
subscribe @ Observable.js:23
call @ takeLast.js:22
subscribe @ Observable.js:23
call @ throwIfEmpty.js:13
subscribe @ Observable.js:23
call @ map.js:16
subscribe @ Observable.js:23
call @ map.js:16
subscribe @ Observable.js:23
call @ catchError.js:14
subscribe @ Observable.js:23
call @ map.js:16
subscribe @ Observable.js:23
innerSubscribe @ innerSubscribe.js:67
_innerSub @ switchMap.js:44
_next @ switchMap.js:34
next @ Subscriber.js:49
notifyNext @ switchMap.js:66
_next @ innerSubscribe.js:10
next @ Subscriber.js:49
(anonymous) @ subscribeToPromise.js:5
invoke @ zone-evergreen.js:364
onInvoke @ core.js:28510
invoke @ zone-evergreen.js:363
run @ zone-evergreen.js:123
(anonymous) @ zone-evergreen.js:857
invokeTask @ zone-evergreen.js:399
onInvokeTask @ core.js:28497
invokeTask @ zone-evergreen.js:398
runTask @ zone-evergreen.js:167
drainMicroTaskQueue @ zone-evergreen.js:569
invokeTask @ zone-evergreen.js:484
invokeTask @ zone-evergreen.js:1621
globalZoneAwareCallback @ zone-evergreen.js:1647
Show 98 more frames
core.js:6162 ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'usePushedAuthorisationRequests' of null
TypeError: Cannot read property 'usePushedAuthorisationRequests' of null
    at LoginService.login (angular-auth-oidc-client.js:3907)
    at MapSubscriber.project (angular-auth-oidc-client.js:4339)
    at MapSubscriber._next (map.js:29)
    at MapSubscriber.next (Subscriber.js:49)
    at MergeMapSubscriber.notifyNext (mergeMap.js:70)
    at SimpleInnerSubscriber._next (innerSubscribe.js:10)
    at SimpleInnerSubscriber.next (Subscriber.js:49)
    at Observable._subscribe (subscribeToArray.js:3)
    at Observable._trySubscribe (Observable.js:42)
    at Observable.subscribe (Observable.js:28)
    at resolvePromise (zone-evergreen.js:798)
    at resolvePromise (zone-evergreen.js:750)
    at zone-evergreen.js:860
    at ZoneDelegate.invokeTask (zone-evergreen.js:399)
    at Object.onInvokeTask (core.js:28497)
    at ZoneDelegate.invokeTask (zone-evergreen.js:398)
    at Zone.runTask (zone-evergreen.js:167)
    at drainMicroTaskQueue (zone-evergreen.js:569)
    at ZoneTask.invokeTask [as invoke] (zone-evergreen.js:484)
    at invokeTask (zone-evergreen.js:1621)

The tricked guard is still working and there is still no error when using AutoLoginGuard as a CanActivate one. I suppose the issue is now caused because of the async flow used for the configureAuth:

export interface OidcConfig {
  passedConfig: OpenIdConfiguration;
  passedAuthWellKnownEndpoints?: AuthWellKnownEndpoints;
}

export function configureAuth(
  httpClient: HttpClient,
  oidcConfigService: OidcConfigService
) {
  const setupAction$ = httpClient.get(`assets/index.json`).pipe(
    switchMap((assetsConfig: any) =>
      forkJoin([
        of(assetsConfig),
        httpClient.get<OidcConfig>(assetsConfig.stsServer),
      ])
    ),
    switchMap(([assetsConfig, oidcConfig]) => {
      const config: OpenIdConfiguration = {
        // 1锔忊儯 The oidcConfig fetched is used as a base...
        ...oidcConfig,
        // 2锔忊儯 over this base, a customization is applied at build level...
        forbiddenRoute: 'forbidden',
        postLogoutRedirectUri: `${window.location.origin}/post-logout-redirect`,
        redirectUrl: `${window.location.origin}/redirect`,
        silentRenew: true,
        silentRenewUrl: `${window.location.origin}/silent-renew.html`,
        unauthorizedRoute: 'unauthorized',
        // 3锔忊儯 Finally, the runtime customization layer is applied so users can always customize it on their side
        ...assetsConfig,
      };
      console.log(
        'apps/my-realm/my-client-id/src/app/app.module.ts',
        'configureAuth',
        { config }
      );
      return oidcConfigService.withConfig(config);
    })
  );

  return () => setupAction$.toPromise();
}

I specify that it is crucial for me to conserve the logic of this flow: a first HTTP call to get the stsServer and 3 customizations layers.

Any key?

@ColinLaws would you like to try also on your side to see if it is only my use case that is breaking or not?

Hey, the repository I provided is runnable as is, just as our samples. I try to reproduce the error because the sts (Keycloak, Identity server etc.) should not matter. The repo is just using my testing sts for the sake of simplicity.

You can test my repo with running npm start and then pressing the button to go to the customers route. It should redirect you to the sts and get back to the customers route. The customers module is a lazy loaded module.

Please let us know if you found the error in our repo. What you can do is take the sample repo and get it step by step as close to your solutions as possible and see when the error pops up.

Thanks for your message @FabianGosebrink, I will do this this week or the next one and will come back to you with news here 馃檪.

@hadrien-toma @FabianGosebrink

We merged the CanLoad interface to the guard. Can we close this?

Greetings Damien

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vit100 picture vit100  路  4Comments

cgatian picture cgatian  路  4Comments

toddtsic picture toddtsic  路  4Comments

sdev95 picture sdev95  路  3Comments

hannesrohde picture hannesrohde  路  3Comments