Skip to content

Commit

Permalink
WIP: Add discord authoriization
Browse files Browse the repository at this point in the history
  • Loading branch information
sevenc-nanashi committed Jul 16, 2023
1 parent 2d6774c commit 1a13dd5
Show file tree
Hide file tree
Showing 21 changed files with 312 additions and 47 deletions.
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,8 @@ GEM
protocol-http (~> 0.23)
rack (>= 1.0)
public_suffix (5.0.3)
puma (6.3.0)
nio4r (~> 2.0)
racc (1.7.1)
rack (2.2.7)
rack-test (2.1.0)
Expand Down Expand Up @@ -374,6 +376,7 @@ DEPENDENCIES
openssl-oaep (~> 0.1.0)
parallel_tests (~> 4.2)
pg (~> 1.4)
puma (~> 6.3)
rails (~> 7.0.4)
redis (~> 4.0)
request_store_rails (~> 2.0)
Expand Down
3 changes: 3 additions & 0 deletions backend/app/controllers/api/charts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ def process_chart_request

def create
params.permit(%i[data chart cover bgm])
require_login!
unless params.to_unsafe_hash.symbolize_keys in {
data: String,
chart: ActionDispatch::Http::UploadedFile,
Expand Down Expand Up @@ -305,6 +306,7 @@ def create

def update
params.permit(%i[data chart cover bgm name])
require_login!
hash = params.to_unsafe_hash.symbolize_keys
if hash[:data].blank? &&
%i[chart cover bgm].all? { |k|
Expand Down Expand Up @@ -398,6 +400,7 @@ def update

def delete
params.require(:name)
require_login!

chart = Chart.find_by(name: params[:name])
unless chart
Expand Down
119 changes: 99 additions & 20 deletions backend/app/controllers/api/discord_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,63 @@

require "cgi"

class Api::DiscordController < ApplicationController
class Api::DiscordController < FrontendController
def redirect_uri
ENV.fetch(
"HOST",
(Rails.env.development? ? "http://" : "https://") + request.host_with_port
)
# ENV.fetch(
# "HOST",
# (Rails.env.development? ? "http://" : "https://") + request.host_with_port
# ) + "/api/discord/callback"
"https://example.com/api/discord/callback"
end
def scope
%w[identify guilds.join guilds]
end
def my_discord
require_login!

unless session.user.discord_id
render json: { discord: nil }
return
end

render json: {
displayName: session.user.discord_display_name,
username: session.user.discord_username,
avatar: session.user.discord_avatar
}
end
def authorize
state = SecureRandom.urlsafe_base64(32)
$redis.with do |conn|
conn.set(
"discord_auth_token/#{state}",
{ session: session.id }.to_json,
{ user_id: session[:user_id] }.to_json,
ex: 5.minutes
)
end
render json: {
url:
"https://discord.com/api/oauth2/authorize" \
"?client_id=#{ENV["DISCORD_CLIENT_ID"]}" \
"&redirect_uri=#{CGI.escape(redirect_uri + "/discord/callback")}" \
"&response_type=code" \
"&scope=#{scope.join("+")}" \
"&state=#{state}"
}
redirect_to "https://discord.com/api/oauth2/authorize" \
"?client_id=#{ENV["DISCORD_CLIENT_ID"]}" \
"&redirect_uri=#{CGI.escape(redirect_uri)}" \
"&response_type=code" \
"&scope=#{scope.join("+")}" \
"&state=#{state}",
allow_other_host: true
end

def callback
params.require %i[code state]
params.permit %i[code state error]

if params[:error]
redirect_to "/charts/upload"
return
end

data =
$redis
.with { |conn| conn.get("discord_auth_token/#{params[:state]}") }
&.then { |json| JSON.parse(json, symbolize_names: true) }

unless data && data[:session] == session.id
unless data && data[:user_id] == session[:user_id]
render json: { error: "Invalid state" }, status: :bad_request
return
end
Expand All @@ -50,12 +68,73 @@ def callback
client_secret: ENV["DISCORD_CLIENT_SECRET"],
grant_type: "authorization_code",
code: params[:code],
redirect_uri: redirect_uri + "/api/discord/callback",
redirect_uri: redirect_uri,
scope: scope.join("+")
}

response = HTTP.post("https://discord.com/api/oauth2/token", form: payload)
response = $discord.post("/oauth2/token", form: payload)

session_user = User.find_by(id: session[:user_id])
session_user.update!(
discord_token: response["access_token"],
discord_refresh_token: response["refresh_token"],
discord_expires_at: Time.now + response["expires_in"].to_i.seconds
)

$redis.with { |conn| conn.del("discord_auth_token/#{params[:state]}") }

discord_user = session_user.discord.get("/users/@me")
session_user.update!(
**if discord_user["discriminator"] == "0"
{
discord_id: discord_user["id"],
discord_username: discord_user["username"],
discord_display_name:
discord_user["global_name"] || discord_user["username"],
discord_avatar:
(
if discord_user["avatar"].nil?
"https://cdn.discordapp.com/embed/avatars/#{
(discord_user["id"].to_i >> 22) % 6
}.png"
else
"https://cdn.discordapp.com/avatars/#{discord_user["id"]}/#{discord_user["avatar"]}.webp"
end
)
}
else
{
discord_id: discord_user["id"],
discord_username:
discord_user["username"] + "#" + discord_user["discriminator"],
discord_display_name: discord_user["username"],
discord_avatar:
(
if discord_user["avatar"].nil?
"https://cdn.discordapp.com/embed/avatars/#{
(discord_user["discriminator"].to_i % 5)
}.png"
else
"https://cdn.discordapp.com/avatars/#{discord_user["id"]}/#{discord_user["avatar"]}.webp"
end
)
}
end
)

begin
$discord.get(
"/guilds/#{ENV["DISCORD_GUILD_ID"]}/members/#{discord_user["id"]}"
)
rescue RuntimeError
$discord.put(
"/guilds/#{ENV["DISCORD_GUILD_ID"]}/members/#{discord_user["id"]}",
json: {
access_token: response["access_token"]
}
)
end

pp response
redirect_to "/charts/upload"
end
end
14 changes: 3 additions & 11 deletions backend/app/controllers/api/my_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ def alt_users

def create_alt_user
params.require(:name)
unless session[:user_id]
return render json: { code: "unauthorized" }, status: 401
end
require_login!

name = params[:name]
if name.length < 4
Expand All @@ -38,10 +36,7 @@ def create_alt_user

def update_alt_user
params.require(:handle)

unless session[:user_id]
return render json: { code: "unauthorized" }, status: 401
end
require_login!

handle = params[:handle][1..].to_i
user = User.find_by(handle:)
Expand All @@ -64,10 +59,7 @@ def update_alt_user

def delete_alt_user
params.require(:handle)

unless session[:user_id]
return render json: { code: "unauthorized" }, status: 401
end
require_login!

handle = params[:handle][1..].to_i
user = User.find_by(handle:)
Expand Down
19 changes: 19 additions & 0 deletions backend/app/controllers/frontend_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,23 @@ def current_user
nil
end
end

around_action do |_controller, action|
success = false
catch :unauthorized do
action.call
success = true
end
unless success
render json: {
code: "not_logged_in",
error: "You are not logged in"
},
status: :unauthorized
end
end

def require_login!
throw :unauthorized unless current_user
end
end
7 changes: 6 additions & 1 deletion backend/app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def display_handle
owner_id ? "x#{handle}" : handle
end

def to_frontend()
def to_frontend
{
handle: owner_id ? "x#{handle}" : handle,
name:,
Expand All @@ -28,4 +28,9 @@ def to_frontend()
chartCount: charts_count
}
end

def discord
return unless discord_token
@discord ||= DiscordRequest.new(bearer_token: discord_token)
end
end
10 changes: 10 additions & 0 deletions backend/config/initializers/discord.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

require_relative "../../lib/discord_request"
$discord = DiscordRequest.new(bot_token: ENV["DISCORD_BOT_TOKEN"])

info = $discord.get("/users/@me")

Rails.logger.info(
"Discord: Logged in as #{info[:username]}##{info[:discriminator]}"
)
2 changes: 1 addition & 1 deletion backend/config/initializers/logger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ def self.logger_outputs_to?(*)
end
end

Rails.logger = Console::Compatible::Logger.new("Rails", Console.logger.output)
Rails.logger = ActiveSupport::TaggedLogging.new(Console::Compatible::Logger.new("Rails", Console.logger.output))
11 changes: 7 additions & 4 deletions backend/config/initializers/sidekiq.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
# frozen_string_literal: true

require "console/compatible/logger"

logger =
ActiveSupport::TaggedLogging.new(
Console::Compatible::Logger.new("Sidekiq", Console.logger.output)
)
Sidekiq.configure_server do |config|
config.redis = { url: ENV["REDIS_URL"] }
config.logger =
Console::Compatible::Logger.new("Sidekiq", Console.logger.output)
config.logger = logger
end

Sidekiq.configure_client do |config|
config.redis = { url: ENV["REDIS_URL"] }
config.logger =
Console::Compatible::Logger.new("Sidekiq", Console.logger.output)
config.logger = logger
end
5 changes: 5 additions & 0 deletions backend/config/puma.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# frozen_string_literal: true

require "console"
require "console/compatible/logger"
# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
Expand Down Expand Up @@ -42,3 +45,5 @@

# Allow puma to be restarted by `bin/rails restart` command.
plugin :tmp_restart

custom_logger Console::Compatible::Logger.new("Puma", Console.logger.output)
3 changes: 2 additions & 1 deletion backend/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@
get "/admin/users/:handle", to: "api/admin#show_user"
post "/admin/reconvert_sus", to: "api/admin#reconvert_sus"

get "/my/discord", to: "api/discord#my_discord"
get "/discord/authorize", to: "api/discord#authorize"
post "/discord/callback", to: "api/discord#callback"
get "/discord/callback", to: "api/discord#callback"
end

scope "/sonolus" do
Expand Down
7 changes: 7 additions & 0 deletions backend/db/migrate/20230716013800_add_discord_name_to_user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class AddDiscordNameToUser < ActiveRecord::Migration[7.0]
def change
add_column :users, :discord_display_name, :string
add_column :users, :discord_username, :string
add_column :users, :discord_avatar, :string
end
end
5 changes: 4 additions & 1 deletion backend/db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 1a13dd5

Please sign in to comment.