Comments (13)
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.
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.
Nice! Do you think this is something that should go into Ueberauth and be available to all the OAuth 2 strategies?
from ueberauth.
@aphillipo not sure about all oauth providers but I didn't have any issues with both Google and Facebook so far
from ueberauth.
@hassox I, for one, want this functionality in Ueberauth.
from ueberauth.
@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.
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.
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.
I want to mock the whole flow but I haven't done it yet.
(You missed out, Elixir London was great!)
from ueberauth.
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.
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.
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/auth/user_from_auth.ex
from ueberauth.
Loosely related to #125
from ueberauth.
Related Issues (20)
- OAuth2 needs new maintainers HOT 4
- State nonce cookie should properly be signed HOT 2
- How can run Ueberauth plug directly? HOT 3
- Custom `callback_port` and `callback_scheme` not used HOT 1
- Callback URL not mounted right when router has nested paths
- Can't set strategy options at runtime HOT 2
- Line Login v2.1 update request which I've done HOT 4
- Gracefully handle unknown providers HOT 10
- callback_port config compiled into plug at compile time HOT 10
- App.Auth.AuthController.request/2 is undefined or private HOT 1
- minor: mix package versions are out of date
- New hex.pm release? HOT 1
- Incorrect callback_url when using Bandit.PhoenixAdapter
- put_session successful, but get_session failed HOT 1
- function MyAppWeb.OAuthController.request/2 is undefined or private HOT 1
- Support for nonces HOT 1
- FR: Support multiple usages of the same strategy in config HOT 13
- Incomplete docs to configure pipeline and check that user is authenticated HOT 3
- Latest release breaks existing paths HOT 6
- Latest version is retired HOT 7
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from ueberauth.