Current section
8 Versions
Jump to
Current section
8 Versions
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…