Code Monkey home page Code Monkey logo

ueberauth's Introduction

Überauth

Build Status Codecov Inline docs Hex Version Hex docs Total Download License Last Updated

An Elixir Authentication System for Plug-based Web Applications

Ueberauth is a two-phase authentication framework that provides a clear API - allowing for many strategies to be created and shared within the community. It is heavily inspired by Omniauth. You could call it a port but it is significantly different in operation - but almost the same concept. Huge hat tip to Intridea.

Ueberauth provides only the initial authentication challenge, (initial OAuth flow, collecting the information from a login form, etc). It does not authenticate each request, that's up to your application. You could issue a token or put the result into a session for your applications needs. Libraries like Guardian can help you with that aspect of authentication.

The two phases are request and callback. These phases are implemented by Strategies.

Strategies

Strategies are plugs that decorate or intercept requests (or both).

Strategies implement the two phases and then may allow the request to flow through to your downstream plugs. Implementing the request and callback phases is optional depending on the strategies requirements. If a strategy does not redirect, the request will be decorated with Ueberauth information and allowed to carry on through the pipeline.

See the full list of the strategies on the Wiki.

Request Phase

The request phase is where you request information about the user. This could be a redirect to an OAuth2 authorization url or a form for collecting username and password. The request phase is concerned with only the collection of information. When a request comes in on the request phase url the relevant strategy will receive the handle_request! call.

In some cases (default) the application using Ueberauth is responsible for implementing the request phase. That is, you should set up a route to receive the request phase and provide a form etc. In some cases, like OAuth, the request phase is used to redirect your user to a 3rd party site to fulfill the request.

For example, an OAuth strategy for GitHub will receive the request phase url and stop the request, redirecting you to GitHub’s OAuth challenge url with some query parameters. Once you complete the GitHub OAuth flow, the user will be redirected back to the host site to the callback URL.

Another example is simple email/password authentication. A request is made by the client to the request phase path and the host application displays a form. The strategy will likely not do anything with the incoming handle_request! request and simply pass through to the application. Once the form is completed, the POST should go to the callback url where it is handled (passwords checked, users created / authenticated).

Callback Phase

The callback phase is where the fun happens. Once a successful request phase has been completed, the request phase provider (OAuth provider or host site, etc) should call the callback URL. The strategy will intercept the request via the handle_callback!. If successful, it should prepare the connection so the Ueberauth.Auth struct can be created, or set errors to indicate a failure.

See Ueberauth.Strategy for more information on constructing the Ueberauth.Auth struct.

Looking for an example? Take a look ueberauth/ueberauth_example.

Setup

Add the dependency

# mix.exs

defp deps do
  # Add the dependency
  [{:ueberauth, "~> 0.10"}]
end

Fetch the dependencies

mix deps.get

Configuring providers

In your configuration file (config/config.exs) provide a list of the providers you intend to use. For example:

config :ueberauth, Ueberauth,
  providers: [
    facebook: { Ueberauth.Strategy.Facebook, [ opt1: "value", opts2: "value" ] },
    github: { Ueberauth.Strategy.Github, [ opt1: "value", opts2: "value" ] }
  ]

This will define two providers for you. The general structure of the providers value is:

config :ueberauth, Ueberauth,
  providers: [
    <provider name>: { <Strategy Module>, [ <strategy options> ] }
  ]

We use the configuration options for defining these to allow for dependency injection in different environments. The provider name will be used to construct request and response paths (by default) but will also be returned in the Ueberauth.Auth struct as the provider field.

Once you've setup your providers, in your router you need to configure the plug to run. The plug should run before your application routes.

In phoenix, plug this module in your controller:

defmodule MyApp.AuthController do
  use MyApp.Web, :controller
  plug Ueberauth
  ...
end

Its URL matching is done via pattern matching rather than explicit runtime checks so your strategies will only fire for relevant requests.

Now that you have this, your strategies will intercept relevant requests for each strategy for both request and callback phases. The default urls are (for our Facebook & GitHub example)

# Request phase paths
/auth/facebook
/auth/github

# Callback phase paths
/auth/facebook/callback
/auth/github/callback

Customizing Paths

These paths can be configured on a per strategy basis by setting options on the provider.

Note: These paths are absolute

config :ueberauth, Ueberauth,
  base_path: "/login", # default is "/auth"
  providers: [
    identity: {Ueberauth.Strategies.Identity, [request_path: "/login/identity",
                                               callback_path: "/login/identity/callback"]}
  ]

Customizing JSON Serializer

Your JSON serializer can be configured depending on what you have installed in your application. Defaults to Jason.

config :ueberauth, Ueberauth,
  json_library: Poison # default is Jason

HTTP Methods

By default, all callback URLs are only available via the "GET" method. You can override this via options to your strategy.

providers: [
  identity: {Ueberauth.Strategies.Identity, [callback_methods: ["POST"]]}
]

Strategy Options

All options that are passed into your strategy are available at runtime to modify the behaviour of the strategy.

Copyright and License

Copyright (c) 2015 Sonny Scroggin

Released under the MIT License, which can be found in the repository in LICENSE.

ueberauth's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ueberauth's Issues

"Could not start application uberauth: could not find application file: uberauth.app"

I'm starting a brand new Phoenix 1.3 project, and am trying to get Uberauth up and running. I've followed the steps, but the part about adding to :applications in mix.exs is tripping me up. If I do this, I see the following error:

** (Mix) Could not start application uberauth: could not find application file: uberauth.app

This same problem happens if I add to :extra_applications instead.

plug version 1.2

This version of plug is quite old....

Is there much involved to update it or make various versions optional ?

Gitlab Strategy Integration

Hey,

Not sure if this is the best place for this but I made a Gitlab OAuth2 Strategy and tested it out using the Example App. How do new strategies normally get consumed by the Üeberauth github org? I wouldn't mind transfering ownership and becoming a collaborator to help maintain the strategy.

If there is a better place to discuss please let me know. Thanks.

Token-based workflow

As a user, I should be able to initialize the authentication workflow using a token.

SPAs, and mobile applications initialize the authentication workflow in the client. The workflow will give you the authentication token that you should be able to use in the backend for getting the user data as the second part of the Ueberauth workflow is doing today.

Proposal

The Ueberauth API should allow the user to initialize the second step of the workflow.

Notes

It shouldn't rely on Plug.Conn since applications with GraphQL or any other non-HTTP based protocol wouldn't be able to use it.

Related to #20 #50 please read more details from those threads.

Multiple paths per strategy no longer works

I don't know if this was previously intentional behaviour but with v0.2.0 it was possible to configure a single provider multiple times to allow authentication at different paths:

config :ueberauth, Ueberauth,
  providers: [
    facebook: {Ueberauth.Strategy.Facebook, [
      request_path: "/auth/facebook",
      callback_path: "/auth/facebook/callback"
    ]},
    facebook: {Ueberauth.Strategy.Facebook, [
      request_path: "/signup/facebook",
      callback_path: "/signup/facebook/callback"
    ]}
  ]

Updating to v0.3.0, this no longer works. The provider defined first appears to be overridden by the latter. This results in an UndefinedFunctionError when accessing /auth/facebook, but /signup/facebook works as expected.

So as I said, I'm not sure if this was previously an intentional design choice, making this new behaviour a regression or if the config above worked in v0.2.0 purely by chance. Either way, I have found it to be important in order to logically separate different parts of the application. The signup flow can be very different to that of logging in and so it makes sense to handle them separately.

Looking at the commit history, it appears this change in behaviour was introduced at 22e0728. Specifically on line 179 of lib/ueberauth.ex, where the use of all_providers = Enum.into(all_providers, %{}) removes all duplicate keys from the keyword list of providers by conversion to a map. (22e0728#diff-3f81f8ab5bb0951f0fa5db7604b4b9b3R179).

It looks like this is done so that Map.split/2 can be used (22e0728#diff-3f81f8ab5bb0951f0fa5db7604b4b9b3R184) but in this case, Keyword.split/2 accomplishes the same thing while preserving the duplicate keys.

What are your thoughts on this?

Request phase and callback phase

Hi, i really liked ueberauth strategies feature. I also checkout the sample example and it works fine. I am not able to customize the code though. I am having problem understanding whether how should i implement the request method as well as callback function. I could not find any documentation for same too. Do we need to manually call the outh url of the third app or does ueberauth does that for me?

How to submit a new Ueberauth Plugin?

Hi there,

I've just written an Ueberauth plugin for Dwolla authentication and I'm just wondering what the protocol is to get it included under the Ueberauth github organization.

I will agree to maintain the project overtime, so it won't be something you guys have to take care of.

Just let me know what next steps I should take.

Thanks!

Add a redirect_uri function so that the redirect_uri is configurable

Hi there! Currently I'm using only the callback functionality of ueberauth (specifically Facebook and Google). My client grabs the access code and then sends it to my API in order to retrieve the token. On dev I'm running my API and client on separate ports, so to support how most ueberauth's implement handle_callback by setting the redirect_uri as so: opts = [redirect_uri: callback_url(conn)], I need a reverse proxy.

Adding a a helper function redirect_uri that grabs either the uri from the config or callback_url for oauth implementations to use would be really helpful for scenarios like this.

What do you think?

Possible to rerequest if permission was denied?

For example, Facebook OAuth lets users choose not to share requested permissions and this leads to failures. Is there a way to rerequest if a permission was denied?

I'd like to rerequest Facebook OAuth if an email was not given.

The "Intercept requests(#28)" is a glitch for Facebook login

As of String.replace_trailing in "Intercept requests with and without a trailing slash(#28)", Facebook login doesn't work correctly.

Due to the replacing, conn, which will be passed to the callbacks(like following) don't include ueberauth_auth in assigns at all.

def facebook_callback(%{ assigns: %{ ueberauth_failure: _fails } } = conn, params) do  

It works correctly without the code in lib/ueberauth.ex, like below:

#if strategy = Map.get(opts, String.replace_trailing(request_path, "/", "")) do
if strategy = Map.get(opts, request_path) do

Cannot get a correct callback_url for a forwarded request

After I tried to forward the request to my controller handing the auth logic as below,

scope "/", Example do
    forward "/auth", AuthController
end

In the AuthController, I wouldn't be able to get a correct callback_url with the \auth prepended.

I think the reason is that when generating the callback_url, the script_name in Plug.Conn doesn't get considered.

URL Resolution - Scheme, Host, Port

I'm working through deploying my first Phoenix app and ran into issues with the URL's used for the different strategies. This specific app has nginx sitting in front of it (I resolved the issues by forwarding across x-forwarded-proto and x-forwarded-for).

I didn't want to use the ueberauth configuration of overriding the callback url because I'm using quite a few strategies and didn't want to have lots of duplication around. I had added configuration to prod.exs as: url: [scheme: "https", host: "sitename.com", port: 443],

But the different ueberauth strategies don't seem to use those settings.

It looks like Ueberauth uses:

defp full_url(conn, path, opts) do
  scheme = conn
  |> forwarded_proto
  |> coalesce(conn.scheme)
  |> normalize_scheme

  %URI{
    host: conn.host,
    port: normalize_port(scheme, conn.port),
    path: path,
    query: encode_query(opts),
    scheme: to_string(scheme),
  }
  |> to_string
end

Whereas Phoenix uses the following (from https://github.com/phoenixframework/phoenix/blob/10b6c1e8df915e82a4040f14f94afc1aed8ab47a/lib/phoenix/endpoint/supervisor.ex#L235-L255):

defp build_url(endpoint, url) do
  build_url(endpoint.config(:https), endpoint.config(:http), url)
end

defp build_url(https, http, url) do
  {scheme, port} =
    cond do
      https ->
        {"https", https[:port]}
      http ->
        {"http", http[:port]}
      true ->
        {"http", 80}
    end

  scheme = url[:scheme] || scheme
  host   = host_to_binary(url[:host] || "localhost")
  port   = port_to_integer(url[:port] || port)

  %URI{scheme: scheme, port: port, host: host}
end

I'm curious to know the decisions around using something different than Phoenix. I believe Uberauth could be used outside of Phoenix and this might be the reason, but I would favor this consistency. Having called this out in the README would have been great too (maybe it is already in the docs someplace and I didn't read enough).

Allow to add extra params to callback_url

Would be good to have ability to pass extra params to callback_url. For example users can login and register, can register for different roles - user, advertiser, whoever.

If I could build callback_url from current conn params, e.g. /auth/facebook?param1=val1&param2=val2
I could use these params in callback_phase to define what to do after successful sign in.

Most of strategies rely on callback_url function from Helpers module
|> Keyword.put(:redirect_uri, callback_url(conn, [no params here]))
FB Instagram VK Twitter

So I guess it's possible to add whitelist of params to config for callback_url. And then fetch this params from conn and automatically add to callback_url.

Maybe there's another approach for this. Appreciate any feedback.

Submit empty login/registration throws error

[error] #PID<0.524.0> running PhoenixGuardian.Endpoint terminated
Server: localhost:4000 (http)
Request: POST /auth/identity/callback
** (exit) an exception was raised:
    ** (ArgumentError) nil given for :uid. Comparison with nil is forbidden as it is unsafe. Instead write a query with is_nil/1, for example: is_nil(s.uid)
        (ecto) lib/ecto/query/builder/filter.ex:107: Ecto.Query.Builder.Filter.runtime!/6
        (ecto) lib/ecto/query/builder/filter.ex:98: Ecto.Query.Builder.Filter.runtime!/2
        (ecto) lib/ecto/repo/queryable.ex:304: Ecto.Repo.Queryable.query_for_get_by/3
        (ecto) lib/ecto/repo/queryable.ex:52: Ecto.Repo.Queryable.get_by/5
        (phoenix_guardian) web/auth/user_from_auth.ex:117: PhoenixGuardian.UserFromAuth.auth_and_validate/2
        (phoenix_guardian) web/auth/user_from_auth.ex:7: PhoenixGuardian.UserFromAuth.get_or_insert/3
        (phoenix_guardian) web/controllers/auth_controller.ex:24: PhoenixGuardian.AuthController.callback/4
        (phoenix_guardian) web/controllers/auth_controller.ex:1: PhoenixGuardian.AuthController.action/2
        (phoenix_guardian) web/controllers/auth_controller.ex:1: PhoenixGuardian.AuthController.phoenix_controller_pipeline/2
        (phoenix_guardian) lib/phoenix/router.ex:261: PhoenixGuardian.Router.dispatch/2
        (phoenix_guardian) web/router.ex:1: PhoenixGuardian.Router.do_call/2
        (phoenix_guardian) lib/phoenix_guardian/endpoint.ex:1: PhoenixGuardian.Endpoint.phoenix_pipeline/1
        (phoenix_guardian) lib/plug/debugger.ex:122: PhoenixGuardian.Endpoint."call (overridable 3)"/2
        (phoenix_guardian) lib/phoenix/endpoint/render_errors.ex:34: PhoenixGuardian.Endpoint.call/2
        (plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
        (cowboy) src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4

Consider using a normal plug, with less code generation

Ideally, you wouldn't need to break from the regular plug api, so instead of this:

Ueberauth.plug "/auth"

You could do

plug Ueberauth, strategy: MyStrategy

or

plug Mystrategy

As it is today, calling Ueberauth.plug generates an external module from the caller, which is a sign we can probably find a better way, such as having the user define an explicit module that use UeberAuth.Strategy, then plug that. Thoughts?

Compilation error in my controller after adding uederauth

I checked every thing four or five times. config.exs, router.ex and controller.ex look fine
== Compilation error in file web/controllers/auth_controller.ex ==
** (FunctionClauseError) no function clause matching in Keyword.pop/3

The following arguments were given to Keyword.pop/3:

    # 1
    nil

    # 2
    :base_path

    # 3
    "/auth"

Attempted function clauses (showing 1 out of 1):

    def pop(keywords, key, default) when is_list(keywords)

(elixir) lib/keyword.ex:964: Keyword.pop/3
lib/ueberauth.ex:177: Ueberauth.init/1
(plug) lib/plug/builder.ex:302: Plug.Builder.init_module_plug/4
(plug) lib/plug/builder.ex:286: anonymous fn/5 in Plug.Builder.compile/3
(elixir) lib/enum.ex:1925: Enum."-reduce/3-lists^foldl/2-0-"/3
(plug) lib/plug/builder.ex:284: Plug.Builder.compile/3
(phoenix) expanding macro: Phoenix.Controller.Pipeline.__before_compile__/1
web/controllers/auth_controller.ex:1: Discuss.AuthController (module)
(elixir) lib/kernel/parallel_compiler.ex:206: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/6

OneLogin

Does ueberauth support onelogin?

Api Login

is there any way to implement this using api ? if i wanna use it to authenticate token from mobile device ?

Invalid plug dependency - unable to use with phoenix 1.4.1

The plug dependency is defined as ~> 1.5.0. It is a wrong pattern to use such notation when referring to the library with the semantic versioning. It should be ~> 1.5 instead, as we can rely on the fact that unless major number change, the newer versions should be backwards compatible. Current approach is too strict.

Current approach disallows using ueberauth with phoenix 1.4.1.

$ mix deps.get
Resolving Hex dependencies...

Failed to use "plug" (version 1.7.2) because
  phoenix (version 1.4.1) requires ~> 1.7
  phoenix_ecto (version 4.0.0) requires ~> 1.0
  phoenix_html (version 2.13.1) requires ~> 1.5
  plug_cowboy (version 2.0.1) requires ~> 1.7
  ueberauth (version 0.6.0) requires ~> 1.5.0
  mix.lock specifies 1.7.2

Allow Config Option for Custom URLs

Currently there is an option for adding custom paths but in certain cases there is a need to be able to use a custom URL instead of a custom path.

writing a custom strategy

I'm working on a project with the need for server-side social-auth. Your library seems pretty good, but there's a strategy missing. I would like to use https://vk.com/ OAuth (the biggest SN in Russia), but it is not in the list.

What do you think, is it worth implementing? Do you have any guidelines how to contribute whole new strategy?

Thanks!

Differenciate by method and path in Ueberauth plug

I'm using the identity strategy with ueberauth and (just for that strategy) I'd like to use the same path as request_path and callback_path. Because the first is accessed via a get request and the second via a post request this did seem to work well initially, but the request_path does not trigger the Ueberauth plug anymore. The callback_path does still work.

config :ueberauth, Ueberauth,
  providers: [
    identity: {Ueberauth.Strategy.Identity, [
      callback_methods: ["POST"],
      request_path: "/login",
      callback_path: "/login",
      param_nesting: "login",
      uid_field: :email,
      nickname_field: :email,
    ]}
  ]

404 when using `forward` in Plug.Router

My app is an umbrella with an api app that forwards to a versioned endpoint, like:

forward("/v1", to: V1)

inside of the V1 router, I have a forward to my auth handler:

forward("/auth", to: V1.Routes.Auth)

and my auth handler looks like:

defmodule V1.Routes.Auth do
  use Plug.Router
  require Logger

  plug(:match)
  plug(:fetch_query_params)
  plug(Ueberauth)
  plug(:dispatch)

  get "/:provider/callback" do
    conn
    |> send_resp(200, %{success: "facebook"})
  end
  match _ do
    conn
    |> send_resp(404, %{error: "Not found"})
  end
end

I have facebook configured and when ever I go to the url: /v1/auth/facebook I get a 404. Now if I move that /auth forward in to main api router, then /auth/facebook will work, but if I add V1 in front of that url, within the forward at all, I get a 404. I have tried setting the base path in the config, and explicitly setting the paths for the facebook strategy config.

Im not using Phoenix for this, it's pure plug.

Configurable json serializer

@yordis @doomspork 👋 Following up on a previous discussion of allowing users to configure their json encoder/decoder. Some apps may be using Jason instead of Poison for example.

For OAuth2 (a dep of many other ueberauth libraries), it looks like Poison is the default, but they allow you to configure it via application config serializers based on a mime-type here.

However, as of now, this would require setting, say Jason as the serializer like so:

# only ueberauth_twitter for now (correct me if I am wrong though) as it is still using oauth1.1
config :ueberauth, json_library: Jason

# all other ueberauth wrapper libraries I believe, which use oauth2
config :oauth2, 
  serializers: %{
    "application/vnd.api+json" => Jason,
    "application/json" => Jason
  }

There is an outstanding PR to allow registration of serializers via OAuth2.register_serializer/2 instead of application config. This would be very useful.
ueberauth/oauth2#92

For now, it seems we could move forward with json_library config to allow ueberauth_twitter to use Jason (or whatever other serializer) if it so desired and note in the README that other libraries (google, facebook, github) can configure accordingly (although I admit this is a bit deceiving). Once/if the related PR above is merged, we could then do the work to make this integration simpler. Lmk what you think or if there is a better way than what I outlined above!

Previous discussion ueberauth/ueberauth_twitter#26

Test mode

Hi!

I've been lost for two days trying to find a proper way of doing testing like you would do with Omniauth.
As a workaround I've set up Bypass, but I find the process clumsy and verbose, since I need to configure each Strategy host to map to different Bypass ports, etc.

So I am willing to tackle the feature but I would like some pointers about how to implement it so we can get a mergeable pull requests.

Thanks.

Document :callback_params in README or similar

Turns out this is a whitelist that defaults to []. We just got bit pretty hard by this behavior being hidden, so it's worth calling out in the docs. Effectively we had a bunch of plugs all acting on the presence of query params and were very confused why they never seemed to actually trip. This whitelist behavior was very unexpected.

Seems to have been added quite a while back in #45

Curious what the utility for that being a whitelist is. I wonder if it should only whitelist if :callback_params is nonzero length or something like that?

Additionally, and perhaps this is a bug to file with the individual strategies but things like https://github.com/ueberauth/ueberauth_google/blob/88b9ae851ffda0d34e991ee85e84a0803f7448d0/lib/ueberauth/strategy/google.ex#L15 seem to be just ignoring these options and leaving them at the door, even after adding to the callback params .

Facebook seems to maybe be handling them, albeit under a different option name? https://github.com/ueberauth/ueberauth_facebook/blob/2b31d99c35852327aa5b509128f0f9f7f689c774/lib/ueberauth/strategy/facebook.ex#L25

I guess as a followup to this: if I want a param to my request pipeline, (such as for e.g. setting a cookie for a user), what is the way to do that, Ueberauth-wide?

Add reader module for Strategies configurations

Add the ability to change the way we read the configuration of the strategies. Right now we have to reload or coding the configuration (See https://github.com/ueberauth/ueberauth/blob/master/lib/ueberauth.ex#L175)

Use Case

Services like Firebase or Auth0 allow you to configure the social media integration on their platform. They save the configuration in the database which allow us to easy change the configuration.

Examples:
screen shot 2017-03-17 at 11 02 31 pm
screen shot 2017-03-17 at 11 03 08 pm

Thought

We could add a configuration, like

config :ueberauth, Ueberauth,
  config_reader: MyApp.Ueberauth.Reader
....

That allow use to load the configuration of the provider. This module could be using Ecto or whatever we want to load that configuration.

The Reader module should follow a protocol in how to send the data back for sure (I need your help in how to think a little bit deeper on this)

cc: @doomspork

Separate ueberauth config for different app under umbrella project

Hi! I'm working on an umbrella project, I have 2 apps, one for admin and one for user.
I want both admin and user can login using facebook account.

  • admin -----auth---> facebook app for admin
  • user -----auth----> facebook app for user

this is config for admin app

config :ueberauth, Ueberauth,
  providers: [
    facebook: {Ueberauth.Strategy.Facebook, []}
  ]

config :ueberauth, Ueberauth.Strategy.Facebook.OAuth,
  client_id: System.get_env("ADMIN_FB_ID"),
  client_secret: System.get_env("ADMIN_FB_SECRET")

this is config for user app

config :ueberauth, Ueberauth,
  providers: [
    facebook: {Ueberauth.Strategy.Facebook, []}
  ]

config :ueberauth, Ueberauth.Strategy.Facebook.OAuth,
  client_id: System.get_env("FB_ID"),
  client_secret: System.get_env("FB_SECRET")

Admin account cannot login using facebook because it use configuration for user app.
How can I separate configuration for admin and user app?
Thank you

Start a wish list for an upgrade to ueberauth

There's been a few points of feedback I've been getting on Ueberauth. It feels like we have a sufficiently diverse community now to really dial in on what would make Ueberauth amazing.

The couple of major things that come to mind are:

  1. Remove the forced reliance on Plug. Constraining Ueberauth to only work through plug makes it inflexible. It seems that we should be able to change the behaviour so that conn is not included in the main authentication part. It should receive some parameters instead so that it can be used outside of the case of a plug. Ueberauth can then provide a plug that makes use of the defined behaviour to do the right thing.
  2. The individual strategies need to be updated to handle 'token' auth rather than the standard browser flow. Although this is currently possible, the ueberauth_* libraries do not support this.

I'd love to start fleshing these ideas out into something more resembling a spec and wondering what thoughts others have about it.

Getting nil in email on fb login

I have set email permission and i am able to get email address when i tested it using graph api explorer. Ueberauth returns nil in email field. The default scope contains email.

Improve configuration

As a user, I should be able to configure the strategies at runtime.

Umbrella applications where we could have multiple Web layers we should be able to have different configurations for Ueberauth based on the apps.

Users want to load the configuration from soft of a database. The workflow should allow you to configure the strategies at runtime when the workflow is initialized.

Proposal

The Ueberauth API should allow the user to initialize the workflow with the desired configuration.

Notes

Ueberauth shouldn't care how the configuration is load, the initialization of the workflows should allow us to pass the config.

Workflow initialized manually could take the configuration in the function call.

Workflow initialized throw Plug has two use cases.

  1. Umbrella app, since you could have multiple apps the Plug would take multiple instances of Ueberauth configurations.

  2. The configuration is loaded based on some information from the connection.

Related to #78 #51 please read the threads for more details about it.

Add token base workflow

Related to #20

Add a method that takes a auth token code that will be used for fetch the user info data. Some work done (https://github.com/yordis/ueberauth_facebook/blob/add-token-based-workflow/lib/ueberauth/strategy/facebook.ex#L56). I would say that it will be better to actually use a different name for it.
This method do not required any Conn or anything related to Plug. Normally will be called manually with some token.
Example:

{:ok, auth} <- Ueberauth.Strategy.Facebook.handle_callback!(token)

Use Case

When the OAuth workflow happens in the clients (like Mobile devices using the native SDKs), the access token will be use for authenticate the user instead of the current OAuth2 workflow.

Thought

There is some overlapping between both workflows (See: https://github.com/yordis/ueberauth_facebook/commit/c4246cf56bf8ffad61297fc88ea6cdee36dfeb69) As you can see the differences between the workflows is that in one of them the user data is inside the Conn.

This is a full implementation of the token based workflow where you will find the overlaps between workflows.

Please read the comments on the code

# The StrawHat module is just because it is implemented in my application
defmodule StrawHat.Ueberauth.Strategy.Facebook do
  alias Ueberauth.Auth.{Info, Credentials, Extra}

  def handle_callback!(token) when is_binary(token) do
    # Re-use the same configuration of the module
    {_module, config} = Application.get_env(:ueberauth, Ueberauth)[:providers][:facebook]
    token = OAuth2.AccessToken.new(token)
    client = Ueberauth.Strategy.Facebook.OAuth.client([token: token])

    case OAuth2.Client.get(client, "/me?fields=#{config[:profile_fields]}") do
      {:ok, %OAuth2.Response{status_code: status_code, body: user}} when status_code in 200..299 ->
        # Call manually `auth/2`, tried to follow the same workflow of the other workflow
        # There is some overlap, probably a good place to refactor
        {:ok, auth(user, token)}
      {:ok, %OAuth2.Response{status_code: 401, body: body}} ->
        {:error, body["error"]["message"]}
      {:error, %OAuth2.Error{reason: reason}} ->
        {:error, reason}
    end
  end
  
   # %OAuth2.AccessToken{} = token I think should be required
   # because it's been use in `credentials` which need this struct
  defp auth(user, %OAuth2.AccessToken{} = token) do
    %Ueberauth.Auth{
      provider: :facebook, # This is different in the other workflow
      strategy: Ueberauth.Strategy.Facebook, # This is different in the other workflow
      uid: uid(user),
      info: info(user),
      extra: extra(user, token),
      credentials: credentials(token)
    }
  end

  def uid(%{"id" => id} = _user), do: id

  def info(user) do
    %Info{
      description: user["bio"],
      email: user["email"],
      first_name: user["first_name"],
      image: fetch_image(user["id"]),
      last_name: user["last_name"],
      name: user["name"],
      urls: %{
        facebook: user["link"],
        website: user["website"]
      }
    }
  end

  def extra(user, token) do
    %Extra{
      raw_info: %{
        token: token,
        user: user
      }
    }
  end

  def credentials(%OAuth2.AccessToken{} = token) do
    scopes = token.other_params["scope"] || ""
    scopes = String.split(scopes, ",")

    %Credentials{
      expires: !!token.expires_at,
      expires_at: token.expires_at,
      scopes: scopes,
      token: token.access_token
    }
  end

  defp fetch_image(uid) do
    "http://graph.facebook.com/#{uid}/picture?type=square"
  end
end

cc: @doomspork

Best practice for connecting multiple accounts

First of all, thanks for the great library.

I was wondering if there's a best practice for allowing a user to connect multiple accounts. For example:

  • Login with Google by navigating to /auth/google
  • Create user record abc in the database and store the auth google details
  • Login with GitHub by navigating to /auth/google?access_token=access_token_abc BUT also passing along the auth details of the currently logged in user.
  • Within /auth/google/callback, tie the account back to the original user.. meaning I have access to access_token_abc

Should I encode the info in the state OAuth property? Should I cache oauth state => user_id? Or something else entirely?

Thanks!

OAuth2.Error at GET /auth/facebook/callback {:tls_alert, 'handshake failure'}

I started running into this error in my application and resorted to seeing if the ueberauth_example app had the same issue and it does when updated to use the most recent Ueberauth (0.4) and strategy (Facebook 0.5, for example)

When trying to authenticate with Facebook, Github, or Google, I get the handshake failure alert

Has anyone else experienced this?

Thanks!

screen shot 2016-11-28 at 2 19 43 pm

[info] Running UeberauthExample.Endpoint with Cowboy using http://localhost:4000
28 Nov 14:18:03 - info: compiled 5 files into 2 files, copied 3 in 1216ms
[info] GET /auth/facebook
[debug] Processing by UeberauthExample.AuthController.request/2
  Parameters: %{"provider" => "facebook"}
  Pipelines: [:browser]
[info] Sent 302 in 43ms
[debug] UeberauthExample.AuthController halted in Ueberauth.call/2
[info] GET /auth/facebook/callback
[debug] Processing by UeberauthExample.AuthController.callback/2
  Parameters: %{"code" => "AQAFevYCxlVAc_SuHdFugvcnLP07V95G6xEZOE0G2fuULM9ux5RNfqBSOSZ_6DA40jA7ZR-98BuhoMAUE8WZBZvm8BbS0hZM_4qlFOx3klyRNOH_OVk15V3T1rr4SonUIY6QiGGIPBHVmupFtv7Zv1_366YVMQsdf9MYU8U2ltZBnWE4ZKbPnWAEzFIerv62pWcOglmibJ1g3MdLGSmlyR6OkmonHFm20qnSiSEMrRjBbmuWDH0EC4lpYdVgDm6TIcv5pBkOnoUqTDUqxV0O7o53VGB8usEhwBtfrimLnK8PiQwr3kXDhPRi85-hXxCzaTI", "provider" => "facebook"}
  Pipelines: [:browser]
[error] SSL: :certify: tls_connection.erl:688:Fatal error: handshake failure - malformed_handshake_data

[info] Sent 500 in 314ms
[error] #PID<0.382.0> running UeberauthExample.Endpoint terminated
Server: lvh.me:4000 (http)
Request: GET /auth/facebook/callback?code=AQAFevYCxlVAc_SuHdFugvcnLP07V95G6xEZOE0G2fuULM9ux5RNfqBSOSZ_6DA40jA7ZR-98BuhoMAUE8WZBZvm8BbS0hZM_4qlFOx3klyRNOH_OVk15V3T1rr4SonUIY6QiGGIPBHVmupFtv7Zv1_366YVMQsdf9MYU8U2ltZBnWE4ZKbPnWAEzFIerv62pWcOglmibJ1g3MdLGSmlyR6OkmonHFm20qnSiSEMrRjBbmuWDH0EC4lpYdVgDm6TIcv5pBkOnoUqTDUqxV0O7o53VGB8usEhwBtfrimLnK8PiQwr3kXDhPRi85-hXxCzaTI
** (exit) an exception was raised:
    ** (OAuth2.Error) {:tls_alert, 'handshake failure'}
        (oauth2) lib/oauth2/client.ex:171: OAuth2.Client.get_token!/4
        (ueberauth_facebook) lib/ueberauth/strategy/facebook.ex:48: Ueberauth.Strategy.Facebook.handle_callback!/1
        (ueberauth) lib/ueberauth/strategy.ex:299: Ueberauth.Strategy.run_callback/2
        (ueberauth_example) web/controllers/auth_controller.ex:1: UeberauthExample.AuthController.phoenix_controller_pipeline/2
        (ueberauth_example) lib/ueberauth_example/endpoint.ex:1: UeberauthExample.Endpoint.instrument/4
        (ueberauth_example) lib/phoenix/router.ex:261: UeberauthExample.Router.dispatch/2
        (ueberauth_example) web/router.ex:1: UeberauthExample.Router.do_call/2
        (ueberauth_example) lib/ueberauth_example/endpoint.ex:1: UeberauthExample.Endpoint.phoenix_pipeline/1
        (ueberauth_example) lib/plug/debugger.ex:123: UeberauthExample.Endpoint."call (overridable 3)"/2
        (ueberauth_example) lib/ueberauth_example/endpoint.ex:1: UeberauthExample.Endpoint.call/2
        (plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
        (cowboy) src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4

System Info:

Mac OS X 10.12.1
Elixir 1.3.2 /Erlang-OTP 19.1

Erlang/OTP 19 [erts-8.1] [source-77fb4f8] [64-bit] [smp:8:8] [async-threads:10] [kernel-poll:false]

Interactive Elixir (1.3.2) - press Ctrl+C to exit (type h() ENTER for help)

I backtracked to Erlang 18.3 with no luck.

Creating Custom Callback URIs for Mobile Apps

Is there a way to create Redirect URIs for Mobile Apps?

I would like to return a callback like myapp://callback#token=<token> so that the phone knows to call the app and open it ala something like this

I didn't see an optional parameter for this?

Any helpers to ueberauth callbacks in a Phoenix application?

Hi,

I am working on a Phoenix application where I am using Ueberauth Slack.
I wanted to write controller tests for callbacks

def callback(%{assigns: %{ueberauth_failure: _fails}} = conn, _params) do.

...

def callback(%{assigns: %{ueberauth_auth: auth}} = conn, _params) do

Any helpers to set assigns properly while testing?

Thanks!

Extra field on `%Info{}` struct

Should we consider adding an extra field to the %Info{} struct? Like we already have other_params on token.

It is handy to store extra information from the social provides, such as: age, gender, birthdays, hometown, and so on.

Any drawbacks I am missing?

Error when plugging the Ueberauth module

== Compilation error on file web/controllers/auth_controller.ex ==
** (FunctionClauseError) no function clause matching in anonymous fn/2 in Ueberauth.init/1
    lib/ueberauth.ex:168: anonymous fn({:identity, [{Ueberauth.Strategy.Identity, []}]}, %{}) in Ueberauth.init/1
    (elixir) lib/enum.ex:1387: Enum."-reduce/3-lists^foldl/2-0-"/3
    (plug) lib/plug/builder.ex:198: Plug.Builder.init_module_plug/3
    (plug) lib/plug/builder.ex:186: anonymous fn/4 in Plug.Builder.compile/3
    (elixir) lib/enum.ex:1387: Enum."-reduce/3-lists^foldl/2-0-"/3
    (plug) lib/plug/builder.ex:186: Plug.Builder.compile/3
    (phoenix) expanding macro: Phoenix.Controller.Pipeline.__before_compile__/1
    web/controllers/auth_controller.ex:1: Bonsai.AuthController (module)
    (elixir) lib/kernel/parallel_compiler.ex:100: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/8

Cryptic build error if config isn't set.

This is not a bug but rather an idea for an improvement. If the Ueberauth config isn't set for your current environment, then during compilation a cryptic build error arises:

Compiling 24 files (.ex)

== Compilation error in file lib/onward_web/controllers/auth_controller.ex ==
** (FunctionClauseError) no function clause matching in Keyword.pop/3

    The following arguments were given to Keyword.pop/3:

        # 1
        nil

        # 2
        :base_path

        # 3
        "/auth"

    Attempted function clauses (showing 1 out of 1):

        def pop(keywords, key, default) when is_list(keywords)

    (elixir) lib/keyword.ex:964: Keyword.pop/3
    lib/ueberauth.ex:177: Ueberauth.init/1
    (plug) lib/plug/builder.ex:302: Plug.Builder.init_module_plug/4
    (plug) lib/plug/builder.ex:286: anonymous fn/5 in Plug.Builder.compile/3
    (elixir) lib/enum.ex:1925: Enum."-reduce/3-lists^foldl/2-0-"/3
    (plug) lib/plug/builder.ex:284: Plug.Builder.compile/3
    (phoenix) expanding macro: Phoenix.Controller.Pipeline.__before_compile__/1
    lib/onward_web/controllers/auth_controller.ex:1: OnwardWeb.AuthController (module)

This could be prevented by adding a check for the presence of the configuration right before this line. If the configuration isn't found, then an exception could be thrown or we could have a pattern match fail so that the user sees the configuration isn't present.

I could do this, but before I do I wanted to run the idea by you in this issue.

Decoupling OAuth2 provider endpoint details from Überauth

Hey all, thanks for the great work on üeberauth! Had an idea and wanted to run it by you all, especially @doomspork @hassox and @scrogson.

Background

I've been building an application that uses Elixir as a GraphQL back-end. It uses different OAuth2 providers for user authentication using a mix of Authorization Code and Implicit flows.

Choosing Facebook as a first implementation, I read through the ueberauth/ueberauth_facebook code to see how y'all do it. In my case, I can't call the functions directly since I'm not using the rest of überauth to manage the user login.

Which got me to thinking - what if the provider specific OAuth2 functions had their own package? Then it could be used on its own (my use case) or as an integration point to ueberauth/ueberauth_facebook.

As a stand-alone package

So I spun up chrislaskey/oauth2_facebook to explore it. Fundamentally, it just pulls all the endpoint specific code (e.g. building the /me? path) out of Ueberauth.Strategy.Facebook - decoupling the endpoint details from the überauth details.

One potential advantage is the endpoint specific code now lives in its own package. Using a separate library like this would lighten the maintenance required on an integration like ueberauth_facebook since you'd only have to manage the data translation instead of also managing the provider endpoint details.

It'd also be a general purpose library for users needing lower-level calls, like I do.

Feedback

As for the code itself, there's still some more work better organize the code - I've overloaded the Strategy file to contain both the generic OAuth get_token! calls and specific endpoint calls like get_user. Would need to talk with Sonny about the right way to organize it.

At any rate, the important pieces are there - it works, there's some test coverage. Enough that you should be able to see the shape of it. Thoughts and feedback appreciated. Cheers!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.