Code Monkey home page Code Monkey logo

Comments (29)

scrogson avatar scrogson commented on May 9, 2024 6

Because Ueberauth doesn't provide a good interface for querying the Facebook API and getting an Ueberauth.Auth struct back, I had to copy paste a lot of code out of Ueberauth in order to request information from Facebook/etc with the token I received from the mobile app.

It sounds to me that we just need to provide a public API (Protocol or callback behaviour) for fetching user data from a token so that you can get the user data back in a unified way outside of the OAuth2 Authorization Code flow.

Ueberauth was designed around OAuth2 Authorization Code flow. The user is redirected from their browser to the authorization server and then back to the redirect_uri (callback) with a code grant that is then used in exchange for an access token.

The access token can then be used to get the user's data and other things on behalf of the user.

If you are wanting to provide a token for your native mobile apps, you will most likely need to implement an endpoint like POST /token where your mobile app can post the token it received from the OAuth2 provider so your app can exchange that for your own auth token (like a JWT generated from Guardian).

POST /token

{"provider": "facebook", "access_token": "<facebook-access-token>"}

Does this make sense?

from ueberauth.

yordis avatar yordis commented on May 9, 2024 6

@hassox the problem is not doing it, the problem is why? Why would I do that for the good of the community. Specially that we are talking about backend code, which we don't care on increase the code base as long as is for the good of the package, no just for sake of adding it.

Look, those people that @Rakoth (amazing job and I am glad that somebody implemented), but now I have to make decision about using ueberauth from the official repo or use them because I really need that.

I hope you understand that I am trying to actually avoid to add complexity on hex packages when we really don't need to do it. I want to see ueberauth to be extended from the next needs, and being realistic, in 2017 is more valuable to have the token based workflow than the other one. With SPAs everywhere and mobile apps.

Sorry I repeated again, it's about if I can do it or not outside of euberauth, it's about extending ueberauth for the good of the community.

from ueberauth.

swennemans avatar swennemans commented on May 9, 2024 4

@danielberkompas Thanks for your reply. I'm still exploring things, and I'm reconsidering my approach after your last comment. Thanks 👍 .

Possibly a better alternative is:

  • Get FB accessToken on the client;
  • Send this token to the server, on the server do a request to (in my case) Facebook's graph API, https://graph.facebook.com/v2.7/{facebook-user-id}?access_token=MY_TOKEN, and request the needed user's information.
  • If token is valid, and thus results are returned, the user is authenticated. If needed store the user's info in the database and exchange the Guardian token.

Thoughts?

from ueberauth.

Rakoth avatar Rakoth commented on May 9, 2024 3

Hi guys, you could use providers forks with token flow implemented:
https://github.com/KosyanMedia/ueberauth_facebook
https://github.com/KosyanMedia/ueberauth_twitter
https://github.com/KosyanMedia/ueberauth_google
https://github.com/KosyanMedia/ueberauth_vk
https://github.com/KosyanMedia/ueberauth_ok

from ueberauth.

yordis avatar yordis commented on May 9, 2024 2

@danielberkompas after watching what you did I notice a common pattern in the other packages and I am starting to understand a little bit better ueberauth in a nutshell.

Things to Notice

Thoughts

  • Create a protocol meaning, hand shaking between the strategies and ueberauth, instead of depending of conn inside the we pass the user info down so you can construct Ueberauth.Auth.
    That allow us to use the same strategy even if we are using token based strategy.
  • Create a token strategy which is a little different and normally it will be call manually with the required access token but it will reuse the strategy configuration + extracting data code

I am just thinking right now it how to do it, I am pretty sure if we open the protocol of the strategies we can actually scale this package to the next required feature.

from ueberauth.

Rakoth avatar Rakoth commented on May 9, 2024 2

Hi! Thanks for your effort on this great library!
Facing the same issue with sdk returning access token. My first thought was to support extra case in handle_callback! when token parameter is present.
I agree though, that provider API could be more clear and reusable, so we don't have to copy-paste private methods from providers in non-trivial cases.

from ueberauth.

danielberkompas avatar danielberkompas commented on May 9, 2024 1

@mbenatti Here's a gist of what I ended up doing: https://gist.github.com/danielberkompas/9822197a8cec66ff011d24ab32e687cf

from ueberauth.

anton-ferio avatar anton-ferio commented on May 9, 2024 1

Hello everyone!
Thank the contributors for a great job!

Ueberauth solves the same problems that omniauth from ruby comunity solves. So the problem (if it is a problem) explained here must be the problem in ruby community too.
There is no general implementation for this case in omniauth, but there is a gem (no longer supported) https://github.com/SoapSeller/omniauth-facebook-access-token which is implementing Oauth API token authentication for Facebook.

I am not sure than defining extra provider is a good idea but why the idea of defining extra ueberauth layer for common case for native iOS and Android application is so bad? Or may be there is a security issues we should know about? Thank you!

from ueberauth.

hassox avatar hassox commented on May 9, 2024 1

@yordis I think I'm agreeing with you. I'm suggesting that we should build this into the libraries to accept a "token" parameter as part of the callback so you get the standard Auth struct. Is that not what you had in mind?

from ueberauth.

hassox avatar hassox commented on May 9, 2024

Can you please explain a little more about what you're wanting to do?

from ueberauth.

fsoeltoni avatar fsoeltoni commented on May 9, 2024

I mean when you piping trough api instead of browser

from ueberauth.

hassox avatar hassox commented on May 9, 2024

If you're piping through an api pipeline rather than browser it sounds like you're into Guardian territory. There's nothing specifically in ueberauth that relies on the session. I'm sorry to go back again, but can you outline what you're trying to do?

from ueberauth.

danielberkompas avatar danielberkompas commented on May 9, 2024

I have a similar issue. I want a mobile app to be able to post an OAuth token to my API authentication endpoint. I then want to be able to run that token through Ueberauth and get back an Ueberauth.Auth struct, which I can then use to authenticate the user the same way I do for regular web requests.

Right now, it isn't clear if there's a way to use Ueberauth without using the plugs, or whether you should use the plugs for this. Does that make sense?

from ueberauth.

swennemans avatar swennemans commented on May 9, 2024

I also struggle(d) with this. But it's mostly based on a misunderstanding of what the goal of this library is. That being said, if if say something misinformed here please comment :)

IMO this sums up the goal of the library nicely:

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

So the goal is only acquiring the user's information. For example when working with a mobile application (as in my case) there is a big chance you don't need this library. Instead you can use a clientside oAuth library to acquire the user' relevant information, store this information if needed, and continue with something like guardian to authenticate future requests.

from ueberauth.

danielberkompas avatar danielberkompas commented on May 9, 2024

@swennemans I'm not sure that really works. If your client app doesn't post an OAuth token to your API to authenticate, how does it authenticate? Do you require your users to type in a password even after they've logged in via OAuth on the client app?

from ueberauth.

swennemans avatar swennemans commented on May 9, 2024

@danielberkompas I was wondering the same thing. Currently I'm using react-native-fbsdk. And I'm experimenting with the following flow:

  • Login / Signup with Facebook button;
  • This will transit user to Facebook login/screen;
  • If an user fills in valid login information, it will return a token;
  • This token is only useful to querying the user information using Facebook's graph API;
  • This queried information I can store/query in my Phoenix' database. Here I can create or query the user based on the user' email (returned from Facebook's graph API;
  • Based on the valid token returned from the Facebook login, I assume the user is authenticated;
  • Then, in order to communicate with the JSON API, I return a Guardian token.
  • If an user is logged-out and wants to login again, the same process applies.

I'm not saying that this is the way to go. But from reading ueberauth's readme, it seems that it's only meant as information retrieval tool. Maybe @hassox can chime in :)

Thoughts?

from ueberauth.

danielberkompas avatar danielberkompas commented on May 9, 2024

Do you query Facebook for user information on the mobile app or on the server?

If on the mobile app, what do you then send to the server to prove the user is authenticated, in exchange for the Guardian token?

If on the server, how does the server get the Facebook token if you don't send it to the server and don't use Ueberauth to fetch information?

from ueberauth.

danielberkompas avatar danielberkompas commented on May 9, 2024

The flow you suggest there is exactly what I'm doing.

Because Ueberauth doesn't provide a good interface for querying the Facebook API and getting an Ueberauth.Auth struct back, I had to copy paste a lot of code out of Ueberauth in order to request information from Facebook/etc with the token I received from the mobile app.

from ueberauth.

kedare avatar kedare commented on May 9, 2024

I am also looking for this kind of scenario (The mobile app gets the token, then send if to the server for validation and get a JWT token back), there is still nothing about this in Ueberauth ? What would be the best approach ?

from ueberauth.

mbenatti avatar mbenatti commented on May 9, 2024

@danielberkompas
Hi
I'm trying to do the same thing, can you share your solution with me?

from ueberauth.

yordis avatar yordis commented on May 9, 2024

For Facebook strategy,

I finished the module that would work when you pass a token.

Things to notice

The things to notice is the overlapping in how to extract the data BUT the differences is that I pass the raw user data instead of extract it from the Plug.Conn.

auth method which leave in https://github.com/ueberauth/ueberauth/blob/master/lib/ueberauth/strategy.ex#L276 have a different shape, because it doesn't require a Plug.Conn but it does require to pass %OAuth2.AccessToken{} = token because credentials need that hash type.
It's been call here https://github.com/ueberauth/ueberauth/blob/master/lib/ueberauth/strategy.ex#L311

defmodule Ueberauth.Strategy.Facebook do
  alias Ueberauth.Auth.{Info, Credentials, Extra}

  def handle_callback!(token) when is_binary(token) do
    {_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 ->
        {:ok, auth(user, token)}
      {:ok, %OAuth2.Response{status_code: 401, body: body}} ->
        {:error, body["error"]["message"]}
      {:error, %OAuth2.Error{reason: reason}} ->
        {:error, reason}
      _other ->
        {:error, :internal_server_error}
    end
  end

  defp auth(user, %OAuth2.AccessToken{} = token) do
    %Ueberauth.Auth{
      provider: :facebook,
      strategy: Ueberauth.Strategy.Facebook,
      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

from ueberauth.

yordis avatar yordis commented on May 9, 2024

Dealing a little bit deeper with the code, I think the best approach from ueberauth is to create two types of strategies. The only thing that overlap is data fetching but everything else is pretty much completely different.

I will stop here because I need some advice from the contributors in what should I do (specially thinking in backward comparability)

from ueberauth.

yordis avatar yordis commented on May 9, 2024

@Rakoth I tried #50 but the idea was shutdown 😢

from ueberauth.

anton-ferio avatar anton-ferio commented on May 9, 2024

Hello everyone again!
I think that @scrogson approach make sense! We don't need to affect default web flow, we only need another flow for server based exchange of token into user info (ueberauth auth format). The main problem is a correct implementation of API and configuration rules format which look good with default rules and general API. I think that strategies will be prepared very fast in case of presented general template. Thank you, and sorry for strange proposation of extra layer.

from ueberauth.

harrygr avatar harrygr commented on May 9, 2024

I came across this whilst trying to figure out a way to authenticate a single page app with my phoenix api. I'm trying the use the google strategy along with the Google sign-in for websites.

Essentially the first leg of authentication is handled entirely client-side; the user clicks the 'login with google' button and a popup from google appears prompting them to sign-in/allow access. Upon successful authentication on the google popup a callback on my spa is fired which receives a googleUser object from which I can obtain a token to send to my server. I'm sending in the form /auth/callback?code={token} which is the same as what's hit in the standard Ueberauth server flow but I'm getting an error like this:

(ArgumentError) raise/1 expects an alias, string or exception as the first argument, got: %OAuth2.Response{body: %{"error" => "invalid_grant", "error_description" => "Code was already redeemed."}

I feel like I'm quite close but ueberauth seems to struggle with the token provided? Perhaps I'm going wrong client side. Any ideas?

from ueberauth.

yordis avatar yordis commented on May 9, 2024

@harrygr if your are handling the OAuth2 workflow in the client side, then you will need what I am pushing for.
This is not currently supported. You will have to do something similar to what I did to be able to do token based workflow

from ueberauth.

hassox avatar hassox commented on May 9, 2024

I think I agree with @scrogson here. The strategies have this stuff already there just isn't a way that you can post it.

If we were to add a "token" parameter to the oauth strategies which assumes you've done client side oauth and have an access token, it should use that to populate the auth struct. Would that solve the issue that you folks are having?

from ueberauth.

yordis avatar yordis commented on May 9, 2024

@hassox and after I deal with it. I don't see the point of literally duplicate 80% of the code just because we don't want to add some layer of abstraction, which is the only reason why I pushed for this.

But you are right, everything can be done without even use anything from ueberauth but the effort is already implemented on this package 😢 just look how much code overlap to each other

from ueberauth.

yordis avatar yordis commented on May 9, 2024

Moved to #89

from ueberauth.

Related Issues (20)

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.