Giter Site home page Giter Site logo

Comments (13)

stevedomin avatar stevedomin commented on April 27, 2024 2

I've implemented something similar, I guess it would be nice to have it built into Ueberauth.

I've loosely followed this draft: https://tools.ietf.org/html/draft-bradley-oauth-jwt-encoded-state-03

from ueberauth.

venkatd avatar venkatd commented on April 27, 2024 1

Here's what I ended up doing...

I made a plug that packs up the query params into the state property which persists between auth/github and auth/github/callback

Usage

For now I have hardcoded the fields to @packable_fields, but if we want to preserve a user_id and other details, we can pack those into the state as well. I opted to go this route instead of caching a state to details map so I could avoid dealing with process state.

  pipeline :auth do
    plug Turtle.AuthPlug
  end

  scope "/auth", Turtle do
    pipe_through :auth
    get "/:provider", AuthController, :request
    get "/:provider/callback", AuthController, :callback
  end
defmodule Turtle.AuthPlug do
  import Plug.Conn

  @packable_params ["redirect_uri"]

  def init(options), do: options

  def call(conn, _opts) do
    conn = fetch_query_params(conn)
    params = conn.params

    new_params = case params do
      %{"code" => _, "state" => _} ->
        decode(params)
      %{"state" => _} ->
        encode(params)
      _ -> params
    end

    update_params(conn, new_params)
  end

  defp encode(params) do
    param_subset = Map.take(params, ["state" | @packable_params])
    packed_state = __MODULE__.Packer.pack(param_subset)
    params
      |> Map.drop(@packable_params)
      |> Map.put("state", packed_state)
  end

  defp decode(params = %{"state" => packed_state}) do
    unpacked_params = __MODULE__.Packer.unpack(packed_state)
    Map.merge(params, unpacked_params)
  end
  defp decode(params), do: params

  defp update_params(conn, new_params) do
    put_in(conn.params, new_params)
  end

  defmodule Packer do
    def pack(params) when is_map(params) do
      signed = JOSE.JWT.sign(jwk, jws, params)

      {_, encoded_state} = JOSE.JWS.compact(signed)

      encoded_state
    end

    def unpack(state) do
      {true, jwt, _} = JOSE.JWT.verify(jwk, state)
      jwt.fields
    end

    defp jwk do
       %{"kty" => "oct", "k" => secret_key}
    end

    defp jws do
      %{"alg" => "HS256"}
    end

    defp secret_key do
      env = Application.get_env(:turtle, Turtle.AuthPlug.Packer)
      env[:secret_key] |> :base64url.encode
    end
  end

end

from ueberauth.

hassox avatar hassox commented on April 27, 2024 1

Nice! Do you think this is something that should go into Ueberauth and be available to all the OAuth 2 strategies?

from ueberauth.

stevedomin avatar stevedomin commented on April 27, 2024 1

@aphillipo not sure about all oauth providers but I didn't have any issues with both Google and Facebook so far

from ueberauth.

develop7 avatar develop7 commented on April 27, 2024

@hassox I, for one, want this functionality in Ueberauth.

from ueberauth.

venkatd avatar venkatd commented on April 27, 2024

@hassox yes I think it should as connecting multiple accounts is fairly common.

Would be good to get some feedback on improving the current implementation... I've been using Elixir ~2 months so might not be doing things idiomatically.

from ueberauth.

aphillipo avatar aphillipo commented on April 27, 2024

Is state available in all oauth providers? I read on stackoverflow somewhere that facebook had broken theirs, but I'd need to check for myself...

I'm just going to use the session and delete the redirect path on errors or successes and have a sensible default.

from ueberauth.

aphillipo avatar aphillipo commented on April 27, 2024

Gah, I've built this using sessions... might switch over then. @stevedomin how are you writing tests for say a Facebook or Google login - do you test the whole flow and mock facebook, say, or just the bits you can? Sorry that I won't see you at Elixir London I'm in Japan :-D

from ueberauth.

stevedomin avatar stevedomin commented on April 27, 2024

I want to mock the whole flow but I haven't done it yet.

(You missed out, Elixir London was great!)

from ueberauth.

mikeni avatar mikeni commented on April 27, 2024

Gonna try to implement both oauth2 request state and session state, I trust the sessions more than the state, need something to fallback on.

from ueberauth.

hassox avatar hassox commented on April 27, 2024

Hey folks. Sorry this issue has dragged on for a bit. I'd like to get it tidied up.

I'm not sure I'm following the approach here now I look back on it. If you want to tie multiple accounts together to a single user resource I'm not sure ueberauth needs to really get involved does it?

In your call back controller function, you will have the result of the callback, and the session already, so if your user is logged in you can just use the verify session + load resource guardian plug (or similar) to load the current user into your callback controller (just don't use ensure authenticated). That will give you the current user if there is one. You can use that to tie it together. What am I missing?

from ueberauth.

aphillipo avatar aphillipo commented on April 27, 2024

I think the discussion descended into redirect from oauth2 providers and maintaining state. I could probably get a pull request done tonight (Japanese time)?

Also I already have the ability to connect multiple accounts with Ueberauth and Guardian - @hassox you wrote the example my code is based upon! Thanks!

https://github.com/hassox/phoenix_guardian/blob/ueberauth-guardian/web/controllers/auth_controller.ex

https://github.com/hassox/phoenix_guardian/blob/ueberauth-guardian/web/auth/user_from_auth.ex

from ueberauth.

Hanspagh avatar Hanspagh commented on April 27, 2024

Loosely related to #125

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.