diff --git a/.idea/price-point-api.iml b/.idea/price-point-api.iml
index 7546ea2..a12de13 100644
--- a/.idea/price-point-api.iml
+++ b/.idea/price-point-api.iml
@@ -42,6 +42,7 @@
+
diff --git a/.opencode/memory/2026-05-30.logfmt b/.opencode/memory/2026-05-30.logfmt
index d298515..541ae2a 100644
--- a/.opencode/memory/2026-05-30.logfmt
+++ b/.opencode/memory/2026-05-30.logfmt
@@ -5,3 +5,4 @@ ts=2026-05-30T17:25:34.868Z type=preference scope=developer-profile content="Tea
ts=2026-05-30T20:44:39.127Z type=decision scope=price-point-api content="price-point-api: Added Votable concern (app/models/concerns/votable.rb). PriceReport includes Votable. Methods: upvote!(user), downvote!(user) with toggle logic (same direction = destroy vote, opposite direction = switch), upvote_count/downvote_count via query (votes.where query). Rejected counter cache — directional counts (upvotes/downvotes) make Rails' built-in counter_cache unsuitable. Stale upvotes_count/downvotes_count columns need migration to drop." tags=voting,concerns,architecture
ts=2026-05-30T20:44:41.808Z type=learning scope=developer-profile content="Learned: ActiveSupport::Concern syntax (extend + included do), find_by(user:) shorthand, ! vs no-! consistency in method calls, counter_cache limitations with polymorphic + directional counts, parentheses optional in Ruby when no args. Built Votable concern from scratch with toggle logic." tags=ruby,rails,concerns,counter-cache
ts=2026-05-30T20:45:19.960Z type=preference scope=developer-profile content="Teaching style correction: Don't recommend patterns without first fully analyzing fit. Counter cache example — recommended before realizing directional counts break it. Verify the pattern actually solves the user's concrete problem before suggesting it. If unsure, say so and let the user decide." tags=teaching,style,lessons
+ts=2026-05-30T21:26:45.561Z type=preference scope=developer-profile content="Auth style preferences: prefers `unless` over guard-clause-with-return for before_action authentication. Doesn't care about strict \"Bearer\" prefix validation in token extraction — fine with split.last."
diff --git a/Gemfile b/Gemfile
index 0b95b29..4b161c9 100644
--- a/Gemfile
+++ b/Gemfile
@@ -10,7 +10,7 @@ gem "puma", ">= 5.0"
# gem "jbuilder"
# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
-# gem "bcrypt", "~> 3.1.7"
+gem "bcrypt", "~> 3.1.7"
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem "tzinfo-data", platforms: %i[ windows jruby ]
diff --git a/Gemfile.lock b/Gemfile.lock
index 7a346cd..7c74c6d 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -77,6 +77,7 @@ GEM
activesupport (>= 6.0.0)
ast (2.4.3)
base64 (0.3.0)
+ bcrypt (3.1.21)
bcrypt_pbkdf (1.1.2)
benchmark (0.5.0)
bigdecimal (4.1.2)
@@ -339,6 +340,7 @@ PLATFORMS
DEPENDENCIES
annotaterb
+ bcrypt (~> 3.1.7)
bootsnap
brakeman
debug
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 4ac8823..c1d2a38 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,2 +1,15 @@
class ApplicationController < ActionController::API
+ before_action :authenticate!
+
+ private
+ def authenticate!
+ token = request.headers["Authorization"]&.split(" ")&.last
+ @current_user = User.find_by(token:)
+
+ render json: { error: "Unauthorized" }, status: :unauthorized unless @current_user
+ end
+
+ def current_user
+ @current_user
+ end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 12a5d4f..39e0d66 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -15,6 +15,7 @@
# index_users_on_email (email) UNIQUE
#
class User < ApplicationRecord
+ has_secure_password
has_many :price_reports, dependent: :nullify
has_many :moderation_logs, foreign_key: :admin_id, dependent: :nullify
end
diff --git a/db/migrate/20260530211142_add_token_to_users.rb b/db/migrate/20260530211142_add_token_to_users.rb
new file mode 100644
index 0000000..ab56502
--- /dev/null
+++ b/db/migrate/20260530211142_add_token_to_users.rb
@@ -0,0 +1,6 @@
+class AddTokenToUsers < ActiveRecord::Migration[8.0]
+ def change
+ add_column :users, :token, :string
+ add_index :users, :token, unique: true
+ end
+end