keycloak-angular icon indicating copy to clipboard operation
keycloak-angular copied to clipboard

Completely exclude some routes from keycloak

Open koldoon opened this issue 5 years ago • 12 comments

- [ ] bug report -> please search for issues before submitting
- [x] feature request

Versions.

6.0.1

Desired functionality.

I have a router with several endpoints. Keycloak service is initialised in root app module with APP_INITIALIZER as described in manual. During the initialization process, keycloak performs redirect to sso page and back. What I need: do NOT perform keycloak initialization and any redirections for some routes at all. How can I achieve this for now?

koldoon avatar Apr 30 '19 14:04 koldoon

Hi, instead of using the APP_INITIALIZER for the entire app, you should use Guards (for example CanLoad on a lazy loaded module) to add the keyCloak logic.

There is also a talk in this thread: https://github.com/mauriciovigolo/keycloak-angular/issues/117

weihsth avatar Jun 16 '19 12:06 weihsth

@weihsth I have a similar question, could your give more detail description. thanks!

lin1010 avatar Aug 02 '19 07:08 lin1010

I wanted to ask the same question, so is there any suggestions @mauriciovigolo regarding this?

To be more accurate, we want routes like '/about', '/signup' publicly accessible. If we exclude mentioned paths from bearerInterceptor, users are still redirected to KC login. If we disable bearer interceptor, than we have manual control using guards and interceptors, but we are not sure is that good way.

We are tried to initialize KeycloakService during app initialization like this:

        await keycloak.init({
          config: environment.keycloakConfig,
          loadUserProfileAtStartUp: false,
          initOptions: {
            onLoad: 'check-sso',
            checkLoginIframe: false
          },
          bearerExcludedUrls: ['/about','/signup'],
          bearerPrefix: 'KC-Bearer'
        });

Except initial approach (by disabling bearerInterceptor), we could place all private routes under private module and initialize Keycloak there as weihsth suggested.

dsarcevic avatar Oct 17 '19 09:10 dsarcevic

Hi @dsarcevic. Are you using an implementation similar to https://github.com/mauriciovigolo/keycloak-angular#authguard?

That example doesn't check if the route is public or not and redirects to the login page if the user is not authenticated.

if (!this.authenticated) {
	this.keycloakAngular.login()
    resolve(false);
	return;
}

One thing you could do is to retrieve the excludedUrls from KeycloakService and check if the current route is public or not, avoiding to call the login method in those cases.

In the newer version (8.0.0), I will completely rewrite this logic and make it easier to handle, but we will have breaking changes.

Feel free to contact me if you need any other help.

mauriciovigolo avatar Oct 21 '19 09:10 mauriciovigolo

Hi @mauriciovigolo , We tried to implement guard which would allow that users without authentication can access to public urls (and not access to protected pages), but if we initialize Keycloak instance as I copied above (using initializer function in app.module), guard is ignored and users are always redirected to Keycloak login.

As I said, if we disable bearerInterceptor and manually put Bearer in header on places where user is authenticated, than we can achieve desired behaviour, but we are not sure is that good approach.

Do you have any idea when we can expect 8.0.0?

p.s. Many thanks for great library

dsarcevic avatar Oct 21 '19 11:10 dsarcevic

Hi @mauriciovigolo , We tried to implement guard which would allow that users without authentication can access to public urls (and not access to protected pages), but if we initialize Keycloak instance as I copied above (using initializer function in app.module), guard is ignored and users are always redirected to Keycloak login.

As I said, if we disable bearerInterceptor and manually put Bearer in header on places where user is authenticated, than we can achieve desired behaviour, but we are not sure is that good approach.

Do you have any idea when we can expect 8.0.0?

p.s. Many thanks for great library

Hi @mauriciovigolo

Any updates on this? I am facing similar blocker issue for my angular app

singhvivek2503 avatar Dec 13 '19 07:12 singhvivek2503

Hi @mauriciovigolo

Any updates on this? I am facing similar blocker issue for my angular app

Same here! Hope you're all well!

pflugich avatar Apr 29 '20 16:04 pflugich

+1 to help motivate for a higher priority of this feature.

Having said that if somebody could provide a more detailed working example that would go a long way.

zwessels avatar Jun 17 '20 16:06 zwessels

The working example is really quite simple. In your AppModule (or wherever the route is, that requires a login) you need to add a canLoad-Guard, that initializes your Keycloak-Instance. In theory it is possible to use a canActivate-Guard but this can lead to an infinite redirect loop.

app.module.ts:

const routes: Routes = [
  {
    path: '/private',
    loadChildren: () =>
      import('./private/private.module').then(m => m.PrivateModule),
    canLoad: [IsAuthorizedGuard],
  },
  // ... other public routes
]

@NgModule({
  imports:      [
    BrowserModule,
    FormsModule,
    RouterModule.forRoot(routes),
    KeycloakAngularModule,
  ],
  providers: [
    KeycloakService
  ],
  declarations: [ AppComponent, HelloComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

is-authorized.guard.ts

@Injectable({
  providedIn: 'root'
})
export class IsAuthorizedGuard implements CanLoad {
  constructor(private readonly keycloakService: KeycloakService) {}

  canLoad(route: Route, segments: UrlSegment[]): Observable<boolean> {
    return this.keycloakService.init(
        // your config
      ).then(this.keycloakService.isLoggedIn());
  }
}

Another problem that occured was, that requests requiring a logged in user performed at startup were no longer possible. For example requests, that load additional information about the logged in user. This can be solved by listening for the OnReady-Event. (args is true if the login was successful)

this.keycloakService.keycloakEvents$
  .pipe(
    filter(({ type, args }) => type === KeycloakEventType.OnReady && args),
    first(),
    switchMap(() => this.http.get<User>('/api/user')),
  )
  .subscribe(user => {
     // do something with the user object
  });

I hope this helps! :)

hesch avatar Jun 26 '20 12:06 hesch

@hesch

canLoad(route: Route, segments: UrlSegment[]): Observable<boolean> {
    return this.keycloakService.init(
        // your config
      ).then(this.keycloakService.isLoggedIn());
  }
  1. I presume it should be this.keycloakService.isLoggedIn instead of this.keycloakService.isLoggedIn().

  2. When I add the this.keycloakService.isLoggedIn part, then the app just redirects back to \ without doing anything but if I take it out and just have: return this.keycloakService.init(config) then it redirects as expected. Problem is then according to the rest of the application Keycloak isn't initialized.

Any ideas?

zwessels avatar Jul 27 '20 23:07 zwessels

@hesch

Thank you so much for your example, I got it working.

My is-authorized.guard.ts needed to change to:

canLoad(route: Route, segments: UrlSegment[]): Promise<boolean> {
    return this.keycloakService.init(keycloakOptions);
  }

Another bit that missing was to add the interceptor to my private module as per #180 :

...
providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: KeycloakBearerInterceptor,
      multi: true
    }
],
...

I didn't run into the scenario where I had to use the keycloakEvents.

zwessels avatar Jul 28 '20 08:07 zwessels

+1

Is same can achievable for nodejs + express app for

/login route

/contactUs

for the time being i'm achieving this like : 1- fetch token first app.use('/api/login', generateKeyCloakToken);

2- then init the keycloak middleware instance app.use(keycloak.middleware());

Thanks

shikharpainulyevon avatar Oct 01 '20 11:10 shikharpainulyevon