weavejester / ring-oauth2

OAuth 2.0 client middleware for Ring
147 stars 38 forks source link

Support for dynamic authorization URL params #41

Open sbauch opened 3 years ago

sbauch commented 3 years ago

Some OAuth providers accept additional URL query params as part of the OAuth authorization URL.

Some examples

• Twitter supports screen_name and force_login parameters - https://developer.twitter.com/en/docs/authentication/api-reference/authorize

• Google supports an hd parameter to restrict logins to users with that domain, among some other non-standard params - https://developers.google.com/identity/protocols/oauth2/openid-connect#sendauthrequest

Some of these, like force_login, can be hardcoded in user space when using this middleware.

But it doesn't seem like one can make a POST request to the launch_uri with additional, dynamic params such that they are passed to the provider's authorization URL.

Would the maintainers here be interested in a PR that adds this functionality, or be willing to add it themselves? I can take a stab, but I'm very new to Clojure.

weavejester commented 3 years ago

If the additional parameters are added to the URL, they aren't parsed by the providers?

sbauch commented 3 years ago

Sorry, I'm not sure I follow.

For things like force_login, I can hardcode that to the map of options for a provider, and those are maintained and passed to the authorization URL when I POST to /auth/twitter i.e.

(def handler
  (wrap-oauth2
   routes
   {:twitter
    {:authorize-uri    "https://api.twitter.com/oauth/authorize?force_login=true"
    ...
    }}))

But if I wanted to use the google hd parameter dynamically, perhaps by having a form for domain name, if I POST with x-www-form-urlencoded parameters, the hd parameter doesn't get passed to the Google authorize URL.

I'm honestly pretty far out of my element, just wrote my first clojure code last week, largely based on the example here.

To be transparent, I'm just wanting to put together some docs for an OAuth service I run to better support Clojure developers who are integrating us.

I've got an example repo here - https://github.com/enterprise-oss/osso-clojure-example. Our service supports passing an email or domain param, so I'm wanting to collect that from a user in the clojure app and send it as a query param to our authorize URL.

I can hardcode these things fine, which works in like a single tenant type of situation. But I want to be able to POST a form to the launch_uri and have this middleware then pass the form params on to the OAuth service's authorize URL.

weavejester commented 3 years ago

Ah, I see what you mean. What about this: we change the launch handler so that if the :launch-uri handler is called with query parameters, those parameters are passed onto the :authorize-uri.

sbauch commented 3 years ago

Yes that sounds like exactly what I need!

I do wonder if there's value in sanitizing or allow-listing the parameters. For instance, Im including the __auth-verify-token param as part of my form, maybe the defaults middleware already pulls that out of the params that hit the handler, but I probably wouldn't want to include that in the redirect to the authorize URL.

Should I take a pass at this? Feels like we would need to pull the params out of the request here and then merge them with the map of values being encoded - https://github.com/weavejester/ring-oauth2/blob/master/src/ring/middleware/oauth2.clj#L19-L26

weavejester commented 3 years ago

Yep, that's what I had in mind, and you can certainly take a go at it if you want.

Kah0ona commented 3 years ago

I think it would be useful to make it even more dynamic; allow the :authorize-uri not only be a string, but a (fn [profile request state] ..) that returns the URI.

I have a use case to integrate with the Shopify oAuth API, and it requires the following uri: https://{shop}.myshopify.com/admin/oauth/authorize?client_id={api_key}&scope={scopes}&redirect_uri={redirect_uri}&state={nonce}&grant_options[]={access_mode}

where the {shop} subdomain is only available at runtime (by checking my logged in user, and its settings, to get the {shop} subdomain from its settings).

The implementation could be quite short, just modifying this function, to see if :authorize-uri in the profile is a fn?.

Would that make sense as an implementation?

wavejumper commented 1 year ago

Hi all,

We've just hit this exact problem with Azure AD (v1) where a resource param is required when making an authorization reequest.

Either @Kah0ona or @sbauch solution looks good for us. It looks like #42 was ready to be merged (pending squashed commits), but abandoned by author.

Happy to dedicate some time in whatever way to get a resolution :)