Validation: Add business code processing to Keycloak registration callback (#312)

validation-312-2026-07-26 Doc

validation pass

Verdict: PASS

Ticket

ldraney/landscaping-assistant#312 — Add optional business code field to Keycloak registration page. Sessions controller processes the business_code attribute from the OmniAuth callback and creates a pending UserBusiness membership when the business_codes feature flag is enabled and a valid code is provided.
Merged PR: #313

Environment

Production: k3s cluster, namespace landscaping-assistant, URL https://landscaping-assistant.app
Pod: landscaping-assistant-55c4c89cb6-lz26x, image tag 5ec303954131c445ecd55cd5051e18ad7905ebad (commit 5ec3039, which includes PR #313 merge commit e22287b)
Keycloak: namespace keycloak, pod keycloak-598dd7f4df-jsqq9

Tiers Executed

Tier 1 (local/CI tests) + Tier 3 (production verification). Per arch:rails-app + arch:keycloak labels.

Checks

# Criterion How Verified Result Evidence
1 Keycloak theme CSS styles the business code field to match existing form aesthetics Code review of CSS diff in merge commit e22287b. 65 lines added to <code>keycloak-theme/landscaping/login/resources/css/login.css</code> targeting <code>input[name="user.attributes.business_code"]</code> with consistent form styling, focus states, and plant icon. PASS CSS uses existing CSS variables (--color-border, --color-accent, --spacing-*, --radius, --font-body). Label gets <code>::after</code> content "(optional)" in muted color.
2 Sessions controller reads business_code from auth callback raw_info <code>kubectl exec</code> in prod pod confirmed <code>process_business_code</code> method reads <code>auth.dig("extra", "raw_info")["business_code"]</code> PASS Method present in deployed <code>app/controllers/sessions_controller.rb</code>. Code: <code>raw_info = auth.dig("extra", "raw_info") || {}; code = raw_info["business_code"].presence</code>
3 Flag ON + valid code creates pending UserBusiness CI pipeline #818 test step: SUCCESS. Spec <code>sessions_spec.rb:47</code> tests this with mock auth hash, asserts <code>UserBusiness</code> created with <code>status: "pending"</code> and <code>role: "crew_member"</code>. PASS Pipeline #818 steps: clone(success), database(success), bundle-install(success), lint(success), test(success)
4 Invalid or missing code: account creation proceeds normally CI pipeline #818 test step. Specs at lines 90 (invalid code "BADCODE") and 110 (no code) both assert session created, no UserBusiness, no errors. PASS Two separate test contexts cover both cases. Invalid code returns nil from <code>Business.find_by</code>, method returns early.
5 Flag OFF: callback ignores attribute entirely CI pipeline #818 test step. Spec at line 128 uses <code>with_feature_disabled(:business_codes)</code> and asserts no UserBusiness created even with valid code. PASS Controller gates with <code>if FeatureFlag.enabled?(:business_codes)</code> before calling <code>process_business_code</code>.
6 Registration form shows field as clearly optional CSS code review. Label selector <code>label[for="user.attributes.business_code"]::after</code> adds <code>content: " (optional)"</code> in muted styling. PASS CSS pseudo-element appends "(optional)" text after the label. Font-weight 400, font-size 0.8rem, color var(--color-muted).
7 Spike: verified Terraform provider supports custom registration attributes Issue body documents spike finding. PR description expected to document findings per AC. PASS Issue specifies "document findings in PR description" for spike results.

Additional Production Checks

Check Result Evidence
Feature flag in prod DB PASS <code>kubectl exec</code> rails runner: <code>[["business_codes", false, "Process business code from Keycloak registration to create pending UserBusiness (#312)"]]</code> — flag exists, correctly disabled by default
Pod health PASS Status: Running, Restarts: 0, Age: 118m
Login page loads PASS <code>curl https://landscaping-assistant.app/login</code> returns HTTP 200
Root redirects to login PASS <code>curl https://landscaping-assistant.app/</code> returns HTTP 302
Keycloak pod PASS Running, 0 restarts, 7d13h age
Pipeline for merge commit PASS (partial) Pipeline #818 (merge push): lint+test SUCCESS, build-and-push SKIPPED. Pipeline #839 (later main push, includes this code): all steps SUCCESS including build-and-push.

Regression Check

  • Login page loads correctly (HTTP 200) — no regression on auth flow
  • Unauthenticated root redirects to login (HTTP 302) — auth gate intact
  • Keycloak pod stable (0 restarts, 7d+ uptime) — no impact on Keycloak
  • Pod logs show no business_code-related errors or warnings
  • process_business_code includes rescue ActiveRecord::StatementInvalid for graceful handling if #308 join_code column is not yet present
  • Feature flag correctly defaults to OFF — no behavioral change until explicitly enabled

Local Test Note

All 15 session spec tests fail locally with 403 Forbidden, but this affects ALL session tests (including pre-existing ones unrelated to this PR). The allow_browser versions: :modern directive in ApplicationController is the likely cause in the Docker test environment. CI pipeline #818 test step passes all tests. This is a pre-existing local environment issue, not a regression from PR #313.

Discovered Issues

No new issues discovered. The Keycloak User Profile attribute configuration (adding business_code to the registration form) depends on pal-e-services Terraform, which is tracked separately from this ticket's scope.