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

Add support for known providers to OIDC #22176

Closed

Conversation

stuartwdouglas
Copy link
Member

This adds specifc config to enable OIDC login for the main OIDC
providers. Having explicit config options like this makes it easy to
search the documentation/dev UI for it.

Fixes #20783

This adds specifc config to enable OIDC login for the main OIDC
providers. Having explicit config options like this makes it easy to
search the documentation/dev UI for it.

Fixes quarkusio#20783
ret.credentials.secret = Optional.of(config.provider.microsoft.get().secret);
ret.authServerUrl = Optional.of("https://login.microsoftonline.com/common/v2.0");
//TODO: do we want to hard code this?
ret.applicationType = OidcTenantConfig.ApplicationType.HYBRID;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, not sure, I'd have to test it on my app to see if it works.

ret.authentication.scopes = Optional.of(config.provider.github.get().scopes);
ret.authentication.userInfoRequired = true;
ret.authentication.setIdTokenRequired(false);
ret.authentication.setRedirectPath("/Login/githubLoginSuccess");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think those links are too specific to my app. But in practice:

  • google/apple/ms works on a single endpoint without requiring UserInfo: everything is in the ID Token
  • github must be handled specially because UserInfo contains the user ID, name and user name, but we have to hit a second endpoint /user/emails to get the user's email.
  • facebook must be handled specially because UserInfo contains the user ID, name and email

Perhaps a better endpoint default would be: /{provider}-oidc-login-success?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naturally, a further layer of abstraction which knows how to fetch the damn fields from all providers and present them to the Quarkus user would be even better: then I only need a single success endpoint :)
But that can wait for another issue.

@sberyozkin
Copy link
Member

Hi @stuartwdouglas Thanks for taking on this task. I'd like to clarify first, how will the configuration look like ?

For example, a user should be able to override any property in the github provider block.
I've been thinking earlier to have something like this:

#defaul tenant, Github
quarkus.oidc.provider=github
#override the scope
quarkus.oidc.authentication.scope=user:something

#facebook tenant

quarkus.oidc.facebook.provider=facebook
#override the scope
quarkus.oidc.facebook.authentication.scope=user:something

I think for this to work OidcTenantConfig should have a copy constructor the outer OidcTenantConfig is initialized from the github or facebook

* Config for well known OIDC providers
*/
@ConfigItem
public ProvidersConfig provider;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it has to be decoupled from OidcTenantConfig, it has to be part of OidcTenantConfig, tenant specific as needed, or rather, IMHO, something like quarkus.oidc.provider=github should point to an instance of OidcTenantConfig, merged with the OidcTenantConfig referencing it, please see below

Copy link
Member

@sberyozkin sberyozkin Dec 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, just realized, this way won't work with the dynamic TenantConfigResolver - one should be able to do, instead of defining this config statically in application.properties,

public class MyTenantConfigResolver implements TenantConfigResolver {
    public Uni<OidcTenantConfig> resolve(RoutingContext rc) 
          {
             OidcTenantConfig github = getDefaultOidcTenantGithubBlock();
             // overrride:
             github.getAuthentication().setScope("user:something");
             return github;
          }
    }
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stuartwdouglas Stuart, how about having an OidcTenantConfig provider enum property with (github, etc, values).

This will allow OidcRecorder, when going over every OidcTenantConfig, pick up a referenced well-known provider OidcTenantConfig instance and merge with the current OidcTenantConfig - it will address all the concerns I've expressed in various comments here

for (var e : knownProviders.entrySet()) {
var provider = e.getValue().get();
if (provider.isPresent()) {
tenants.put(e.getKey(), provider.get());
Copy link
Member

@sberyozkin sberyozkin Dec 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, but it is not correct - it creates a dedicated tenant per every social provider config, but it won't allow to override a specific property using OidcTenantConfig - with this approach we'd end up moving a good portion of OidcTenantConfig properties under the github and other provider specific blocks. I'd say it is a problem.
I understand that GitHub and other config groups you have added are meant to customize - but it creates a parallel stream for configuring things.

But also equally important, it does not allow to vary with the provider, each OIDC tenant allows to have:

quarkus.oidc.github1.provider=github
quarkus.oidc.github1.authentication.scopes=a

quarkus.oidc.github2.provider=github
quarkus.oidc.github2.authentication.scopes=b

It may not be something one would want to do in production but for tests it is very useful.

Copy link
Member

@sberyozkin sberyozkin Dec 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, so continuing from the earlier comment, so you have Map<String, Supplier<Optional<OidcTenantConfig>>> knownProviders.
But instead of creating tenants as you have typed here, the following will be more aligned with the OIDC tenancy:

if (oidcTenantConfig.provider.isPresent()) {
    // Merge - this ensures the github/etc properties are overridable  
    OidcTenantConfig thisIsCurrentTenantConfig = new OidcTenantConfig(knownProviders.get(oidcTenantConfig.provider.get()));
    // continue dealing with this tenant config
}

import io.quarkus.runtime.annotations.ConfigItem;

@ConfigGroup
public class GitHub {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO, GitHub (and other providers) groups should extend OidcTenantConfig and override properties with GitHub/etc specific properties

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

client-id, client-secret, scopes - these are all available in OidcTenantConfig, this is my earlier concern, it is not worth IMHO to start mirroring some of those properties here. By extending OidcTenantConfig they can be set as needed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I now think that these groups have to be removed, IMHO, the supplied OidcTenantConfig per every provider should be enough

ret.credentials.secret = Optional.of(config.provider.github.get().secret);
ret.authServerUrl = Optional.of("https://github.com/login/oauth");
//TODO: do we want to hard code this?
ret.applicationType = OidcTenantConfig.ApplicationType.HYBRID;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HYBRID won't work for GitHub - quarkus-oidc service apps can not handle opaque tokens from providers like GitHub since it has no introspection endpoint - I've opened an issue to make sure a userinfo check is enough

ret.authentication.scopes = Optional.of(config.provider.github.get().scopes);
ret.authentication.userInfoRequired = true;
ret.authentication.setIdTokenRequired(false);
ret.authentication.setRedirectPath("/Login/githubLoginSuccess");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it has to be an application specific property

Copy link
Contributor

@pedroigor pedroigor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also agree with @sberyozkin that we could rely on the properties already available from OidcTenantConfig. But looks like there are some improvements on documentation and dev UI.

I would also add a hook for each provider so that the authorization/authentication response could be processed accordingly to the particularities of providers. For instance, github might require calling additional endpoints to fetch info about the subject, etc. Perhaps it could also avoid simplifying the code a bit by not introducing provider-specific behavior into the main code path. Another example is Apple ID, which requires setting up a JWT to authenticate.

@sberyozkin
Copy link
Member

sberyozkin commented Dec 14, 2021

Hi @pedroigor

Another example is Apple ID, which requires setting up a JWT to authenticate.

@FroMage is on it, the existing JWT gen code will be reused - but will be sent as a client post secret value.

Providing plugin points for provider specific behaviors is a nice idea, I propose though to deal with it separately as this PR is complex in its own way :-)

@sberyozkin
Copy link
Member

sberyozkin commented Dec 24, 2021

Hi Stuart, I did a quick prototype based on my comments, https://github.com/sberyozkin/quarkus/pull/new/well_known_providers.

It will allow the following variations:

  1. Default tenant:
quarkus.oidc.provider=github
quarkus.oidc.client-id=myclientid
//etc
  1. Single custom tenant which may read better
quarkus.oidc.github.provider=github
quarkus.oidc.github.client-id=myclientid
//etc
  1. More than one tenant
quarkus.oidc.github.provider=github
quarkus.oidc.github.client-id=myclientid


quarkus.oidc.apple.provider=apple
quarkus.oidc.apple.client-id=myclientid
//etc

Note especially in the last case - the client authentication requirements may differ (with Apple it would be a fairly complex JWT auth setup) which is what I implied that we'd need to mirror OidcTenantConfig in ProviderConfig in the current PR.

It works in application.properties and dynamically with TenantConfigResolver.

Hope it looks reasonable, I'll try to complete it soon enough, the only real complexity is copying OidcTenantConfig :-)

@gsmet
Copy link
Member

gsmet commented Jan 10, 2022

This one has been superseded by #22572.

@gsmet gsmet closed this Jan 10, 2022
@quarkus-bot quarkus-bot bot added the triage/invalid This doesn't seem right label Jan 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/oidc triage/invalid This doesn't seem right
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support compact Social Provider configuration in OIDC
5 participants