Skip to content

Commit 8abf82e

Browse files
flavorjonesclaude
andcommitted
Add bearer token authentication for audit console
- Rename SaasAdminController to Admin::AuditController - Override require_authentication to support bearer tokens alongside session auth, allowing API access to audit console - Add migration for audits1984_auditor_tokens table - Add controller tests for authentication scenarios including staff validation on token-based access Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 7ba36a8 commit 8abf82e

File tree

6 files changed

+77
-8
lines changed

6 files changed

+77
-8
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class Admin::AuditsController < ::AdminController
2+
private
3+
# Extend Fizzy's authentication to support auditor bearer tokens.
4+
def require_authentication
5+
authenticate_by_audit_bearer_token || super
6+
end
7+
8+
def authenticate_by_audit_bearer_token
9+
if auditor = auditor_from_bearer_token
10+
Current.identity = auditor
11+
end
12+
end
13+
14+
def find_current_auditor
15+
Current.identity
16+
end
17+
end

saas/app/controllers/saas_admin_controller.rb

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# This migration comes from audits1984 (originally 20260126000000)
2+
class CreateAuditorTokens < ActiveRecord::Migration[7.0]
3+
def change
4+
create_table :audits1984_auditor_tokens do |t|
5+
t.uuid :auditor_id, null: false, index: { unique: true }
6+
t.string :token_digest, null: false
7+
t.datetime :expires_at, null: false
8+
9+
t.timestamps
10+
11+
t.index :token_digest, unique: true
12+
end
13+
end
14+
end

saas/db/saas_schema.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema[8.2].define(version: 2025_12_16_000000) do
13+
ActiveRecord::Schema[8.2].define(version: 2026_01_26_230838) do
1414
create_table "account_billing_waivers", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
1515
t.uuid "account_id", null: false
1616
t.datetime "created_at", null: false
@@ -43,6 +43,16 @@
4343
t.index ["stripe_subscription_id"], name: "index_account_subscriptions_on_stripe_subscription_id", unique: true
4444
end
4545

46+
create_table "audits1984_auditor_tokens", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
47+
t.uuid "auditor_id", null: false
48+
t.datetime "created_at", null: false
49+
t.datetime "expires_at", null: false
50+
t.string "token_digest", null: false
51+
t.datetime "updated_at", null: false
52+
t.index ["auditor_id"], name: "index_audits1984_auditor_tokens_on_auditor_id", unique: true
53+
t.index ["token_digest"], name: "index_audits1984_auditor_tokens_on_token_digest", unique: true
54+
end
55+
4656
create_table "audits1984_audits", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
4757
t.uuid "auditor_id", null: false
4858
t.datetime "created_at", null: false

saas/lib/fizzy/saas/engine.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ class Engine < ::Rails::Engine
120120
config.console1984.ask_for_username_if_empty = true
121121
config.console1984.base_record_class = "::SaasRecord"
122122

123-
config.audits1984.base_controller_class = "::SaasAdminController"
123+
config.audits1984.base_controller_class = "::Admin::AuditsController"
124124
config.audits1984.auditor_class = "::Identity"
125125
config.audits1984.auditor_name_attribute = :email_address
126126

saas/test/controllers/admin/audits_controller_test.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,38 @@ class Admin::AuditsControllerTest < ActionDispatch::IntegrationTest
3838

3939
assert_response :unauthorized
4040
end
41+
42+
test "valid bearer token is allowed" do
43+
token = Audits1984::AuditorToken.generate_for(identities(:david))
44+
45+
untenanted do
46+
get saas.admin_audits1984_path, headers: { "Authorization" => "Bearer #{token}" }
47+
end
48+
49+
assert_response :success
50+
end
51+
52+
test "expired bearer token is forbidden" do
53+
token = Audits1984::AuditorToken.generate_for(identities(:david))
54+
Audits1984::AuditorToken.update_all(expires_at: 1.day.ago)
55+
56+
untenanted do
57+
get saas.admin_audits1984_path, headers: { "Authorization" => "Bearer #{token}" }
58+
end
59+
60+
assert_response :unauthorized
61+
end
62+
63+
test "bearer token for non-staff user is forbidden" do
64+
# Even with a valid token, non-staff users should be denied access.
65+
# This handles the case where a user's staff privileges are revoked
66+
# after a token was issued.
67+
token = Audits1984::AuditorToken.generate_for(identities(:jz))
68+
69+
untenanted do
70+
get saas.admin_audits1984_path, headers: { "Authorization" => "Bearer #{token}" }
71+
end
72+
73+
assert_response :forbidden
74+
end
4175
end

0 commit comments

Comments
 (0)