mirror of
https://github.com/mgerb/classic-wow-forums
synced 2026-01-11 01:22:49 +00:00
authentication with persistence working
This commit is contained in:
@@ -21,7 +21,6 @@ defmodule MyApp.Guardian do
|
||||
# the resource id so here we'll rely on that to look it up.
|
||||
id = claims["sub"]
|
||||
# resource = MyApp.get_resource_by_id(id)
|
||||
IO.inspect(claims)
|
||||
resource = id
|
||||
{:ok, resource}
|
||||
end
|
||||
|
||||
@@ -4,7 +4,8 @@ defmodule MyApp.BattleNet.Auth do
|
||||
|
||||
def token_uri, do: "https://us.battle.net/oauth/token"
|
||||
|
||||
def get_token(code) do
|
||||
@spec get_access_token(String.t) :: {:ok, String.t} | {:error, String.t}
|
||||
def get_access_token(code) do
|
||||
client_id = Application.get_env(:myapp, :bnet_client_id)
|
||||
client_secret = Application.get_env(:myapp, :bnet_client_secret)
|
||||
redirect_uri = Application.get_env(:myapp, :bnet_redirect_uri)
|
||||
@@ -14,28 +15,16 @@ defmodule MyApp.BattleNet.Auth do
|
||||
HTTPoison.request(:post, token_uri, get_req_body(code), [], req_options)
|
||||
|> parse_body
|
||||
|> parse_token
|
||||
|> validate_user
|
||||
|> generate_jwt
|
||||
end
|
||||
|
||||
defp parse_body({:error, err}), do: {:error, err}
|
||||
defp parse_body({:ok, %HTTPoison.Response{body: body}}), do: Poison.decode(body)
|
||||
|
||||
defp parse_token({:ok, %{"access_token" => token}}), do: {:ok, token}
|
||||
defp parse_token({:ok, %{"error" => err}}), do: {:error, err}
|
||||
defp parse_token({:ok, %{"error" => error}}), do: {:error, error}
|
||||
defp parse_token({:error, err}), do: {:error, "Authentication error"}
|
||||
|
||||
defp validate_user({:error, err}), do: {:error, err}
|
||||
defp validate_user({:ok, token}), do: User.get_user(token)
|
||||
|
||||
defp generate_jwt({:error, err}), do: {:error, err}
|
||||
defp generate_jwt({:ok, user}) do
|
||||
case JWT.get_jwt(user, user) do
|
||||
{:ok, token} -> {:ok, Map.merge(user, %{"token" => token})}
|
||||
{:error, err} -> {:error, err}
|
||||
end
|
||||
end
|
||||
|
||||
@spec get_req_body(String.t) :: tuple
|
||||
defp get_req_body(code) do
|
||||
redirect_uri = Application.get_env(:myapp, :bnet_redirect_uri)
|
||||
{:form, [
|
||||
|
||||
@@ -1,16 +1,32 @@
|
||||
defmodule MyApp.BattleNet.User do
|
||||
defstruct id: nil, battletag: nil
|
||||
|
||||
@type battle_net_user :: %{"battle_net_id": integer, "battletag": String.t, "access_token": String.t}
|
||||
|
||||
def api_url, do: "https://us.api.battle.net"
|
||||
|
||||
def get_user(access_token) do
|
||||
case HTTPoison.get(resource_url("account/user", access_token)) do
|
||||
{:ok, %HTTPoison.Response{body: body}} -> {:ok, Poison.decode!(body, as: Battlenet.User)}
|
||||
{:error, err} -> {:error, err}
|
||||
# grab user information from battle net api - use token for auth
|
||||
@spec get_user(String.t | {atom, any}) :: {:ok, battle_net_user} | {:error, any}
|
||||
def get_user(access_token) when is_binary(access_token) do
|
||||
HTTPoison.get(resource_url("account/user", access_token))
|
||||
|> parse_user_response(access_token)
|
||||
end
|
||||
def get_user({:ok, access_token}), do: get_user(access_token)
|
||||
def get_user({:error, error}), do: {:error, error}
|
||||
|
||||
defp parse_user_response({:error, error}, _), do: {:error, error}
|
||||
defp parse_user_response({:ok, %HTTPoison.Response{body: body}}, access_token) do
|
||||
case Poison.decode(body) do
|
||||
{:ok, user} ->
|
||||
user = user
|
||||
|> Map.merge(%{"access_token" => access_token}) # add access token to return map
|
||||
|> Map.put("battle_net_id", Map.get(user, "id")) # change id key to battle_net_id
|
||||
|> Map.delete("id") # remove id key
|
||||
{:ok, user}
|
||||
{:error, error} -> {:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
defp resource_url(path, access_token) do
|
||||
"#{api_url}/#{path}?access_token=#{access_token}"
|
||||
"#{api_url()}/#{path}?access_token=#{access_token}"
|
||||
end
|
||||
end
|
||||
|
||||
81
lib/myapp/data/user.ex
Normal file
81
lib/myapp/data/user.ex
Normal file
@@ -0,0 +1,81 @@
|
||||
defmodule MyApp.Data.User do
|
||||
use Ecto.Schema
|
||||
import Ecto.Query
|
||||
import Ecto.Changeset
|
||||
alias MyApp.Repo
|
||||
alias MyApp.Data
|
||||
|
||||
@derive {Poison.Encoder, except: [:__meta__]}
|
||||
schema "user" do
|
||||
field :battle_net_id, :integer
|
||||
field :battletag, :string
|
||||
timestamps()
|
||||
end
|
||||
|
||||
def changeset(user, params \\ %{}) do
|
||||
user
|
||||
|> cast(params, [:battle_net_id, :battletag])
|
||||
|> validate_required([:battle_net_id, :battletag])
|
||||
|> unique_constraint(:battle_net_id)
|
||||
end
|
||||
|
||||
@spec get_user(integer) :: nil | map
|
||||
defp get_user(battle_net_id) do
|
||||
query = from u in "user",
|
||||
where: u.battle_net_id == ^battle_net_id,
|
||||
select: [:id, :battle_net_id, :battletag]
|
||||
Repo.one(query)
|
||||
end
|
||||
|
||||
# insert user info in database - if not exists - update battletag if it has changed
|
||||
@spec upsert_user(%{"battle_net_id": integer, "battletag": String.t} | tuple) :: {:ok, map} | {:error, any}
|
||||
def upsert_user(params) when is_map(params) do
|
||||
# check for current user in database
|
||||
case get_user(Map.get(params, "battle_net_id")) do
|
||||
nil -> insert_user(params)
|
||||
user ->
|
||||
# update user if battletag has changed
|
||||
if Map.get(user, :battletag) != Map.get(params, "battletag") do
|
||||
update_battletag(user, params)
|
||||
else
|
||||
{:ok, user}
|
||||
end
|
||||
end
|
||||
|> add_access_token(Map.get(params, "access_token"))
|
||||
end
|
||||
def upsert_user({:ok, params}), do: upsert_user(params)
|
||||
def upsert_user({:error, error}), do: {:error, error}
|
||||
|
||||
# need to add token back to map because we don't store it in the database
|
||||
defp add_access_token({:error, error}, _), do: {:error, error}
|
||||
defp add_access_token({:ok, user}, access_token) do
|
||||
{:ok, Map.merge(user, %{access_token: access_token})}
|
||||
end
|
||||
|
||||
defp insert_user(params) do
|
||||
cs = changeset(%Data.User{}, params)
|
||||
cs
|
||||
|> Repo.insert
|
||||
|> process_insert_or_update
|
||||
end
|
||||
|
||||
# it's possible for a user's battle tag to change - if so update it
|
||||
defp update_battletag(user, params) do
|
||||
cs = Data.User.changeset(Map.merge(%Data.User{}, user), %{battletag: Map.get(params, "battletag")})
|
||||
cs
|
||||
|> Repo.update
|
||||
|> process_insert_or_update
|
||||
end
|
||||
|
||||
defp process_insert_or_update({:error, changeset}), do: {:error, map_changeset(changeset)}
|
||||
defp process_insert_or_update({:ok, user}) do
|
||||
{:ok, Map.take(user, [:id, :battle_net_id, :battletag])} # only grab the fields we need
|
||||
end
|
||||
|
||||
defp map_changeset(changeset) do
|
||||
Enum.map(changeset.errors, fn {key, val} ->
|
||||
%{key => elem(val, 0)}
|
||||
end)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -4,11 +4,15 @@ defmodule MyApp.JWT do
|
||||
# ~1 year
|
||||
defp tokenTTL(), do: {52, :weeks}
|
||||
|
||||
def get_jwt(user, claims) do
|
||||
case Guardian.encode_and_sign(user, claims, ttl: tokenTTL()) do
|
||||
{:ok, token, _claims} -> {:ok, token}
|
||||
{:error, _token, _claims} -> {:error, "JWT error"}
|
||||
@spec add_jwt(map | {atom, any}) :: {:ok, map} | {:error, String.t}
|
||||
def add_jwt(user) when is_map(user) do
|
||||
case Guardian.encode_and_sign(user, user, ttl: tokenTTL()) do
|
||||
{:ok, token, _claims} -> {:ok, Map.merge(user, %{token: token})}
|
||||
{:error, error} -> {:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
def add_jwt({:ok, user}), do: add_jwt(user)
|
||||
def add_jwt({:error, error}), do: {:error, error}
|
||||
|
||||
end
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
defmodule MyApp.Repo do
|
||||
use Ecto.Repo, otp_app: :myapp
|
||||
@dialyzer {:nowarn_function, rollback: 1}
|
||||
|
||||
@doc """
|
||||
Dynamically loads the repository url from the
|
||||
|
||||
Reference in New Issue
Block a user