diff --git a/src/invidious/database/sessions.cr b/src/invidious/database/sessions.cr index 96587082..51193803 100644 --- a/src/invidious/database/sessions.cr +++ b/src/invidious/database/sessions.cr @@ -62,6 +62,15 @@ module Invidious::Database::SessionIDs PG_DB.query_one?(request, sid, as: String) end + def select_token(sid : String) : String? + request = <<-SQL + SELECT id FROM session_ids + WHERE id = $1 + SQL + + PG_DB.query_one?(request, sid, as: String) + end + def select_all(email : String) : Array({session: String, issued: Time}) request = <<-SQL SELECT id, issued FROM session_ids diff --git a/src/invidious/routes/api/v1/authentication.cr b/src/invidious/routes/api/v1/authentication.cr new file mode 100644 index 00000000..aad6d365 --- /dev/null +++ b/src/invidious/routes/api/v1/authentication.cr @@ -0,0 +1,51 @@ +module Invidious::Routes::API::V1::Authentication + def self.login(env) + env.response.content_type = "application/json" + # locale = env.get("preferences").as(Preferences).locale + if !CONFIG.login_enabled + return error_json(400, "Login has been disabled by administrator") + else + creds = Credentials.from_json(env.request.body || "{}") + user = Invidious::Database::Users.select(email: creds.username) + old_sid = creds.token + if user + if Crypto::Bcrypt::Password.new(creds.password).verify(password.byte_slice(0, 55)) + sid = Base64.urlsafe_encode(Random::Secure.random_bytes(32)) + Invidious::Database::SessionIDs.insert(sid, email) + if old_sid != "" + Invidious::Database::SessionIDs.delete(old_sid) + end + token = Invidious::Database::SessionIDs.select_token(sid) + response = JSON.build do |json| + json.object do + json.field "session", token[:session] + json.field "issued", token[:issued].to_unix + end + end + return response + else + return error_json(401, "Wrong username or password") + end + else + return error_json(400, "Not registered") + end + end + end + + def self.signout(env) + env.response.content_type = "application/json" + user = env.get("user").as(User) + sid = env.request.cookies["SID"].value + Invidious::Database::SessionIDs.delete(sid: sid) + env.response.status_code = 200 + end +end + +struct Credentials + include JSON::Serializable + include YAML::Serializable + + property username : String + property password : String + property token : String +end diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr index daaf4d88..127ee4de 100644 --- a/src/invidious/routing.cr +++ b/src/invidious/routing.cr @@ -244,6 +244,10 @@ module Invidious::Routing get "/api/v1/search/suggestions", {{namespace}}::Search, :search_suggestions get "/api/v1/hashtag/:hashtag", {{namespace}}::Search, :hashtag + # Authentication + post "/api/v1/auth/login", {{namespace}}::Authentication, :login + post "/api/v1/auth/signout", {{namespace}}::Authentication, :signout + # Authenticated # The notification APIs cannot be extracted yet! They require the *local* notifications constant defined in invidious.cr