Move to discord oauth
This commit is contained in:
parent
e5d97870a1
commit
cac78a4f4e
11
.env.example
11
.env.example
@ -7,12 +7,15 @@ POSTGRES_PASSWORD=password
|
|||||||
POSTGRES_DATABASE=chessh
|
POSTGRES_DATABASE=chessh
|
||||||
|
|
||||||
WEB_PORT=8080
|
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
|
CLIENT_REDIRECT_AFTER_OAUTH=http://127.0.0.1:3000/auth-successful
|
||||||
|
|
||||||
GITHUB_CLIENT_ID=
|
DISCORD_CLIENT_ID=
|
||||||
GITHUB_CLIENT_SECRET=
|
DISCORD_CLIENT_SECRET=
|
||||||
GITHUB_USER_AGENT=
|
DISCORD_USER_AGENT=
|
||||||
|
|
||||||
JWT_SECRET=aVerySecretJwtSigningSecret
|
JWT_SECRET=aVerySecretJwtSigningSecret
|
||||||
|
|
||||||
|
2
build.sh
2
build.sh
@ -8,7 +8,7 @@ docker build . -t chessh/server
|
|||||||
|
|
||||||
cd front
|
cd front
|
||||||
docker build \
|
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_SERVER=${REACT_APP_SSH_SERVER} \
|
||||||
--build-arg REACT_APP_SSH_PORT=${REACT_APP_SSH_PORT} \
|
--build-arg REACT_APP_SSH_PORT=${REACT_APP_SSH_PORT} \
|
||||||
. -t chessh/frontend
|
. -t chessh/frontend
|
||||||
|
@ -17,8 +17,9 @@ config :chessh, RateLimits,
|
|||||||
create_game_rate: 2
|
create_game_rate: 2
|
||||||
|
|
||||||
config :chessh, Web,
|
config :chessh, Web,
|
||||||
github_oauth_login_url: "https://github.com/login/oauth/access_token",
|
discord_oauth_login_url: "https://discord.com/api/oauth2/token",
|
||||||
github_user_api_url: "https://api.github.com/user"
|
discord_user_api_url: "https://discord.com/api/users/@me",
|
||||||
|
discord_scope: "identify"
|
||||||
|
|
||||||
config :joken, default_signer: "secret"
|
config :joken, default_signer: "secret"
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import Config
|
import Config
|
||||||
|
|
||||||
config :chessh,
|
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,
|
config :chessh, Web,
|
||||||
github_client_id: System.get_env("GITHUB_CLIENT_ID"),
|
discord_client_id: System.get_env("DISCORD_CLIENT_ID"),
|
||||||
github_client_secret: System.get_env("GITHUB_CLIENT_SECRET"),
|
discord_client_secret: System.get_env("DISCORD_CLIENT_SECRET"),
|
||||||
github_user_agent: System.get_env("GITHUB_USER_AGENT"),
|
discord_user_agent: System.get_env("DISCORD_USER_AGENT"),
|
||||||
client_redirect_after_successful_sign_in:
|
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"))
|
port: String.to_integer(System.get_env("WEB_PORT", "8080"))
|
||||||
|
|
||||||
config :joken,
|
config :joken,
|
||||||
|
@ -8,10 +8,10 @@ RUN npm ci
|
|||||||
|
|
||||||
COPY . /usr/app
|
COPY . /usr/app
|
||||||
|
|
||||||
ARG REACT_APP_GITHUB_OAUTH
|
ARG REACT_APP_DISCORD_OAUTH
|
||||||
ARG REACT_APP_SSH_SERVER
|
ARG REACT_APP_SSH_SERVER
|
||||||
ARG REACT_APP_SSH_PORT
|
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_SERVER $REACT_APP_SSH_SERVER
|
||||||
ENV REACT_APP_SSH_PORT $REACT_APP_SSH_PORT
|
ENV REACT_APP_SSH_PORT $REACT_APP_SSH_PORT
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
@ -34,8 +34,11 @@ export const Root = () => {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<a href={process.env.REACT_APP_GITHUB_OAUTH} className="button">
|
<a
|
||||||
🐙 Login w/ GitHub 🐙
|
href={process.env.REACT_APP_DISCORD_OAUTH}
|
||||||
|
className="button"
|
||||||
|
>
|
||||||
|
👾 Login w/ Discord 👾
|
||||||
</a>
|
</a>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -18,6 +18,14 @@ export const Home = () => {
|
|||||||
<hr />
|
<hr />
|
||||||
<h3>Getting Started</h3>
|
<h3>Getting Started</h3>
|
||||||
<ol>
|
<ol>
|
||||||
|
<div>
|
||||||
|
<li>
|
||||||
|
Consider joining the{" "}
|
||||||
|
<a href={process.env.REACT_APP_DISCORD_INVITE}>CheSSH Discord</a>{" "}
|
||||||
|
to receive notifications when other players are looking for
|
||||||
|
opponents, or when it is your move in a game.
|
||||||
|
</li>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<li>
|
<li>
|
||||||
Add a <Link to="/keys">public key</Link>, or{" "}
|
Add a <Link to="/keys">public key</Link>, or{" "}
|
||||||
@ -50,11 +58,14 @@ export const Home = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<li>Finally, play chess!</li>
|
<li>
|
||||||
<p>Ideally, keeping the following contols in mind:</p>
|
Finally, play chess! Ideally, keeping the following contols in
|
||||||
|
mind:
|
||||||
|
</li>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Ctrl + b / Escape to return to the main menu.</li>
|
<li>Ctrl + b / Escape to return to the main menu.</li>
|
||||||
<li>Ctrl + c / Ctrl + d to exit at any point.</li>
|
<li>Ctrl + c / Ctrl + d to exit CheSSH at any point.</li>
|
||||||
<li>Arrow keys to move around the board.</li>
|
<li>Arrow keys to move around the board.</li>
|
||||||
<li>
|
<li>
|
||||||
Select a piece with "enter", and move it to a square by pressing
|
Select a piece with "enter", and move it to a square by pressing
|
||||||
|
@ -5,7 +5,7 @@ defmodule Chessh.Player do
|
|||||||
|
|
||||||
@derive {Inspect, except: [:password]}
|
@derive {Inspect, except: [:password]}
|
||||||
schema "players" do
|
schema "players" do
|
||||||
field(:github_id, :integer)
|
field(:discord_id, :string)
|
||||||
|
|
||||||
field(:username, :string)
|
field(:username, :string)
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ defmodule Chessh.Player do
|
|||||||
defimpl Jason.Encoder, for: Chessh.Player do
|
defimpl Jason.Encoder, for: Chessh.Player do
|
||||||
def encode(value, opts) do
|
def encode(value, opts) do
|
||||||
Jason.Encode.map(
|
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
|
opts
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
@ -37,7 +37,7 @@ defmodule Chessh.Player do
|
|||||||
|
|
||||||
def registration_changeset(player, attrs, opts \\ []) do
|
def registration_changeset(player, attrs, opts \\ []) do
|
||||||
player
|
player
|
||||||
|> cast(attrs, [:username, :password, :github_id])
|
|> cast(attrs, [:username, :password, :discord_id])
|
||||||
|> validate_username()
|
|> validate_username()
|
||||||
|> validate_password(opts)
|
|> validate_password(opts)
|
||||||
end
|
end
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
defmodule Chessh.SSH.Client do
|
defmodule Chessh.SSH.Client do
|
||||||
alias IO.ANSI
|
alias IO.ANSI
|
||||||
require Logger
|
|
||||||
|
|
||||||
use GenServer
|
use GenServer
|
||||||
|
|
||||||
@clear_codes [
|
@clear_codes [
|
||||||
@ -164,6 +162,10 @@ defmodule Chessh.SSH.Client do
|
|||||||
"\e[B" -> :down
|
"\e[B" -> :down
|
||||||
"\e[D" -> :left
|
"\e[D" -> :left
|
||||||
"\e[C" -> :right
|
"\e[C" -> :right
|
||||||
|
"\eOA" -> :up
|
||||||
|
"\eOB" -> :down
|
||||||
|
"\eOD" -> :left
|
||||||
|
"\eOC" -> :right
|
||||||
"\r" -> :return
|
"\r" -> :return
|
||||||
x -> x
|
x -> x
|
||||||
end
|
end
|
||||||
|
@ -2,7 +2,6 @@ defmodule Chessh.Web.Endpoint do
|
|||||||
alias Chessh.{Player, Repo, Key, PlayerSession}
|
alias Chessh.{Player, Repo, Key, PlayerSession}
|
||||||
alias Chessh.Web.Token
|
alias Chessh.Web.Token
|
||||||
use Plug.Router
|
use Plug.Router
|
||||||
require Logger
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
plug(Plug.Logger)
|
plug(Plug.Logger)
|
||||||
@ -17,27 +16,33 @@ defmodule Chessh.Web.Endpoint do
|
|||||||
plug(:dispatch)
|
plug(:dispatch)
|
||||||
|
|
||||||
get "/oauth/redirect" do
|
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 =
|
resp =
|
||||||
case conn.params do
|
case conn.params do
|
||||||
%{"code" => req_token} ->
|
%{"code" => req_token} ->
|
||||||
case :httpc.request(
|
case :httpc.request(
|
||||||
:post,
|
:post,
|
||||||
{String.to_charlist(
|
{String.to_charlist(discord_login_url), [], 'application/x-www-form-urlencoded',
|
||||||
"#{github_login_url}?client_id=#{client_id}&client_secret=#{client_secret}&code=#{req_token}"
|
'scope=#{discord_scope}&client_id=#{client_id}&client_secret=#{client_secret}&code=#{req_token}&grant_type=authorization_code&redirect_uri=#{redirect_uri}'},
|
||||||
), [], 'application/json', ''},
|
|
||||||
[],
|
[],
|
||||||
[]
|
[]
|
||||||
) do
|
) do
|
||||||
{:ok, {{_, 200, 'OK'}, _, resp}} ->
|
{:ok, {{_, 200, 'OK'}, _, resp}} ->
|
||||||
URI.decode_query(String.Chars.to_string(resp))
|
Jason.decode!(String.Chars.to_string(resp))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
{status, body} =
|
{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
|
conn
|
||||||
|> assign_jwt_and_redirect_or_encode(status, body)
|
|> assign_jwt_and_redirect_or_encode(status, body)
|
||||||
@ -200,14 +205,16 @@ defmodule Chessh.Web.Endpoint do
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_github_configs() do
|
defp get_discord_configs() do
|
||||||
Enum.map(
|
Enum.map(
|
||||||
[
|
[
|
||||||
:github_oauth_login_url,
|
:discord_oauth_login_url,
|
||||||
:github_client_id,
|
:discord_scope,
|
||||||
:github_client_secret,
|
:discord_client_id,
|
||||||
:github_user_api_url,
|
:discord_client_secret,
|
||||||
:github_user_agent
|
:discord_user_api_url,
|
||||||
|
:discord_user_agent,
|
||||||
|
:client_redirect_after_successful_sign_in
|
||||||
],
|
],
|
||||||
fn key -> Application.get_env(:chessh, Web)[key] end
|
fn key -> Application.get_env(:chessh, Web)[key] end
|
||||||
)
|
)
|
||||||
@ -246,27 +253,27 @@ defmodule Chessh.Web.Endpoint do
|
|||||||
end
|
end
|
||||||
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
|
case resp do
|
||||||
%{"access_token" => access_token} ->
|
%{"access_token" => access_token} ->
|
||||||
case :httpc.request(
|
case :httpc.request(
|
||||||
:get,
|
:get,
|
||||||
{String.to_charlist(github_user_api_url),
|
{String.to_charlist(discord_user_api_url),
|
||||||
[
|
[
|
||||||
{'Authorization', String.to_charlist("Bearer #{access_token}")},
|
{'Authorization', String.to_charlist("Bearer #{access_token}")},
|
||||||
{'User-Agent', github_user_agent}
|
{'User-Agent', discord_user_agent}
|
||||||
]},
|
]},
|
||||||
[],
|
[],
|
||||||
[]
|
[]
|
||||||
) do
|
) do
|
||||||
{:ok, {{_, 200, 'OK'}, _, user_details}} ->
|
{:ok, {{_, 200, 'OK'}, _, user_details}} ->
|
||||||
%{"login" => username, "id" => github_id} =
|
%{"username" => username, "id" => discord_id} =
|
||||||
Jason.decode!(String.Chars.to_string(user_details))
|
Jason.decode!(String.Chars.to_string(user_details))
|
||||||
|
|
||||||
%Player{id: id} =
|
%Player{id: id} =
|
||||||
Repo.insert!(%Player{github_id: github_id, username: username},
|
Repo.insert!(%Player{discord_id: discord_id, username: username},
|
||||||
on_conflict: [set: [github_id: github_id]],
|
on_conflict: [set: [discord_id: discord_id]],
|
||||||
conflict_target: :github_id
|
conflict_target: :discord_id
|
||||||
)
|
)
|
||||||
|
|
||||||
{200,
|
{200,
|
||||||
@ -283,7 +290,7 @@ defmodule Chessh.Web.Endpoint do
|
|||||||
end
|
end
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
{400, %{errors: "Failed to retrieve token from GitHub. Try again."}}
|
{400, %{errors: "Failed to retrieve token from Discord. Try again."}}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -5,13 +5,13 @@ defmodule Chessh.Repo.Migrations.CreatePlayer do
|
|||||||
execute("CREATE EXTENSION IF NOT EXISTS citext", "")
|
execute("CREATE EXTENSION IF NOT EXISTS citext", "")
|
||||||
|
|
||||||
create table(:players) do
|
create table(:players) do
|
||||||
add(:github_id, :integer, null: false)
|
add(:discord_id, :string, null: false)
|
||||||
add(:username, :citext, null: false)
|
add(:username, :citext, null: false)
|
||||||
add(:hashed_password, :string, null: true)
|
add(:hashed_password, :string, null: true)
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
create(unique_index(:players, [:username]))
|
create(unique_index(:players, [:username]))
|
||||||
create(unique_index(:players, [:github_id]))
|
create(unique_index(:players, [:discord_id]))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -2,7 +2,7 @@ defmodule Chessh.Auth.PasswordAuthenticatorTest do
|
|||||||
use ExUnit.Case
|
use ExUnit.Case
|
||||||
alias Chessh.{Player, Repo}
|
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
|
setup_all do
|
||||||
Ecto.Adapters.SQL.Sandbox.checkout(Repo)
|
Ecto.Adapters.SQL.Sandbox.checkout(Repo)
|
||||||
|
@ -2,7 +2,7 @@ defmodule Chessh.Auth.PublicKeyAuthenticatorTest do
|
|||||||
use ExUnit.Case
|
use ExUnit.Case
|
||||||
alias Chessh.{Key, Repo, Player}
|
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 %{
|
@valid_key %{
|
||||||
name: "The Gamer Machine",
|
name: "The Gamer Machine",
|
||||||
key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ/2LOJGGEd/dhFgRxJ5MMv0jJw4s4pA8qmMbZyulN44"
|
key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ/2LOJGGEd/dhFgRxJ5MMv0jJw4s4pA8qmMbZyulN44"
|
||||||
|
@ -3,10 +3,10 @@ defmodule Chessh.Auth.UserRegistrationTest do
|
|||||||
use ExUnit.Case
|
use ExUnit.Case
|
||||||
alias Chessh.{Player, Repo}
|
alias Chessh.{Player, Repo}
|
||||||
|
|
||||||
@valid_user %{username: "logan", password: "password", github_id: 4}
|
@valid_user %{username: "logan", password: "password", discord_id: "4"}
|
||||||
@invalid_username %{username: "a", password: "password", github_id: 7}
|
@invalid_username %{username: "a", password: "password", discord_id: "7"}
|
||||||
@invalid_password %{username: "aasdf", password: "pass", github_id: 6}
|
@invalid_password %{username: "aasdf", password: "pass", discord_id: "6"}
|
||||||
@repeated_username %{username: "LoGan", password: "password", github_id: 5}
|
@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
|
test "Password must be at least 8 characters and username must be at least 2" do
|
||||||
refute Player.registration_changeset(%Player{}, @invalid_password).valid?
|
refute Player.registration_changeset(%Player{}, @invalid_password).valid?
|
||||||
|
@ -5,7 +5,7 @@ defmodule Chessh.SSH.AuthTest do
|
|||||||
@localhost '127.0.0.1'
|
@localhost '127.0.0.1'
|
||||||
@localhost_inet {{127, 0, 0, 1}, 1}
|
@localhost_inet {{127, 0, 0, 1}, 1}
|
||||||
@key_name "The Gamer Machine"
|
@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_test_keys_dir Path.join(Application.compile_env!(:chessh, :key_dir), "client_keys")
|
||||||
@client_pub_key 'id_ed25519.pub'
|
@client_pub_key 'id_ed25519.pub'
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user