Git Product home page Git Product logo

ueberauth_twitter'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_twitter's People

Contributors

arnodirlam avatar brenthargrave avatar cgorshing avatar chulkilee avatar clupprich avatar doomspork avatar gmile avatar gwbrown avatar jbrowning avatar jerodsanto avatar kelostrada avatar kianmeng avatar snewcomer avatar ybur-yug avatar yordis avatar

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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

ueberauth_twitter's Issues

Poison is referenced but isn't a dependency

Ueberauth.Strategy.Twitter.fetch_user/2 invokes Poison for JSON decoding but Poison is not listed as a dependency in mix.exs.

Here's the compile-time error:

warning: function Poison.decode!/1 is undefined (module Poison is not available)
Found at 2 locations:
  lib/ueberauth/strategy/twitter.ex:107
  lib/ueberauth/strategy/twitter.ex:113

Runtime configuration

It is not possible to configure the twitter credentials at runtime - they must be present and compile time.

Other ueberauth providers, like ueberauth_google provide such functionality like this:

config :ueberauth, Ueberauth.Strategy.Google.OAuth,
  client_id: {System, :get_env, ["GOOGLE_CLIENT_ID"]},
  client_secret: {System, :get_env, ["GOOGLE_CLIENT_SECRET"]}

An example implementation can be found in this PR: #39

Ensuring can use library with Jason

Currently, without Poison installed, decoding the response will fail. With the introduction of Jason, we could allow the consumer to specify their own json parser. What do you think? If you agree, I'll put up a small PR.

One idea I have via a config option:

config :ueberauth_twitter, json_library: Jason

Crash when no Key or Secret are provided.

I have experienced a crash when the consumer_key or consumer_secret are not set. I know this is an off case but other strategies seem to handle this fine by returning an error that gets added to the flash errors and the crash seems due to an unexpected response from the twitter API.

I am following the set up provided by this repo; https://github.com/ueberauth/ueberauth_example

I currently have the twitter strategy set up as follows:

config :ueberauth, Ueberauth.Strategy.Twitter.OAuth,
  consumer_key: System.get_env("TWITTER_CONSUMER_KEY"),
  consumer_secret: System.get_env("TWITTER_CONSUMER_SECRET")

and neither of those env variables have been set in my system.

The stack trace is as follows below:

Request: GET /auth/twitter
** (exit) an exception was raised:
    ** (ArgumentError) raise/1 expects an alias, string or exception as the first argument, got: {:ok, {{'HTTP/1.1', 401, 'Authorization Required'}, [{'cache-control', 'no-cache, no-store, must-revalidate, pre-check=0, post-check=0'}, {'date', 'Sat, 23 Apr 2016 20:48:45 GMT'}, {'pragma', 'no-cache'}, {'server', 'tsa_a'}, {'www-authenticate', 'OAuth realm="https://api.twitter.com"'}, {'content-length', '64'}, {'content-type', 'application/json; charset=utf-8'}, {'expires', 'Tue, 31 Mar 1981 05:00:00 GMT'}, {'last-modified', 'Sat, 23 Apr 2016 20:48:45 GMT'}, {'content-disposition', 'attachment; filename=json.json'}, {'set-cookie', 'guest_id=v1%3A146144452548831847; Domain=.twitter.com; Path=/; Expires=Mon, 23-Apr-2018 20:48:45 UTC'}, {'status', '401 Unauthorized'}, {'strict-transport-security', 'max-age=631138519'}, {'x-connection-hash', 'ade5541be64a39b6dadaad6e48272394'}, {'x-content-type-options', 'nosniff'}, {'x-frame-options', 'SAMEORIGIN'}, {'x-response-time', '7'}, {'x-transaction', '29df355169716468'}, {'x-twitter-response-tags', 'BouncerCompliant'}, {'x-xss-protection', '1; mode=block'}], '{"errors":[{"code":32,"message":"Could not authenticate you."}]}'}}
(ueberauth_twitter) lib/ueberauth/strategy/twitter/oauth.ex:71: Ueberauth.Strategy.Twitter.OAuth.request_token!/2
(ueberauth_twitter) lib/ueberauth/strategy/twitter.ex:17: Ueberauth.Strategy.Twitter.handle_request!/1

The crash is happening on this line: oauth.ex#71

Please let me know if there is anything more that I can provide to help.

Requesting new release

v0.3.0...master

Not much has changed since the prior release, so maybe a 0.3.1 release, but right now I'd love to have the More descriptive error message on OAuth request_token failure changes :)

Compilation Error on Recent Package

== Compilation error on file lib/ueberauth/strategy/twitter.ex ==
** (SyntaxError) lib/ueberauth/strategy/twitter.ex:63: "(" is missing terminator ")". unexpected token: "end" at line 66
(elixir) lib/kernel/parallel_compiler.ex:117: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1

could not compile dependency :ueberauth_twitter, "mix compile" failed. You can recompile this dependency with "mix deps.compile ueberauth_twitter", update it with "mix deps.update ueberauth_twitter" or clean it with "mix deps.clean ueberauth_twitter"

Fixed by adding ending parenthesis at end of line 66:
Previous:

def credentials(conn) do
{token, _secret} = get_session(conn, :twitter_token
%Credentials{token: token}
end

FIX:
def credentials(conn) do
{token, _secret} = get_session(conn, :twitter_token)
%Credentials{token: token}
end

FunctionClauseError: no function clause matching in Ueberauth.Strategy.Twitter.OAuth.access_token/3

We've been receving lots of errors like the following one in the last hour

File lib/ueberauth/strategy/twitter/oauth.ex line 20 in Ueberauth.Strategy.Twitter.OAuth.access_token/3 (ueberauth_twitter) args
File lib/ueberauth/strategy/twitter.ex line 29 in Ueberauth.Strategy.Twitter.handle_callback!/1 (ueberauth_twitter)
File lib/ueberauth/strategy.ex line 307 in Ueberauth.Strategy.run_callback/2 (ueberauth)
File lib/my_project_web/controllers/frontend/auth_controller.ex line 1 in MyProjectWeb.Frontend.AuthController.phoenix_controller_pipeline/2 (my_project)
File lib/phoenix/router.ex line 288 in Phoenix.Router.__call__/2 (phoenix)
File lib/plug/error_handler.ex line 64 in MyProjectWeb.Router.call/2 (my_project)
File lib/my_project_web/endpoint.ex line 1 in MyProjectWeb.Endpoint.plug_builder_call/2 (my_project)
File lib/my_project_web/endpoint.ex line 1 in MyProjectWeb.Endpoint.call/2 (my_project) 

The first argument to this function is a token, that is set in the session during the request phase, which was nil. Any idea how this could have happened?

How to specify callback URL in this library?

Steps to Reproduce

I've gotten an error "Callback URL not approved for this client application. Approved callback URLs can be adjusted in your application settings (Code 415)"

Right, in my Twitter app it's perhaps not what what this library expects. But how to specify the callback URL here, for this library, though? This isn't mentioned anywhere in the README

Expected Result

Actual Result

session not fetched, call fetch_session/2, lib/ueberauth/strategy/twitter.ex:20

The error occurs in {: phoenix, "~> 1.4.10"}

Error

  def handle_request!(conn) do
    token = Twitter.OAuth.request_token!([], [redirect_uri: callback_url(conn)])

    conn
    |> put_session(:twitter_token, token)
    |> redirect!(Twitter.OAuth.authorize_url!(token))
  end

FIX

def handle_request!(conn) do
    token = Twitter.OAuth.request_token!([], [redirect_uri: callback_url(conn)])

    conn
    |> fetch_session                                                 # Add this line
    |> put_session(:twitter_token, token)
    |> redirect!(Twitter.OAuth.authorize_url!(token))
  end


Error handling in oauth request_token!/2

{:error, error} -> raise error

error does not contain a string or exception here and causes an ArgumentError:

Request: GET /auth/twitter
** (exit) an exception was raised:
    ** (ArgumentError) raise/1 and reraise/2 expect a module name, string or exception as the first argument, got: {:ok, {{'HTTP/1.1', 403, 'Forbidden'}, [{'cache-control', 'no-cache, no-store, must-revalidate, pre-check=0, post-check=0'}, {'date', 'Sat, 16 Jun 2018 21:44:38 GMT'}, {'pragma', 'no-cache'}, {'server', 'tsa_o'}, {'content-length', '203'}, {'content-type', 'application/xml;charset=utf-8'}, {'expires', 'Tue, 31 Mar 1981 05:00:00 GMT'}, {'last-modified', 'Sat, 16 Jun 2018 21:44:38 GMT'}, {'ml', 'A'}, {'set-cookie', 'personalization_id="v1_eQFxxxxxxxxxxxxxxxxA=="; Expires=Mon, 15 Jun 2020 21:44:38 GMT; Path=/; Domain=.twitter.com'}, {'set-cookie', 'guest_id=v1%3A152918547824207218; Expires=Mon, 15 Jun 2020 21:44:38 GMT; Path=/; Domain=.twitter.com'}, {'status', '403 Forbidden'}, {'strict-transport-security', 'max-age=631138519'}, {'x-connection-hash', 'faca8xxxxxxxxxxxxxxxxxxxxxx4'}, {'x-content-type-options', 'nosniff'}, {'x-frame-options', 'SAMEORIGIN'}, {'x-response-time', '124'}, {'x-transaction', '002exxxxxxxxxx2'}, {'x-twitter-response-tags', 'BouncerCompliant'}, {'x-ua-compatible', 'IE=edge,chrome=1'}, {'x-xss-protection', '1; mode=block; report=https://twitter.com/i/xss_report'}], '<?xml version="1.0" encoding="UTF-8"?><errors><error code="415">Callback URL not approved for this client application. Approved callback URLs can be adjusted in your application settings</error></errors>'}}
        (ueberauth_twitter) lib/ueberauth/strategy/twitter/oauth.ex:71: Ueberauth.Strategy.Twitter.OAuth.request_token!/2
        (ueberauth_twitter) lib/ueberauth/strategy/twitter.ex:17: Ueberauth.Strategy.Twitter.handle_request!/1
        (roadblock) lib/roadblock_web/controllers/auth_controller.ex:1: RoadblockWeb.AuthController.phoenix_controller_pipeline/2
        (roadblock) lib/roadblock_web/endpoint.ex:1: RoadblockWeb.Endpoint.instrument/4
        (phoenix) lib/phoenix/router.ex:278: Phoenix.Router.__call__/1
        (roadblock) lib/roadblock_web/endpoint.ex:1: RoadblockWeb.Endpoint.plug_builder_call/2
        (roadblock) lib/roadblock_web/endpoint.ex:1: RoadblockWeb.Endpoint.call/2
        (plug) lib/plug/adapters/cowboy/handler.ex:16: Plug.Adapters.Cowboy.Handler.upgrade/4

Maybe you could use something like {:error, error} -> raise inspect(error)

Could not authenticate you. (Code 32)

Steps to Reproduce

I added all the config README into my project, but I get Could not authenticate you. (Code 32) error when I want to redirect to twitter


Actual Result

** (exit) an exception was raised:
    ** (Ueberauth.Strategy.Twitter.OAuth.ApiError) Could not authenticate you. (Code 32)
        (ueberauth_twitter 0.4.1) lib/ueberauth/strategy/twitter/oauth.ex:82: Ueberauth.Strategy.Twitter.OAuth.request_token!/2
        (ueberauth_twitter 0.4.1) lib/ueberauth/strategy/twitter.ex:18: Ueberauth.Strategy.Twitter.handle_request!/1
        (mishka_social 0.1.0) lib/auth/handel_request.ex:10: MishkaSocial.Auth.HandelRequest.handle_social_sender/1
        (mishka_html 0.1.0) lib/mishka_html_web/controllers/auth_controller.ex:1: MishkaHtmlWeb.AuthController.action/2
        (mishka_html 0.1.0) lib/mishka_html_web/controllers/auth_controller.ex:1: MishkaHtmlWeb.AuthController.phoenix_controller_pipeline/2
        (phoenix 1.6.6) lib/phoenix/router.ex:355: Phoenix.Router.__call__/2
        (mishka_html 0.1.0) lib/plug/error_handler.ex:80: MishkaHtmlWeb.Router.call/2
        (mishka_html 0.1.0) lib/mishka_html_web/endpoint.ex:1: MishkaHtmlWeb.Endpoint.plug_builder_call/2
        (mishka_html 0.1.0) lib/plug/debugger.ex:136: MishkaHtmlWeb.Endpoint."call (overridable 3)"/2
        (mishka_html 0.1.0) lib/mishka_html_web/endpoint.ex:1: MishkaHtmlWeb.Endpoint.call/2
        (phoenix 1.6.6) lib/phoenix/endpoint/cowboy2_handler.ex:54: Phoenix.Endpoint.Cowboy2Handler.init/4
        (cowboy 2.9.0) /Users/shahryar/Desktop/mishka-cms/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
        (cowboy 2.9.0) /Users/shahryar/Desktop/mishka-cms/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
        (cowboy 2.9.0) /Users/shahryar/Desktop/mishka-cms/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3
        (stdlib 3.16.1) proc_lib.erl:226: :proc_lib.init_p_do_apply/3

I do not know this PR #38 fix this or not, it has conflict and I think it is old. It should be noted, GitHub and google work.

Incorrect raising of network errors on token retrieval

If :oauth.get/3 results in a network error it returns a tuple with error information. This results in an ArgumentError in Ueberauth.Strategy.Twitter.OAuth.request_token!/2 because Kernel.raise/1 only supports exceptions, binaries, and atoms:

raise/1 expects an alias, string or exception as the first argument, got: {:error,
{:failed_connect, [{:to_address, {'api.twitter.com', 443}}, {:inet, [:inet], :nxdomain}]}}

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.