summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/invidious.cr175
-rw-r--r--src/invidious/users.cr19
-rw-r--r--src/invidious/views/login.ecr79
3 files changed, 157 insertions, 116 deletions
diff --git a/src/invidious.cr b/src/invidious.cr
index 0a2174ce..ec9b339a 100644
--- a/src/invidious.cr
+++ b/src/invidious.cr
@@ -760,37 +760,22 @@ get "/login" do |env|
referer = get_referer(env, "/feed/subscriptions")
+ email = nil
+ password = nil
+ captcha = nil
+
account_type = env.params.query["type"]?
account_type ||= "invidious"
captcha_type = env.params.query["captcha"]?
captcha_type ||= "image"
- if account_type == "invidious"
- if captcha_type == "image"
- captcha = generate_captcha(HMAC_KEY, PG_DB)
- else
- response = HTTP::Client.get(TEXTCAPTCHA_URL).body
- response = JSON.parse(response)
-
- tokens = response["a"].as_a.map do |answer|
- create_response(answer.as_s, "sign_in", HMAC_KEY, PG_DB)
- end
-
- text_captcha = {
- question: response["q"].as_s,
- tokens: tokens,
- }
- end
- end
-
tfa = env.params.query["tfa"]?
tfa ||= false
templated "login"
end
-# See https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/youtube.py#L79
post "/login" do |env|
locale = LOCALES[env.get("preferences").as(Preferences).locale]?
@@ -805,11 +790,13 @@ post "/login" do |env|
password = env.params.body["password"]?
account_type = env.params.query["type"]?
- account_type ||= "google"
+ account_type ||= "invidious"
- if account_type == "google"
+ case account_type
+ when "google"
tfa_code = env.params.body["tfa"]?.try &.lchop("G-")
+ # See https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/youtube.py#L79
begin
client = make_client(LOGIN_URL)
headers = HTTP::Headers.new
@@ -911,7 +898,11 @@ post "/login" do |env|
end
if !tfa_code
- next env.redirect "/login?tfa=true&type=google&referer=#{URI.escape(referer)}"
+ account_type = "google"
+ captcha_type = "image"
+ tfa = true
+ captcha = nil
+ next templated "login"
end
tl = challenge_results[1][2]
@@ -976,6 +967,7 @@ post "/login" do |env|
cookie.extension = cookie.extension.not_nil!.gsub(".youtube.com", host)
cookie.extension = cookie.extension.not_nil!.gsub("Secure; ", "")
+ env.response.cookies << cookie
end
if env.request.cookies["PREFS"]?
@@ -992,65 +984,7 @@ post "/login" do |env|
error_message = translate(locale, "Login failed. This may be because two-factor authentication is not enabled on your account.")
next templated "error"
end
- elsif account_type == "invidious"
- answer = env.params.body["answer"]?
- text_answer = env.params.body["text_answer"]?
-
- if config.captcha_enabled
- if answer
- answer = answer.lstrip('0')
- answer = OpenSSL::HMAC.hexdigest(:sha256, HMAC_KEY, answer)
-
- challenge = env.params.body["challenge"]?
- token = env.params.body["token"]?
-
- begin
- validate_response(challenge, token, answer, "sign_in", HMAC_KEY, PG_DB, locale)
- rescue ex
- if ex.message == translate(locale, "Invalid user")
- error_message = translate(locale, "Invalid answer")
- else
- error_message = ex.message
- end
-
- next templated "error"
- end
- elsif text_answer
- text_answer = Digest::MD5.hexdigest(text_answer.downcase.strip)
-
- challenges = env.params.body.select { |k, v| k.match(/text_challenge\d+/) }
- tokens = env.params.body.select { |k, v| k.match(/text_token\d+/) }
-
- found_valid_captcha = false
-
- error_message = translate(locale, "Invalid CAPTCHA")
- challenges.each_with_index do |challenge, i|
- begin
- challenge = challenge[1]
- token = tokens[i][1]
- validate_response(challenge, token, text_answer, "sign_in", HMAC_KEY, PG_DB, locale)
- found_valid_captcha = true
- rescue ex
- if ex.message == translate(locale, "Invalid user")
- error_message = translate(locale, "Invalid answer")
- else
- error_message = ex.message
- end
- end
- end
-
- if !found_valid_captcha
- next templated "error"
- end
- else
- error_message = translate(locale, "CAPTCHA is a required field")
- next templated "error"
- end
- end
-
- action = env.params.body["action"]?
- action ||= "signin"
-
+ when "invidious"
if !email
error_message = translate(locale, "User ID is a required field")
next templated "error"
@@ -1061,14 +995,9 @@ post "/login" do |env|
next templated "error"
end
- if action == "signin"
- user = PG_DB.query_one?("SELECT * FROM users WHERE LOWER(email) = LOWER($1)", email, as: User)
-
- if !user
- error_message = translate(locale, "Invalid username or password")
- next templated "error"
- end
+ user = PG_DB.query_one?("SELECT * FROM users WHERE LOWER(email) = LOWER($1)", email, as: User)
+ if user
if !user.password
error_message = translate(locale, "Please sign in using 'Sign in with Google'")
next templated "error"
@@ -1102,12 +1031,78 @@ post "/login" do |env|
cookie.expires = Time.new(1990, 1, 1)
env.response.cookies << cookie
end
- elsif action == "register"
+ else
if !config.registration_enabled
error_message = "Registration has been disabled by administrator."
next templated "error"
end
+ if config.captcha_enabled
+ captcha_type = env.params.body["captcha_type"]?
+ answer = env.params.body["answer"]?
+ change_type = env.params.body["change_type"]?
+
+ if !captcha_type || change_type
+ if change_type
+ captcha_type = change_type
+ end
+ captcha_type ||= "image"
+
+ account_type = "invidious"
+ tfa = false
+
+ if captcha_type == "image"
+ captcha = generate_captcha(HMAC_KEY, PG_DB)
+ else
+ captcha = generate_text_captcha(HMAC_KEY, PG_DB)
+ end
+
+ next templated "login"
+ end
+
+ challenges = env.params.body.select { |k, v| k.match(/^challenge\[\d+\]$/) }
+ tokens = env.params.body.select { |k, v| k.match(/^token\[\d+\]$/) }
+
+ answer ||= ""
+ captcha_type ||= "image"
+
+ case captcha_type
+ when "image"
+ answer = answer.lstrip('0')
+ answer = OpenSSL::HMAC.hexdigest(:sha256, HMAC_KEY, answer)
+
+ challenge = env.params.body["challenge[0]"]?
+ token = env.params.body["token[0]"]?
+
+ begin
+ validate_response(challenge, token, answer, "sign_in", HMAC_KEY, PG_DB, locale)
+ rescue ex
+ error_message = ex.message
+ next templated "error"
+ end
+ when "text"
+ answer = Digest::MD5.hexdigest(answer.downcase.strip)
+
+ found_valid_captcha = false
+
+ error_message = translate(locale, "Invalid CAPTCHA")
+ challenges.each_with_index do |challenge, i|
+ begin
+ challenge = challenge[1]
+ token = tokens[i][1]
+ validate_response(challenge, token, answer, "sign_in", HMAC_KEY, PG_DB, locale)
+ found_valid_captcha = true
+ rescue ex
+ error_message = ex.message
+ end
+ end
+
+ if !found_valid_captcha
+ next templated "error"
+ end
+ end
+ end
+
if password.empty?
error_message = translate(locale, "Password cannot be empty")
next templated "error"
@@ -1166,6 +1161,8 @@ post "/login" do |env|
end
env.redirect referer
+ else
+ env.redirect referer
end
end
diff --git a/src/invidious/users.cr b/src/invidious/users.cr
index 308786c2..1131c77e 100644
--- a/src/invidious/users.cr
+++ b/src/invidious/users.cr
@@ -328,7 +328,22 @@ def generate_captcha(key, db)
answer = "#{hour}:#{minute.to_s.rjust(2, '0')}:#{second.to_s.rjust(2, '0')}"
answer = OpenSSL::HMAC.hexdigest(:sha256, key, answer)
- challenge, token = create_response(answer, "sign_in", key, db)
+ return {
+ question: image,
+ tokens: [create_response(answer, "sign_in", key, db)],
+ }
+end
+
+def generate_text_captcha(key, db)
+ response = HTTP::Client.get(TEXTCAPTCHA_URL).body
+ response = JSON.parse(response)
+
+ tokens = response["a"].as_a.map do |answer|
+ create_response(answer.as_s, "sign_in", key, db)
+ end
- return {image: image, challenge: challenge, token: token}
+ return {
+ question: response["q"].as_s,
+ tokens: tokens,
+ }
end
diff --git a/src/invidious/views/login.ecr b/src/invidious/views/login.ecr
index 20363b2c..82e9396c 100644
--- a/src/invidious/views/login.ecr
+++ b/src/invidious/views/login.ecr
@@ -8,7 +8,7 @@
<div class="h-box">
<div class="pure-g">
<div class="pure-u-1-2">
- <a class="pure-button <% if account_type == "invidious" %>pure-button-disabled<% end %>" href="/login">
+ <a class="pure-button <% if account_type == "invidious" %>pure-button-disabled<% end %>" href="/login?type=invidious">
<%= translate(locale, "Login/Register") %>
</a>
</div>
@@ -22,55 +22,84 @@
<% if account_type == "invidious" %>
<form class="pure-form pure-form-stacked" action="/login?referer=<%= URI.escape(referer) %>&type=invidious" method="post">
<fieldset>
+ <% if email %>
+ <input name="email" type="hidden" value="<%= email %>">
+ <% else %>
<label for="email"><%= translate(locale, "User ID:") %></label>
<input required class="pure-input-1" name="email" type="text" placeholder="User ID">
+ <% end %>
+ <% if password %>
+ <input name="password" type="hidden" value="<%= password %>">
+ <% else %>
<label for="password"><%= translate(locale, "Password:") %></label>
<input required class="pure-input-1" name="password" type="password" placeholder="Password">
+ <% end %>
- <% if config.captcha_enabled %>
- <% if captcha_type == "image" %>
- <img style="width:100%" src='<%= captcha.not_nil![:image] %>'/>
- <input type="hidden" name="token" value="<%= captcha.not_nil![:token] %>">
- <input type="hidden" name="challenge" value="<%= captcha.not_nil![:challenge] %>">
+ <% if captcha %>
+ <% case captcha_type when %>
+ <% when "image" %>
+ <% captcha = captcha.not_nil! %>
+ <img style="width:100%" src='<%= captcha[:question] %>'/>
+ <% captcha[:tokens].each_with_index do |token, i| %>
+ <input type="hidden" name="challenge[<%= i %>]" value="<%= token[0] %>">
+ <input type="hidden" name="token[<%= i %>]" value="<%= token[1] %>">
+ <% end %>
+ <input type="hidden" name="captcha_type" value="image">
<label for="answer"><%= translate(locale, "Time (h:mm:ss):") %></label>
- <input required type="text" name="answer" type="text" placeholder="h:mm:ss">
+ <input type="text" name="answer" type="text" placeholder="h:mm:ss">
+ <% when "text" %>
+ <% captcha = captcha.not_nil! %>
+ <% captcha[:tokens].each_with_index do |token, i| %>
+ <input type="hidden" name="challenge[<%= i %>]" value="<%= token[0] %>">
+ <input type="hidden" name="token[<%= i %>]" value="<%= token[1] %>">
+ <% end %>
+ <input type="hidden" name="captcha_type" value="text">
+ <label for="answer"><%= captcha[:question] %></label>
+ <input type="text" name="answer" type="text" placeholder="Answer">
+ <% end %>
+ <button type="submit" name="action" value="signin" class="pure-button pure-button-primary">
+ <%= translate(locale, "Register") %>
+ </button>
+
+ <% case captcha_type when %>
+ <% when "image" %>
<label>
- <a href="/login?referer=<%= URI.escape(referer) %>&type=invidious&captcha=text">
+ <button type="submit" name="change_type" class="pure-button pure-button-primary" value="text">
<%= translate(locale, "Text CAPTCHA") %>
- </a>
+ </button>
</label>
- <% else %>
- <% text_captcha.not_nil![:tokens].each_with_index do |token, i| %>
- <input type="hidden" name="text_challenge<%= i %>" value="<%= token[0] %>">
- <input type="hidden" name="text_token<%= i %>" value="<%= token[1] %>">
- <% end %>
- <label for="text_answer"><%= text_captcha.not_nil![:question] %></label>
- <input required type="text" name="text_answer" type="text" placeholder="Answer">
-
+ <% when "text" %>
<label>
- <a href="/login?referer=<%= URI.escape(referer) %>&type=invidious">
+ <button type="submit" name="change_type" class="pure-button pure-button-primary" value="image">
<%= translate(locale, "Image CAPTCHA") %>
- </a>
+ </button>
</label>
<% end %>
+ <% else %>
+ <button type="submit" name="action" value="signin" class="pure-button pure-button-primary">
+ <%= translate(locale, "Sign In") %>/<%= translate(locale, "Register") %>
+ </button>
<% end %>
-
- <button type="submit" name="action" value="signin" class="pure-button pure-button-primary"><%= translate(locale, "Sign In") %></button>
- <% if config.registration_enabled %>
- <button type="submit" name="action" value="register" class="pure-button pure-button-primary"><%= translate(locale, "Register") %></button>
- <% end %>
</fieldset>
</form>
<% elsif account_type == "google" %>
- <form class="pure-form pure-form-stacked" action="/login?referer=<%= URI.escape(referer) %>" method="post">
+ <form class="pure-form pure-form-stacked" action="/login?referer=<%= URI.escape(referer) %>&type=google" method="post">
<fieldset>
+ <% if email %>
+ <input name="email" type="hidden" value="<%= email %>">
+ <% else %>
<label for="email"><%= translate(locale, "Email:") %></label>
<input required class="pure-input-1" name="email" type="email" placeholder="Email">
+ <% end %>
+ <% if password %>
+ <input name="password" type="hidden" value="<%= password %>">
+ <% else %>
<label for="password"><%= translate(locale, "Password:") %></label>
<input required class="pure-input-1" name="password" type="password" placeholder="Password">
+ <% end %>
<% if tfa %>
<label for="tfa"><%= translate(locale, "Google verification code:") %></label>