Packages

Steam API and Auth (with Phoenix/Plug integration) for Elixir

Current section

8 Versions

Jump to

Compare versions

8 files changed
+298 additions
-25 deletions
  @@ -1,11 +1,14 @@
1 1 {<<"app">>,<<"steamex">>}.
2 2 {<<"build_tools">>,[<<"mix">>]}.
3 3 {<<"contributors">>,[<<"Eric Entin">>]}.
4 - {<<"description">>,<<"Steam API and Auth (via Plug) for Elixir">>}.
4 + {<<"description">>,
5 + <<"Steam API and Auth (with Phoenix/Plug integration) for Elixir">>}.
5 6 {<<"elixir">>,<<"~> 1.0">>}.
6 7 {<<"files">>,
7 - [<<"lib/steamex.ex">>,<<"lib/steamex/auth.ex">>,<<"mix.exs">>,
8 - <<"README.md">>,<<"LICENSE">>]}.
8 + [<<"lib/steamex.ex">>,<<"lib/steamex/auth.ex">>,
9 + <<"lib/steamex/auth/phoenix.ex">>,<<"lib/steamex/auth/phoenix/router.ex">>,
10 + <<"lib/steamex/auth/phoenix/view.ex">>,<<"lib/steamex/auth/plug.ex">>,
11 + <<"mix.exs">>,<<"README.md">>,<<"LICENSE">>]}.
9 12 {<<"licenses">>,[<<"Apache 2.0">>]}.
10 13 {<<"links">>,[{<<"GitHub">>,<<"https://github.com/antipax/steamex">>}]}.
11 14 {<<"name">>,<<"steamex">>}.
  @@ -18,4 +21,4 @@
18 21 [{<<"app">>,<<"plug">>},
19 22 {<<"optional">>,false},
20 23 {<<"requirement">>,<<"~> 1.0.0">>}]}]}.
21 - {<<"version">>,<<"0.0.1">>}.
24 + {<<"version">>,<<"0.0.2">>}.
  @@ -1,2 +1,14 @@
1 1 defmodule Steamex do
2 + @moduledoc """
3 + Steamex is a library for interacting with Steam Auth and other APIs.
4 +
5 + Currently it only supports Steam Auth, but support for other APIs is coming
6 + soon.
7 +
8 + If you are a Phoenix user, please look at the documentation for
9 + `Steamex.Auth.Phoenix` for a quick-start guide.
10 +
11 + If you use another Plug-based framework (or just Plug by itself), please see
12 + the `Steamex.Auth.Plug` documentation for more information.
13 + """
2 14 end
  @@ -3,22 +3,69 @@ defmodule Steamex.Auth do
3 3 Functions related to authenticating with the Steam OpenID provider.
4 4 """
5 5
6 - @doc """
7 - Get the URL you should direct your users to in order to log in.
8 - """
9 - def get_auth_url(realm, return_to, options \\ [])
10 - when is_binary(realm) and is_binary(return_to) and is_list(options) do
11 - url_params = %{
12 - "openid.claimed_id": "http://specs.openid.net/auth/2.0/identifier_select",
13 - "openid.identity": "http://specs.openid.net/auth/2.0/identifier_select",
14 - "openid.mode": "checkid_setup",
15 - "openid.ns": "http://specs.openid.net/auth/2.0",
16 - "openid.ns.ax": "http://openid.net/srv/ax/1.0",
17 - "openid.ns.sreg": "http://openid.net/extensions/sreg/1.1",
18 - "openid.realm": realm,
19 - "openid.return_to": return_to
20 - }
6 + @steam_login_url_base "https://steamcommunity.com/openid/login"
21 7
22 - "https://steamcommunity.com/openid/login?#{URI.encode_query(url_params)}"
8 + @doc """
9 + Returns the URL you should direct your users to in order to log in.
10 +
11 + `realm` should be set to your base URL, i.e. "http://example.com".
12 +
13 + `return_to` is the URL that Steam will redirect to after a successful auth.
14 + If you use `Steamex.Auth.Phoenix`, this is `realm <> "/steamex/return_to"`
15 + by default.
16 + """
17 + @spec auth_url(String.t, String.t) :: String.t
18 + def auth_url(realm, return_to) when is_binary(realm) and is_binary(return_to) do
19 + @steam_login_url_base <> "?" <> URI.encode_query %{
20 + "openid.claimed_id" => "http://specs.openid.net/auth/2.0/identifier_select",
21 + "openid.identity" => "http://specs.openid.net/auth/2.0/identifier_select",
22 + "openid.mode" => "checkid_setup",
23 + "openid.ns" => "http://specs.openid.net/auth/2.0",
24 + "openid.realm" => realm,
25 + "openid.return_to" => return_to
26 + }
27 + end
28 +
29 + @doc """
30 + Validate a "return_to" payload from Steam.
31 +
32 + You probably want to use `Steamex.Auth.Plug`/`Steamex.Auth.Phoenix`
33 + to handle this for you, but it's here in case you want to validate payloads
34 + manually.
35 +
36 + This function will raise an exception if validation fails, or return the
37 + user's Steam ID as an integer if it succeeds.
38 + """
39 + @spec validate_payload(map, atom) :: integer
40 + def validate_payload(payload, httpoison \\ HTTPoison) do
41 + signed = Dict.fetch!(payload, "openid.signed")
42 +
43 + signed_param_names = String.split(signed, ",") |> Enum.map(&"openid.#{&1}")
44 +
45 + params =
46 + Dict.take(payload, signed_param_names)
47 + |> Dict.merge %{
48 + "openid.assoc_handle" => Dict.fetch!(payload, "openid.assoc_handle"),
49 + "openid.sig" => Dict.fetch!(payload, "openid.sig"),
50 + "openid.ns" => Dict.fetch!(payload, "openid.ns"),
51 + "openid.mode" => "check_authentication"
52 + }
53 +
54 + Application.ensure_all_started(:httpoison)
55 +
56 + %HTTPoison.Response{status_code: 200, body: body} =
57 + httpoison.post! @steam_login_url_base, URI.encode_query(params), [
58 + {"Content-Type", "application/x-www-form-urlencoded"}
59 + ]
60 +
61 + unless String.contains? body, "is_valid:true" do
62 + raise "Invalid auth attempt: #{inspect payload}"
63 + end
64 +
65 + "http://steamcommunity.com/openid/id/" <> steamid64_str = payload["openid.identity"]
66 +
67 + {steamid64, ""} = Integer.parse(steamid64_str)
68 +
69 + steamid64
23 70 end
24 71 end
  @@ -0,0 +1,64 @@
1 + defmodule Steamex.Auth.Phoenix do
2 + @moduledoc """
3 + Conveniences for integrating with Phoenix.
4 +
5 + In your web.ex:
6 +
7 + def view do
8 + quote do
9 + ...
10 + use Steamex.Auth.Phoenix, :view
11 + ...
12 + end
13 + end
14 +
15 + def router do
16 + quote do
17 + ...
18 + use Steamex.Auth.Phoenix, :router
19 + ...
20 + end
21 + end
22 +
23 + In your router.ex (outside of any scopes, next to your routes):
24 +
25 + steamex_route_auth
26 +
27 + In your views, when you want to include a link to log in:
28 +
29 + steamex_auth_url(@conn)
30 +
31 + If a user successfully authenticates, they will be redirected to "/" and
32 + `:steamex_steamid64` will be set in their session to their integer steamid64.
33 +
34 + You can also set a different default redirection URL by setting in your config:
35 +
36 + config :steamex, Steamex,
37 + redirect_to: "/profile/me"
38 +
39 + You can also manually override the redirection URL on a per-link basis by
40 + calling steamex_auth_url like:
41 +
42 + steamex_auth_url(@conn, redirect_to: "/profile/me")
43 +
44 + The redirect_to parameter will only allow local paths to be specified.
45 +
46 + If you do not need a persistent user, you can just use this session value.
47 +
48 + If you do need a persistent user (i.e. in your DB), you can implement a Plug
49 + that will either find or create a new user for the steamid64 and assign it
50 + to conn.assigns for future use.
51 + """
52 +
53 + defmacro __using__(:router) do
54 + quote do
55 + import Steamex.Auth.Phoenix.Router
56 + end
57 + end
58 +
59 + defmacro __using__(:view) do
60 + quote do
61 + import Steamex.Auth.Phoenix.View
62 + end
63 + end
64 + end
  @@ -0,0 +1,32 @@
1 + defmodule Steamex.Auth.Phoenix.Router do
2 + @moduledoc """
3 + Router helpers for using Steamex auth with Phoenix.
4 + """
5 +
6 + @doc """
7 + Inserts a route for the Steamex auth return_to URL.
8 +
9 + This route (by default) is:
10 +
11 + get "/steamex/return_to", Steamex.Auth.Plug, [], as: :steamex_auth_return_to
12 +
13 + You can customize the return_to URL via the first parameter.
14 +
15 + The plug options can be altered via the second parameter. Please refer to
16 + `Steamex.Auth.Plug` for available options (if any).
17 +
18 + The router options can be altered via the final parameter. Please note that
19 + if you change the `:as` option, you must also alter your invocation of the
20 + `Steamex.Auth.Phoenix.View` helper. In this case it is suggested that you
21 + add your own view helper that calls the Steamex one with the appropriate
22 + options.
23 + """
24 + @spec steamex_route_auth(String.t, Keyword.t, Keyword.t) :: Macro.t
25 + defmacro steamex_route_auth(url \\ "/steamex/return_to", steamex_auth_plug_opts \\ [], router_opts \\ []) do
26 + router_opts = Keyword.merge([as: :steamex_auth_return_to], router_opts)
27 +
28 + quote do
29 + get unquote(url), Steamex.Auth.Plug, unquote(steamex_auth_plug_opts), unquote(router_opts)
30 + end
31 + end
32 + end
Loading more files…