Most OAuth2 client registrations use a centralized provider. For example, Facebook always has an authorization URI of https://www.facebook.com/v2.8/dialog/oauth.
Now imagine the hypothetical situation where Facebook becomes decentralized and allows people to run instances of its service on their own servers. These servers may only be known at runtime. How could this service be consumed using OAuth2 client?
For example, in the centralized model the authorization request is triggered by /oauth2/authorization/facebook. The single client registration for the id facebook is obtained from the ClientRegistrationRepository and the DefaultOAuth2AuthorizationRequestResolver initiates the authorization request.
In the decentralized model we need to additionally communicate which server to use. For example, if I spun up Facebook on https://www.myfacebook.com/, how would authorization proceed?
One approach would be to keep a single facebook client registration but pass the provider to the authorization request, e.g. /oauth2/authorization/facebook?provider=https%3A%2F%2Fwww.myfacebook.com%2F. This would still allow DefaultOAuth2AuthorizationRequestResolver to match the request and perform the authorization process. Although we would need to provide an authorizationRequestCustomizer to modify the authorization request to use the provider's authorization endpoint. We become unstuck here since the customizer doesn't have access to custom HTTP request parameters like provider.
Another approach would to embed the provider details into the client registration id, e.g. /oauth2/authorization/facebook-https%3A%2F%2Fwww.myfacebook.com%2F (ignoring the problematic encoding for now). We could then supply a custom ClientRegistrationRepository that extracted the provider from the registration id and dynamically built a client registration with the provider-specific endpoints. This is quite a hack though given that horrible authorization URL.
Perhaps a combination of the two could be the way to go? Supplying the provider to the authorization URL seems reasonable, but it would then require a mechanism to find client registrations by registration id and provider. ClientRegistrationRepository could be enhanced to consider more than registration id, but no doubt this has bigger ramifications.
Have you encountered this use-case before? I'd be interested in any approaches that don't involve
using a completely custom OAuth2AuthorizationRequestResolver.
Comment From: jgrandja
Thanks for getting in touch @markhobson, but it feels like this is a question that would be better suited to Stack Overflow. We prefer to use GitHub issues only for bugs and enhancements. Feel free to update this issue with a link to the re-posted question (so that other people can find it).
Having said that, I would consider implementing a custom ClientRegistrationRepository and registering it as a @Bean. If you don't know the provider specific protocol endpoints at configuration time and need to resolve it at runtime, then a custom ClientRegistrationRepository @Bean could resolve the provider details and then create/configure the internal ClientRegistration's. See this example for explicitly configuring a ClientRegistrationRepository.
Hope this helps?
Comment From: markhobson
Thanks for the reply @jgrandja. I was torn between asking here or on Stack Overflow, apologies for the noise.
I'm happy with implementing a custom ClientRegistrationRepository, it's more the restriction of keying ClientRegistrations by a single registrationId that's causing the issue. Continuing the above example this leads to N client registrations of id facebook. In a decentralized world the natural key for client registrations would be (registrationId, providerUrl).
Anyhow, I'll collate my thoughts and post a clearer question on Stack Overflow if I struggle to achieve this. Thanks for your time.
Comment From: jgrandja
@markhobson The registrationId is simply a logical unique identifier. You could name it anything you want, including (registrationId, providerUrl), as long as it's unique within the ClientRegistrationRepository.
Comment From: markhobson
@jgrandja Sure, my fear with this approach was how DefaultOAuth2AuthorizationRequestResolver uses this compound registrationId to match URLs. I'll continue down this path but it felt a bit inappropriate.