Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request]: Support multiple configurations for AutoLoginPartialRoutesGuard #1661

Open
nlu-clgx opened this issue Jan 24, 2023 · 29 comments

Comments

@nlu-clgx
Copy link

I have multiple configurations with different configuration IDs, and I'd like to be able to use the AutoLoginPartialRoutesGuard to authorize my application automatically. Currently, this route guard takes the top config and uses it, but I'd like to be able to configure it beforehand and select which configuration it should use for the route guard.

@arufolo
Copy link

arufolo commented Jan 26, 2023

I also just ran into a similar issue with the AuthInterceptor. The AuthInterceptor does not support multiple Open ID Configurations with the same HTTP Route. The AuthInterceptor always tried to use the first configuration with the matching route, regardless if an AccessToken exists for that configuration or not.

I feel like these two issues can be knocked out together

@FabianGosebrink FabianGosebrink self-assigned this Jan 28, 2023
@FabianGosebrink
Copy link
Collaborator

FabianGosebrink commented Jan 28, 2023

I have this on my list, this is a needed feature. Will build this as next thing I think. I do not know when this is ready. My plan is, that you can pass route data telling the guard, what config to take. Do not know if this works, but I will try it ;) Thanks.

@nlu-clgx
Copy link
Author

Thanks for your quick response! In the meantime, I'm just using a ConfigService and factory to decide which config should be used, but would gladly switch over to the new change once it is created!

@FabianGosebrink
Copy link
Collaborator

I think the guard is doable with route data. But for the interceptor you have to decide which access token you want to add or which config for which route. That conflicts with passing a configId for the interceptor, doesn't it?

@BittnerBarnabas
Copy link

My plan is, that you can pass route data telling the guard, what config to take.

Are you thinking of adding support for specifying multiple configs? It'd be super useful if we were able to auto log in for multiple configs.

Currently I'm doing a weird auth loop in the login component which calls authorize(configId) for all unauthenticated configs. The problem with this approach is that the login page won't redirect to the original route the user was trying to access when the AutoLoginPartialRoutesGuard intercepted the call.

@FabianGosebrink
Copy link
Collaborator

FabianGosebrink commented Feb 18, 2023

I saw that in my tests as well. I think about removing the ability or configuring the guard as such, that you can turn off the automatic redirection, then you can pass one configId to the guard which the guard will take then and I could return the saved route as part of the LoginResult so that you can redirect then by yourself. Does that make sense?

@BittnerBarnabas
Copy link

With this implementation wouldn't the guard be only able to auth with 1 config?

I was thinking something like this:

  1. AuthGuard configured with [config1, config2] for a route.
  2. User tries to access route
  3. Auth guard gets called
    3.1 (if no auth present) Save original route
    3.2 Auth with config1
    3.3 Auth with config2
    3.4 Redirect back to saved route

For my use case I need to get a separate access token for each service the UI calls.

@FabianGosebrink
Copy link
Collaborator

FabianGosebrink commented Feb 18, 2023

How should the guard decide which config to take if both have an access token, and you configure multiple ones? We can not do multiple logins because every login is a redirect to the IDP.

@BittnerBarnabas
Copy link

If both have an access token shouldn't the guard let the request go through?

If the guard can handle the login for 1 config (which requires redirects) what's the limiting factor to generalise it for n configs?

@FabianGosebrink
Copy link
Collaborator

Yes, if both have an AT then the guard would be good to go. But if both have none, we would redirect to IDP 1, you login, come back, guard kicks in, there is another non-authorized-config, you are getting redirected again, come back and then, when n configs are authenticated, redirect to the stored route? Have to think about this.

@BittnerBarnabas
Copy link

Yes, that's what I was thinking. The guard could be configured with n configs.

  1. It'd try to find any config which is unauthorized,
  2. if found
    2.1 do the current login "dance" then get redirected to the route
    2.2 which makes angular call the guard again. goto 1.)
  3. If not found let the request through

Every iteration you expect to find 1 less unauthorized configs so eventually you get to 3.)

Or if you're unauthorized the whole process breaks and it's the application's responsibility to implement the behavior to handle this case.

@FabianGosebrink
Copy link
Collaborator

I also just ran into a similar issue with the AuthInterceptor. The AuthInterceptor does not support multiple Open ID Configurations with the same HTTP Route. The AuthInterceptor always tried to use the first configuration with the matching route, regardless if an AccessToken exists for that configuration or not.

Thinking about that one as well. But I think these are two different issues. We could tell the interceptor which config to check with an HTTP-param. But this is another issue imho.

@BittnerBarnabas
Copy link

I agree. That's about selecting the right token to use, rather than getting the tokens automatically. Also it sounds complex enough to warrant its own issue.

@FabianGosebrink
Copy link
Collaborator

Can you provide (pseudo) code on how you want to use this feature of the guard as an api? I need to see where you want to put the configIds, how you would like to use it in your code. We also covered the case, if a user just wants to tell the guard to use one particular config, right? Thanks.

@BittnerBarnabas
Copy link

BittnerBarnabas commented Feb 18, 2023

I think this could be configured in the oidc config object.

For my specific use case, I think having the option to auto-login all of the configs is fine (I just implemented it in a hacky way by "overriding" getId() method in AutoLoginPartialRoutesGuard (I'm using V12)). -> let me know if you're interested I can share.

But thinking from a flexibility pov, we may need some more sophisticated matching either regex, or an arbitrary matcher function can be passed in.

idea for regex:

{
   configId: config1,
   autoLoginRouteMatch: "*" 
}, 
{ configId: config2, autoLoginRouteMatch: "$route2/subroute1" },
{ configId: config3, autoLoginRouteMatch: "$route2/" },

Would kick off auto login (if the guard is present in the router config) when a user visits mywebsite.com/route2 for config1 and config3.

My super simple hacky implementation (without being able to reference those 2 services :/ ):
Injectable({providedIn: 'root'})
export class AutoLoginPartialRoutesGuardV2 implements CanActivate, CanActivateChild, CanLoad {
    private authStateService: any
    private configurationProvider: any

    constructor(private delegate: AutoLoginPartialRoutesGuard) {
        //ts-ignore
        this.authStateService = delegate.authStateService
        //ts-ignore
        this.configurationProvider = delegate.configurationProvider
        //ts-ignore
        this.delegate.getId = this.getId
    }

    canLoad(route: Route, segments: UrlSegment[]): booLean {
        return this.delegate.canLoad(route, segments);
    }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
        return this.delegate.canActivate(route, state);
    }

    canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
        return this.delegate.canActivateChild(route, state);
    }

    private getId(): string {
        const unauthorisedConfigId = this.configurationProvider.getAllConfigurations()
            .find((c: OpenIdConfiguration) => !this.authStateService.areAuthStorageTokensValid(c.configId))
            ?.configId

        if (unauthorisedConfigId) {
            return unauthorisedConfigId
        }
        return this.configurationProvider.getOpenIDConfiguration().configId;
    }
}

@FabianGosebrink
Copy link
Collaborator

What about passing it in the route config as data?

export const ROUTES: Routes = [
  { path: '', component: HomeComponent },
  { 
    path: 'profile', 
    component: ProfileComponent, 
    canActivate: [AuthGuard] 
  },
  { 
    path: 'admin', 
    component: AdminComponent, 
    canActivate: [AuthGuard], 
    data: { 
      needsAuthConfigs: ['configId1', 'configId2']
    } 
  }
];

something like this?

@BittnerBarnabas
Copy link

This is nice as well, (didn't know you can pass in params here!).
I prefer this, above my initial suggestion, as long as there's a way to say "I want all configs".

@FabianGosebrink
Copy link
Collaborator

I am just thinking about what when you have multiple configs, and you provide non data in the route. Would you expect it to use the first one?

@BittnerBarnabas
Copy link

I think taking the first config if there are multiple ones configured is a bit unexpected.

The change could be so that it auto-logins all configs if there are no explicit configs listed. If I'm configuring multiple configs it's probably because I want them all to auto-login by default (or maybe not, lol).

Not sure though if this change would break people who use multiple configs but rely on the 1st one being selected for auto-login.

@FabianGosebrink
Copy link
Collaborator

We have to be careful here... But I totally see that when you put an AuthGuard on a Route you would want all configs to be authenticated and not just the first one...

@BittnerBarnabas
Copy link

Yeah, tbf I'm happy as long as I can say : use all configs. It doesn't have to be the default behavior, it can be a separate parameter or whatever fits best.

@FabianGosebrink
Copy link
Collaborator

FabianGosebrink commented Feb 18, 2023

Got you. To be frank as well: I just have to think like the users of our library do. You can think what fits you best, which is totally fine :) thanks for your inputs! That's really nice! Appreciate it. I think we can do it that way, and you pass the configs to the route. This should be a doable task. For backwards compatibility reasons, I will leave it: When you do not specify a config, the first one is going to be taken. Docs have to be updated accordingly. Now I need to find the time implementing it :D

@BittnerBarnabas
Copy link

Nice, thanks for the quick responses! Yeah I can imagine the issues with maintaining an os library it's no easy feat..

@FabianGosebrink
Copy link
Collaborator

This is why your feedback is so much appreciated. Thank you!!! 💖

@damienbod
Copy link
Owner

@FabianGosebrink Should we do something here?

@FabianGosebrink
Copy link
Collaborator

Yes this is something we can target, but I am short on time until June. Will have a look at this then.

@mnmpeterson
Copy link

In my case I need it to attempt to login using one config (for SSO). If that fails, try again using a second config (prompts for credentials). Not sure how to do this even writing my own route guard.

@Bhushan1808
Copy link

Bhushan1808 commented Jun 5, 2024

@FabianGosebrink Do we any update on this feature?

@Bhushan1808
Copy link

Thanks for your quick response! In the meantime, I'm just using a ConfigService and factory to decide which config should be used, but would gladly switch over to the new change once it is created!

Hi there, I am also running into similar type of problem. Can i get an context how are you handling such problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants