pow-auth / pow_assent

Multi-provider authentication for your Pow enabled app
https://powauth.com
MIT License
318 stars 50 forks source link

Add a a way to use the same provider strat more than once with different client ids. #204

Open mitcheaton1 opened 3 years ago

mitcheaton1 commented 3 years ago

We use the google oauth strategy for web, and for ios, but the client ids are different for each, and no secret is needed for ios. We used two different provider ids [google, google_ios] in our config to differentiate how to store the secrets for client id and secret which come form env vars.

The issue is PowAssent.Plug.callback_upsert tries to insert a new user in the database schema if the provider is different, and it fails as the user already exists as we have a unique constraint on email.

the call @spec callback_upsert(Conn.t(), binary(), map(), binary()) calls another method maybe_authenticate() which calls authenticate in the same module that takes the provider to see if a user exists.

I forked and temp fixed this by just converting the provider var to be google if provider comes in as google_ios and it works like a charm. not sure how a permanent fix would look to use 2 providers with the same strat, without breaking PowAssent.Plug.callback_upsert

The error was user already exists with this email

danschultzer commented 3 years ago

If I understand this correctly, the only difference in with the two strategies is the client id (and no client secret for ios)? You use PowAssent in the API with the ios config, and in web with the web config?

In that case, it's best handled by dynamically updating the client id depending if the API or the web interface is accessed. You can use PowAssent.Plug.merge_provider_config/3 helper for this, and handle it in a plug:

# config/prod.exs
config :my_app_web, google_ios_client_id: "GOOGLE_CLIENT_ID"

# my_app_web/router.ex

  pipeline :api do
    # ...
    plug :put_google_ios_client_id
  end

  defp put_google_ios_client_id(conn, _opts) do
    PowAssent.Plug.merge_provider_config(:google, client_id: Application.get_env(:my_app_web, :google_ios_client_id), client_secret: nil)
  end

Above is untested, but should give you an idea of how you just need to adjust the config when the request comes in.

mitcheaton1 commented 3 years ago

we will give this a shot. thanks dan!

sherbondy commented 2 years ago

Hey @danschultzer happy I happened across this issue.

I'm working on adding "incremental auth" support for a Pow Assent powered web application, and figure merge_provider_config would also be the recommended path for achieving such a thing?

Eg the scenario in mind is:

In this case I was initially scratching my head looking for a way to dynamically change the config for the Google provider, similar to this issue.

Would you be keen if I wrote up a quick community guides PR for the docs to elaborate on this? (name TBD, maybe something like "Supporting Incremental Authorization and other dynamic per-request configuration" re: how to effectively use merge_provider_config ?)

danschultzer commented 2 years ago

I'm working on adding "incremental auth" support for a Pow Assent powered web application, and figure merge_provider_config would also be the recommended path for achieving such a thing?

Yeah it should work well here. You could probably pass a query param with scopes to the auth route and set up a plug to pull them into the strategy config. Unless you have a custom auth controller, then it would be cleaner to just add it to the controller.

Would you be keen if I wrote up a quick community guides PR for the docs to elaborate on this?

I would be very happy to see that, thanks!

For the title, it could just be called something simple like Dynamic Strategy Configuration that starts out by explaining how the config can be updated dynamically, and then go into a practical example like the incremental authorization.