Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add token base workflow #50

Closed
yordis opened this issue Mar 18, 2017 · 9 comments
Closed

Add token base workflow #50

yordis opened this issue Mar 18, 2017 · 9 comments

Comments

@yordis
Copy link
Member

yordis commented Mar 18, 2017

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

@yordis
Copy link
Member Author

yordis commented Mar 18, 2017

I will be more than happy to contribute on this but I need directions, specially thinking on fallback

@scrogson
Copy link
Member

@yordis please see my comment here

@yordis
Copy link
Member Author

yordis commented Mar 18, 2017

@scrogson but the idea is actually expand ueberauth module to support that workflow once you have the token. Notice how much duplicated code is needed for accomplish the task.

I understand how to accomplish the task but I think is better to add that feature. I think is a perfect place and it will help a lot of people.

@yordis yordis mentioned this issue Mar 31, 2017
@Abacaxi-Nelson
Copy link

I understand how to accomplish the task but I think is better to add that feature. I think is a perfect place and it will help a lot of people.

@yordis i agree. I need this feature to jump on ueberauth, and leave Rails...

@yordis
Copy link
Member Author

yordis commented Jun 19, 2017

@tugal the ticket is closed so 😢

@hassox
Copy link
Member

hassox commented Jun 20, 2017

I'd like to attack this feature in the very near future. I'm seeing this come up as an issue more and more. My focus atm is getting guardian 1.0 out but I'm hearing this issue loud and clear. One thing I'm not sure about is how to get this into ueberauth base rather than delegating it out to the individual strategies. Is there a behaviour we can define that would provide enough to kick this off or do you folks think that strategies should look after it?

@yordis
Copy link
Member Author

yordis commented Jun 20, 2017

@hassox if you are focus on Guardian I can help over this side.

One thing I'm not sure about is how to get this into ueberauth base rather than delegating it out to the individual strategies.
We can't, every strategy knows about his implementation detail. The only thing we could do is define the protocol of communication. I don't see this working in ueberauth side, specially that you have twitter in OAuth 1 still 😭

Is there a behaviour we can define that would provide enough to kick this off or do you folks think that strategies should look after it?
Strategy providers have to look after it. Ueberauth will just implement the protocol definition and workflow.

Side notes,
The reason that I didn't want to do what is listed here #20 (comment) is that, all their implementations required a Plug.Conn to be able to work. If want to see ueberauth as Web based only that would makes sense, but now with GraphQL or even good architectures around OTP apps, the attachment to any Web framework becomes a wall, at least for the token
In my case, I pushed all the user identity to it's own OTP, which just receive a token string passed from the web server.

@Abacaxi-Nelson
Copy link

I'd like to attack this feature in the very near future. I'm seeing this come up as an issue more and more. My focus atm is getting guardian 1.0 out but I'm hearing this issue loud and clear. One thing I'm not sure about is how to get this into ueberauth base rather than delegating it out to the individual strategies. Is there a behaviour we can define that would provide enough to kick this off or do you folks think that strategies should look after it?

@hassox : Why not create another optionnal method "validate_token" (ina ddition to exiting ones : request and callback), where statégies are responsible to implement. In my point of view, only stratégies know how to validate their tokens...

Nice to hean that this feature still open..

@yordis
Copy link
Member Author

yordis commented Aug 28, 2017

Moved to #61

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants