From cac78a4f4eced88878e746d96b305ecc62826494 Mon Sep 17 00:00:00 2001 From: Logan Hunt Date: Tue, 31 Jan 2023 14:01:35 -0700 Subject: [PATCH] Move to discord oauth --- .env.example | 11 ++-- build.sh | 2 +- config/config.exs | 5 +- config/runtime.exs | 10 ++-- front/Dockerfile | 4 +- front/src/root.jsx | 7 ++- front/src/routes/home.jsx | 17 +++++-- lib/chessh/schema/player.ex | 6 +-- lib/chessh/ssh/client/client.ex | 6 ++- lib/chessh/web/web.ex | 51 +++++++++++-------- .../20221219082326_create_player.exs | 4 +- test/auth/password_test.exs | 2 +- test/auth/pubkey_test.exs | 2 +- test/schema/register_test.exs | 8 +-- test/ssh/ssh_auth_test.exs | 2 +- 15 files changed, 82 insertions(+), 55 deletions(-) diff --git a/.env.example b/.env.example index fb669c3..20ddef1 100644 --- a/.env.example +++ b/.env.example @@ -7,12 +7,15 @@ POSTGRES_PASSWORD=password POSTGRES_DATABASE=chessh WEB_PORT=8080 -REACT_APP_GITHUB_OAUTH=https://github.com/login/oauth/authorize?client_id=CLIENT_ID_HERE&redirect_uri=http://127.0.0.1:3000/api/oauth/redirect + +REACT_APP_DISCORD_INVITE=https://discord.gg/ajsdlkfjaskldjf +REACT_APP_DISCORD_OAUTH=https://discord.com/api/oauth2/authorize?client_id=CLIENT_ID&redirect_uri=FRONTEND_REDIRECT_PORT_FROM_BELOW&response_type=code&scope=identify + CLIENT_REDIRECT_AFTER_OAUTH=http://127.0.0.1:3000/auth-successful -GITHUB_CLIENT_ID= -GITHUB_CLIENT_SECRET= -GITHUB_USER_AGENT= +DISCORD_CLIENT_ID= +DISCORD_CLIENT_SECRET= +DISCORD_USER_AGENT= JWT_SECRET=aVerySecretJwtSigningSecret diff --git a/build.sh b/build.sh index 1cf7f4e..5781727 100755 --- a/build.sh +++ b/build.sh @@ -8,7 +8,7 @@ docker build . -t chessh/server cd front docker build \ - --build-arg REACT_APP_GITHUB_OAUTH=${REACT_APP_GITHUB_OAUTH} \ + --build-arg REACT_APP_DISCORD_OAUTH=${REACT_APP_DISCORD_OAUTH} \ --build-arg REACT_APP_SSH_SERVER=${REACT_APP_SSH_SERVER} \ --build-arg REACT_APP_SSH_PORT=${REACT_APP_SSH_PORT} \ . -t chessh/frontend diff --git a/config/config.exs b/config/config.exs index e9fca38..44230ae 100644 --- a/config/config.exs +++ b/config/config.exs @@ -17,8 +17,9 @@ config :chessh, RateLimits, create_game_rate: 2 config :chessh, Web, - github_oauth_login_url: "https://github.com/login/oauth/access_token", - github_user_api_url: "https://api.github.com/user" + discord_oauth_login_url: "https://discord.com/api/oauth2/token", + discord_user_api_url: "https://discord.com/api/users/@me", + discord_scope: "identify" config :joken, default_signer: "secret" diff --git a/config/runtime.exs b/config/runtime.exs index dfd8427..2be7431 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -1,14 +1,14 @@ import Config config :chessh, - ssh_port: String.to_integer(System.get_env("SSH_PORT", "42069")) + ssh_port: String.to_integer(System.get_env("SSH_PORT", "34355")) config :chessh, Web, - github_client_id: System.get_env("GITHUB_CLIENT_ID"), - github_client_secret: System.get_env("GITHUB_CLIENT_SECRET"), - github_user_agent: System.get_env("GITHUB_USER_AGENT"), + discord_client_id: System.get_env("DISCORD_CLIENT_ID"), + discord_client_secret: System.get_env("DISCORD_CLIENT_SECRET"), + discord_user_agent: System.get_env("DISCORD_USER_AGENT"), client_redirect_after_successful_sign_in: - System.get_env("CLIENT_REDIRECT_AFTER_OAUTH", "http://localhost:3000"), + System.get_env("CLIENT_REDIRECT_AFTER_OAUTH", "http://127.0.0.1:3000/oauth-successfule"), port: String.to_integer(System.get_env("WEB_PORT", "8080")) config :joken, diff --git a/front/Dockerfile b/front/Dockerfile index 03a4a4e..16f14f7 100644 --- a/front/Dockerfile +++ b/front/Dockerfile @@ -8,10 +8,10 @@ RUN npm ci COPY . /usr/app -ARG REACT_APP_GITHUB_OAUTH +ARG REACT_APP_DISCORD_OAUTH ARG REACT_APP_SSH_SERVER ARG REACT_APP_SSH_PORT -ENV REACT_APP_GITHUB_OAUTH $REACT_APP_GITHUB_OAUTH +ENV REACT_APP_DISCORD_OAUTH $REACT_APP_DISCORD_OAUTH ENV REACT_APP_SSH_SERVER $REACT_APP_SSH_SERVER ENV REACT_APP_SSH_PORT $REACT_APP_SSH_PORT RUN npm run build diff --git a/front/src/root.jsx b/front/src/root.jsx index 8de2575..1abc67b 100644 --- a/front/src/root.jsx +++ b/front/src/root.jsx @@ -34,8 +34,11 @@ export const Root = () => { ) : ( <> - - 🐙 Login w/ GitHub 🐙 + + 👾 Login w/ Discord 👾 )} diff --git a/front/src/routes/home.jsx b/front/src/routes/home.jsx index 0660a72..c65d87d 100644 --- a/front/src/routes/home.jsx +++ b/front/src/routes/home.jsx @@ -18,6 +18,14 @@ export const Home = () => {

Getting Started

    +
    +
  1. + Consider joining the{" "} + CheSSH Discord{" "} + to receive notifications when other players are looking for + opponents, or when it is your move in a game. +
  2. +
  3. Add a public key, or{" "} @@ -50,11 +58,14 @@ export const Home = () => { />
  4. -
  5. Finally, play chess!
  6. -

    Ideally, keeping the following contols in mind:

    +
  7. + Finally, play chess! Ideally, keeping the following contols in + mind: +
  8. +
    • Ctrl + b / Escape to return to the main menu.
    • -
    • Ctrl + c / Ctrl + d to exit at any point.
    • +
    • Ctrl + c / Ctrl + d to exit CheSSH at any point.
    • Arrow keys to move around the board.
    • Select a piece with "enter", and move it to a square by pressing diff --git a/lib/chessh/schema/player.ex b/lib/chessh/schema/player.ex index f12ad9e..9c83349 100644 --- a/lib/chessh/schema/player.ex +++ b/lib/chessh/schema/player.ex @@ -5,7 +5,7 @@ defmodule Chessh.Player do @derive {Inspect, except: [:password]} schema "players" do - field(:github_id, :integer) + field(:discord_id, :string) field(:username, :string) @@ -24,7 +24,7 @@ defmodule Chessh.Player do defimpl Jason.Encoder, for: Chessh.Player do def encode(value, opts) do Jason.Encode.map( - Map.take(value, [:id, :github_id, :username, :created_at, :updated_at]), + Map.take(value, [:id, :discord_id, :username, :created_at, :updated_at]), opts ) end @@ -37,7 +37,7 @@ defmodule Chessh.Player do def registration_changeset(player, attrs, opts \\ []) do player - |> cast(attrs, [:username, :password, :github_id]) + |> cast(attrs, [:username, :password, :discord_id]) |> validate_username() |> validate_password(opts) end diff --git a/lib/chessh/ssh/client/client.ex b/lib/chessh/ssh/client/client.ex index 461dfbe..9ec1250 100644 --- a/lib/chessh/ssh/client/client.ex +++ b/lib/chessh/ssh/client/client.ex @@ -1,7 +1,5 @@ defmodule Chessh.SSH.Client do alias IO.ANSI - require Logger - use GenServer @clear_codes [ @@ -164,6 +162,10 @@ defmodule Chessh.SSH.Client do "\e[B" -> :down "\e[D" -> :left "\e[C" -> :right + "\eOA" -> :up + "\eOB" -> :down + "\eOD" -> :left + "\eOC" -> :right "\r" -> :return x -> x end diff --git a/lib/chessh/web/web.ex b/lib/chessh/web/web.ex index 8c0929a..067a27c 100644 --- a/lib/chessh/web/web.ex +++ b/lib/chessh/web/web.ex @@ -2,7 +2,6 @@ defmodule Chessh.Web.Endpoint do alias Chessh.{Player, Repo, Key, PlayerSession} alias Chessh.Web.Token use Plug.Router - require Logger import Ecto.Query plug(Plug.Logger) @@ -17,27 +16,33 @@ defmodule Chessh.Web.Endpoint do plug(:dispatch) get "/oauth/redirect" do - [github_login_url, client_id, client_secret, github_user_api_url, github_user_agent] = - get_github_configs() + [ + discord_login_url, + discord_scope, + client_id, + client_secret, + discord_user_api_url, + discord_user_agent, + redirect_uri + ] = get_discord_configs() resp = case conn.params do %{"code" => req_token} -> case :httpc.request( :post, - {String.to_charlist( - "#{github_login_url}?client_id=#{client_id}&client_secret=#{client_secret}&code=#{req_token}" - ), [], 'application/json', ''}, + {String.to_charlist(discord_login_url), [], 'application/x-www-form-urlencoded', + 'scope=#{discord_scope}&client_id=#{client_id}&client_secret=#{client_secret}&code=#{req_token}&grant_type=authorization_code&redirect_uri=#{redirect_uri}'}, [], [] ) do {:ok, {{_, 200, 'OK'}, _, resp}} -> - URI.decode_query(String.Chars.to_string(resp)) + Jason.decode!(String.Chars.to_string(resp)) end end {status, body} = - create_player_from_github_response(resp, github_user_api_url, github_user_agent) + create_player_from_discord_response(resp, discord_user_api_url, discord_user_agent) conn |> assign_jwt_and_redirect_or_encode(status, body) @@ -200,14 +205,16 @@ defmodule Chessh.Web.Endpoint do end) end - defp get_github_configs() do + defp get_discord_configs() do Enum.map( [ - :github_oauth_login_url, - :github_client_id, - :github_client_secret, - :github_user_api_url, - :github_user_agent + :discord_oauth_login_url, + :discord_scope, + :discord_client_id, + :discord_client_secret, + :discord_user_api_url, + :discord_user_agent, + :client_redirect_after_successful_sign_in ], fn key -> Application.get_env(:chessh, Web)[key] end ) @@ -246,27 +253,27 @@ defmodule Chessh.Web.Endpoint do end end - defp create_player_from_github_response(resp, github_user_api_url, github_user_agent) do + defp create_player_from_discord_response(resp, discord_user_api_url, discord_user_agent) do case resp do %{"access_token" => access_token} -> case :httpc.request( :get, - {String.to_charlist(github_user_api_url), + {String.to_charlist(discord_user_api_url), [ {'Authorization', String.to_charlist("Bearer #{access_token}")}, - {'User-Agent', github_user_agent} + {'User-Agent', discord_user_agent} ]}, [], [] ) do {:ok, {{_, 200, 'OK'}, _, user_details}} -> - %{"login" => username, "id" => github_id} = + %{"username" => username, "id" => discord_id} = Jason.decode!(String.Chars.to_string(user_details)) %Player{id: id} = - Repo.insert!(%Player{github_id: github_id, username: username}, - on_conflict: [set: [github_id: github_id]], - conflict_target: :github_id + Repo.insert!(%Player{discord_id: discord_id, username: username}, + on_conflict: [set: [discord_id: discord_id]], + conflict_target: :discord_id ) {200, @@ -283,7 +290,7 @@ defmodule Chessh.Web.Endpoint do end _ -> - {400, %{errors: "Failed to retrieve token from GitHub. Try again."}} + {400, %{errors: "Failed to retrieve token from Discord. Try again."}} end end end diff --git a/priv/repo/migrations/20221219082326_create_player.exs b/priv/repo/migrations/20221219082326_create_player.exs index 8044344..0e605c9 100644 --- a/priv/repo/migrations/20221219082326_create_player.exs +++ b/priv/repo/migrations/20221219082326_create_player.exs @@ -5,13 +5,13 @@ defmodule Chessh.Repo.Migrations.CreatePlayer do execute("CREATE EXTENSION IF NOT EXISTS citext", "") create table(:players) do - add(:github_id, :integer, null: false) + add(:discord_id, :string, null: false) add(:username, :citext, null: false) add(:hashed_password, :string, null: true) timestamps() end create(unique_index(:players, [:username])) - create(unique_index(:players, [:github_id])) + create(unique_index(:players, [:discord_id])) end end diff --git a/test/auth/password_test.exs b/test/auth/password_test.exs index 55d9867..4072293 100644 --- a/test/auth/password_test.exs +++ b/test/auth/password_test.exs @@ -2,7 +2,7 @@ defmodule Chessh.Auth.PasswordAuthenticatorTest do use ExUnit.Case alias Chessh.{Player, Repo} - @valid_user %{username: "logan", password: "password", github_id: 1} + @valid_user %{username: "logan", password: "password", discord_id: "1"} setup_all do Ecto.Adapters.SQL.Sandbox.checkout(Repo) diff --git a/test/auth/pubkey_test.exs b/test/auth/pubkey_test.exs index 52d326a..690dfdf 100644 --- a/test/auth/pubkey_test.exs +++ b/test/auth/pubkey_test.exs @@ -2,7 +2,7 @@ defmodule Chessh.Auth.PublicKeyAuthenticatorTest do use ExUnit.Case alias Chessh.{Key, Repo, Player} - @valid_user %{username: "logan", password: "password", github_id: 2} + @valid_user %{username: "logan", password: "password", discord_id: "2"} @valid_key %{ name: "The Gamer Machine", key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ/2LOJGGEd/dhFgRxJ5MMv0jJw4s4pA8qmMbZyulN44" diff --git a/test/schema/register_test.exs b/test/schema/register_test.exs index 00ac413..6d32769 100644 --- a/test/schema/register_test.exs +++ b/test/schema/register_test.exs @@ -3,10 +3,10 @@ defmodule Chessh.Auth.UserRegistrationTest do use ExUnit.Case alias Chessh.{Player, Repo} - @valid_user %{username: "logan", password: "password", github_id: 4} - @invalid_username %{username: "a", password: "password", github_id: 7} - @invalid_password %{username: "aasdf", password: "pass", github_id: 6} - @repeated_username %{username: "LoGan", password: "password", github_id: 5} + @valid_user %{username: "logan", password: "password", discord_id: "4"} + @invalid_username %{username: "a", password: "password", discord_id: "7"} + @invalid_password %{username: "aasdf", password: "pass", discord_id: "6"} + @repeated_username %{username: "LoGan", password: "password", discord_id: "5"} test "Password must be at least 8 characters and username must be at least 2" do refute Player.registration_changeset(%Player{}, @invalid_password).valid? diff --git a/test/ssh/ssh_auth_test.exs b/test/ssh/ssh_auth_test.exs index 70a57be..024a54f 100644 --- a/test/ssh/ssh_auth_test.exs +++ b/test/ssh/ssh_auth_test.exs @@ -5,7 +5,7 @@ defmodule Chessh.SSH.AuthTest do @localhost '127.0.0.1' @localhost_inet {{127, 0, 0, 1}, 1} @key_name "The Gamer Machine" - @valid_user %{username: "logan", password: "password", github_id: 3} + @valid_user %{username: "logan", password: "password", discord_id: "3"} @client_test_keys_dir Path.join(Application.compile_env!(:chessh, :key_dir), "client_keys") @client_pub_key 'id_ed25519.pub'