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

a bunch of fixes #5

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 34 additions & 100 deletions lib/ueberauth/strategy/steam.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,92 +4,74 @@ defmodule Ueberauth.Strategy.Steam do
"""

use Ueberauth.Strategy

alias Ueberauth.Auth.Info
alias Ueberauth.Auth.Extra

defdelegate checkid_setup_url(callback_url), to: __MODULE__.OpenID
defdelegate check_authentication(params), to: __MODULE__.OpenID
defdelegate get_steam_user_id(claimed_id), to: __MODULE__.OpenID
defdelegate get_steam_user(steam_user_id), to: __MODULE__.API

@doc ~S"""
Handles initial request for Steam authentication.

Redirects the given `conn` to the Steam login page.
"""
@spec handle_request!(Plug.Conn.t) :: Plug.Conn.t
def handle_request!(conn) do
query =
%{
"openid.mode" => "checkid_setup",
"openid.realm" => callback_url(conn),
"openid.return_to" => callback_url(conn),
"openid.ns" => "http://specs.openid.net/auth/2.0",
"openid.claimed_id" => "http://specs.openid.net/auth/2.0/identifier_select",
"openid.identity" => "http://specs.openid.net/auth/2.0/identifier_select",
}
|> URI.encode_query

redirect!(conn, "https://steamcommunity.com/openid/login?" <> query)
redirect!(conn, checkid_setup_url(callback_url(conn)))
end

@doc ~S"""
Handles the callback from Steam.
"""
@spec handle_callback!(Plug.Conn.t) :: Plug.Conn.t
def handle_callback!(conn = %Plug.Conn{params: %{"openid.mode" => "id_res"}}) do
params = conn.params

[valid, user] =
[ # Validate and retrieve the steam user at the same time.
fn -> validate_user(params) end,
fn -> retrieve_user(params) end,
]
|> Enum.map(&Task.async/1)
|> Enum.map(&Task.await/1)

case valid && !is_nil(user) do
true ->
conn
|> put_private(:steam_user, user)
false ->
set_errors!(conn, [error("invalid_user", "Invalid steam user")])
def handle_callback!(%{params: %{"openid.mode" => "id_res"}} = conn) do
with \
{:ok, %{"openid.claimed_id" => claimed_id}} <- check_authentication(conn.params),
{:ok, steam_user_id} <- get_steam_user_id(claimed_id),
{:ok, steam_user} <- get_steam_user(steam_user_id)
do
conn |> put_private(:steam_user, steam_user)
else
{:error, :invalid_request} ->
set_errors!(conn, [error("invalid_openid", "Invalid OpenID authentication request")])
{:error, :invalid_user} ->
set_errors!(conn, [error("invalid_user", "Invalid Steam user")])
_ ->
set_errors!(conn, [error("invalid_response", "Invalid response")])
end
end

@doc false
def handle_callback!(conn) do
set_errors!(conn, [error("invalid_openid", "Invalid openid response received")])
set_errors!(conn, [error("invalid_request", "Invalid request")])
end

@doc false
@spec handle_cleanup!(Plug.Conn.t) :: Plug.Conn.t
def handle_cleanup!(conn) do
conn
|> put_private(:steam_user, nil)
conn |> put_private(:steam_user, nil)
end

@doc ~S"""
Fetches the uid field from the response.

Takes the `steamid` from the `steamuser` saved in the `conn`.
Takes the information from `steam_user` saved in `conn`.
"""
@spec uid(Plug.Conn.t) :: pos_integer
def uid(conn) do
conn.private.steam_user.steamid |> String.to_integer
String.to_integer(conn.private.steam_user["steamid"])
end

@doc ~S"""
Fetches the fields to populate the info section of the `Ueberauth.Auth` struct.

Takes the information from the `steamuser` saved in the `conn`.
Takes the information from `steam_user` saved in `conn`.
"""
@spec info(Plug.Conn.t) :: Info.t
def info(conn) do
user = conn.private.steam_user

steam_user = conn.private.steam_user
%Info{
image: user.avatar,
name: user.realname,
location: user.loccountrycode,
name: steam_user["realname"],
nickname: steam_user["personaname"],
image: steam_user["avatar"],
location: steam_user["loccountrycode"],
urls: %{
Steam: user.profileurl,
steam_profile: steam_user["profileurl"]
}
}
end
Expand All @@ -99,59 +81,11 @@ defmodule Ueberauth.Strategy.Steam do

Returns the `steamuser` saved in the `conn` as `raw_info`.
"""
@spec extra(Plug.Conn.t) :: Extra.t
def extra(conn) do
%Extra{
raw_info: %{
user: conn.private.steam_user
}
}
end

@spec retrieve_user(map) :: map | nil
defp retrieve_user(%{"openid.claimed_id" => "http://steamcommunity.com/openid/id/" <> id}) do
key =
:ueberauth
|> Application.fetch_env!(Ueberauth.Strategy.Steam)
|> Keyword.get(:api_key)
url = "https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=" <> key <> "&steamids=" <> id

with {:ok, %HTTPoison.Response{body: body}} <- HTTPoison.get(url),
{:ok, user} <- Poison.decode(body, keys: :atoms)
do
List.first(user.response.players)
else
_ -> nil
end
end

@spec validate_user(map) :: boolean
defp validate_user(params) do
query =
params
|> Enum.filter(fn {key, _value} -> String.starts_with?(key, "openid.") end)
|> Enum.into(%{})
|> Map.put("openid.mode", "check_authentication")
|> URI.encode_query

case HTTPoison.get("https://steamcommunity.com/openid/login?" <> query) do
{:ok, %HTTPoison.Response{body: body, status_code: 200}} ->
String.contains?(body, "is_valid:true\n")
_ ->
false
end
end

# Block undocumented function
@doc false
@spec default_options :: []
def default_options

@doc false
@spec credentials(Plug.Conn.t) :: Ueberauth.Auth.Credentials.t
def credentials(_conn), do: %Ueberauth.Auth.Credentials{}

@doc false
@spec auth(Plug.Conn.t) :: Ueberauth.Auth.t
def auth(conn)
end
21 changes: 21 additions & 0 deletions lib/ueberauth/strategy/steam/api.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
defmodule Ueberauth.Strategy.Steam.API do
@url_steam_summaries "https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002"

defp get_steam_api_key do
{:ok, env} = Application.fetch_env(:ueberauth, Ueberauth.Strategy.Steam)
{:ok, api_key} = Keyword.fetch(env, :api_key)
api_key
end

def get_steam_user(steam_user_id) do
with \
params <- %{key: get_steam_api_key(), steamids: steam_user_id},
{:ok, %{body: body}} <- HTTPoison.get(@url_steam_summaries, [], params: params),
{:ok, %{"response" => %{"players" => [player|_]}}} <- Poison.decode(body)
do
{:ok, player}
else _ ->
{:error, :invalid_user}
end
end
end
34 changes: 34 additions & 0 deletions lib/ueberauth/strategy/steam/openid.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
defmodule Ueberauth.Strategy.Steam.OpenID do
@url_namespace "http://specs.openid.net/auth/2.0"
@url_login "https://steamcommunity.com/openid/login"

def checkid_setup_url(callback_url) do
query = checkid_setup_query(callback_url, callback_url)
@url_login <> "?" <> URI.encode_query(query)
end

defp checkid_setup_query(realm, return_to) do
%{
"openid.mode" => "checkid_setup",
"openid.realm" => realm,
"openid.return_to" => return_to,
"openid.ns" => @url_namespace,
"openid.claimed_id" => @url_namespace <> "/identifier_select",
"openid.identity" => @url_namespace <> "/identifier_select",
}
end

def check_authentication(params) do
check_params = Map.put(params, "openid.mode", "check_authentication")
case HTTPoison.get(@url_login, [], params: check_params) do
{:ok, %{status_code: 200, body: "ns:" <> @url_namespace <> "\nis_valid:true\n"}} ->
{:ok, params}
_ ->
{:error, :invalid_request}
end
end

def get_steam_user_id("http://steamcommunity.com/openid/id/" <> id), do: {:ok, id}
def get_steam_user_id("https://steamcommunity.com/openid/id/" <> id), do: {:ok, id}
def get_steam_user_id(_), do: {:error, :badarg}
end
4 changes: 2 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ defmodule UeberauthSteam.Mixfile do

# Testing
test_coverage: [tool: ExCoveralls],
preferred_cli_env: ["coveralls": :test, "coveralls.detail": :test, "coveralls.post": :test, "coveralls.html": :test],
preferred_cli_env: [coveralls: :test, "coveralls.detail": :test, "coveralls.post": :test, "coveralls.html": :test],
dialyzer: [ignore_warnings: "dialyzer.ignore-warnings"],

# Docs
Expand Down Expand Up @@ -54,7 +54,7 @@ defmodule UeberauthSteam.Mixfile do
{:ueberauth, "~> 0.4"},

# Testing
{:meck, "~> 0.8.4", only: :test},
{:meck, "~> 0.8.12", only: :test},

# Code Maintenance
{:credo, "~> 0.7", only: [:dev, :test]},
Expand Down
8 changes: 5 additions & 3 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
%{"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], []},
%{
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], []},
"certifi": {:hex, :certifi, "1.1.0", "c9b71a547016c2528a590ccfc28de786c7edb74aafa17446b84f54e04efc00ee", [:rebar3], []},
"credo": {:hex, :credo, "0.7.4", "0c33bcce4d574ce6df163cbc7d1ecb22de65713184355bd3be81cc4ab0ecaafa", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, optional: false]}]},
"dialyxir": {:hex, :dialyxir, "0.5.0", "5bc543f9c28ecd51b99cc1a685a3c2a1a93216990347f259406a910cf048d1d7", [:mix], []},
Expand All @@ -11,12 +12,13 @@
"idna": {:hex, :idna, "4.0.0", "10aaa9f79d0b12cf0def53038547855b91144f1bfcc0ec73494f38bb7b9c4961", [:rebar3], []},
"inch_ex": {:hex, :inch_ex, "0.5.6", "418357418a553baa6d04eccd1b44171936817db61f4c0840112b420b8e378e67", [:mix], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, optional: false]}]},
"jsx": {:hex, :jsx, "2.8.2", "7acc7d785b5abe8a6e9adbde926a24e481f29956dd8b4df49e3e4e7bcc92a018", [:mix, :rebar3], []},
"meck": {:hex, :meck, "0.8.4", "59ca1cd971372aa223138efcf9b29475bde299e1953046a0c727184790ab1520", [:make, :rebar], []},
"meck": {:hex, :meck, "0.8.12", "1f7b1a9f5d12c511848fec26bbefd09a21e1432eadb8982d9a8aceb9891a3cf2", [:rebar3], [], "hexpm"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []},
"mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [:mix], []},
"mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []},
"oauth2": {:hex, :oauth2, "0.9.1", "cac86d87f35ec835bfe4c791263bdb88c0d8bf1617d64f555ede4e9d913e35ef", [:mix], [{:hackney, "~> 1.7", [hex: :hackney, optional: false]}]},
"plug": {:hex, :plug, "1.3.5", "7503bfcd7091df2a9761ef8cecea666d1f2cc454cbbaf0afa0b6e259203b7031", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [hex: :mime, optional: false]}]},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], []},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], []},
"ueberauth": {:hex, :ueberauth, "0.4.0", "bc72d5e5a7bdcbfcf28a756e34630816edabc926303bdce7e171f7ac7ffa4f91", [:mix], [{:plug, "~> 1.2", [hex: :plug, optional: false]}]}}
"ueberauth": {:hex, :ueberauth, "0.4.0", "bc72d5e5a7bdcbfcf28a756e34630816edabc926303bdce7e171f7ac7ffa4f91", [:mix], [{:plug, "~> 1.2", [hex: :plug, optional: false]}]},
}
Loading