Add headless helpers for custom dashboard integrations#9
Conversation
Introduce three helper modules that make building custom API key UIs
effortless without dictating styling:
- TokenSession: Manages "show token once" pattern for secret keys
- store(session, api_key) - save token after creation
- retrieve_once(session) - get and clear token
- available?(session) - check without clearing
- ExpirationOptions: Handles expiration preset dropdowns
- for_select - returns options for Rails select helper
- parse("30_days") - converts preset to datetime
- default_value - returns "no_expiration"
- ViewHelpers: Data formatting for views (include in ApplicationHelper)
- api_key_status/status_label/status_info
- api_key_environment_label/environment_from_token
- api_key_type_label/publishable?/secret?
These helpers are designed to be framework-agnostic - they return data,
not HTML, so integrators can use Tailwind, Bootstrap, or any styling.
Co-Authored-By: Claude <noreply@anthropic.com>
Introduce FormBuilderExtensions module that reduces view boilerplate
while letting integrators control all styling. Opt-in via initializer:
Rails.application.config.to_prepare do
ActionView::Helpers::FormBuilder.include(ApiKeys::FormBuilderExtensions)
end
Form helpers included:
- api_key_expiration_select(options, html_options)
One-line expiration dropdown with all presets
<%= form.api_key_expiration_select(class: "my-select") %>
- api_key_scopes_checkboxes(scopes, checked:, &block)
Block-based scope rendering - you provide markup, we handle logic
<%= form.api_key_scopes_checkboxes(@scopes) do |scope, checked| %>
<label><%= check_box_tag ..., checked %><%= scope %></label>
<% end %>
- api_key_token_data
Returns structured hash for building custom token display UIs
{ masked:, full:, viewable:, type:, environment: }
Design principle: helpers yield data and handle logic, integrators
provide all HTML/CSS. Works with any design system.
Co-Authored-By: Claude <noreply@anthropic.com>
Model scopes on ApiKey: - .publishable - keys with key_type: "publishable" - .secret - keys that are NOT publishable (includes legacy) Owner instance methods (HasApiKeys): - available_api_key_scopes - get scopes for forms - can_create_api_key?(key_type:) - check limits before UI - create_api_key! now accepts expires_at_preset: param Auto-cleaning: - scopes= setter auto-removes blank values (form checkbox fix) - create_api_key! also cleans scopes array Wire up in lib/api_keys.rb: - Require all helper modules - Create top-level aliases for cleaner API: - ApiKeys::TokenSession (vs ApiKeys::Helpers::TokenSession) - ApiKeys::ExpirationOptions - ApiKeys::ViewHelpers Co-Authored-By: Claude <noreply@anthropic.com>
Extract reusable partials from key row for cleaner code: - _token_display.html.erb - masked/viewable token with show button - _key_badges.html.erb - type, environment, expiration badges - _key_status.html.erb - active/expired/revoked status badge - _key_actions.html.erb - edit/revoke action buttons - _empty_state.html.erb - empty state messaging Update existing partials: - _key_row.html.erb - now uses extracted partials - _keys_table.html.erb - simplified table structure - _publishable_keys.html.erb - improved section layout - _secret_keys.html.erb - improved section layout Add minimal JS for show/copy token functionality: - Event delegation for .btn-show-token and .btn-copy-token - Vanilla JS, no framework dependencies - Progressive enhancement (works without JS) Co-Authored-By: Claude <noreply@anthropic.com>
Document all helpers and best practices learned from production integration into a real SaaS application. New sections: - Building Custom Integrations (overview + what you'll need) - Quick Setup (4-step guide with code) - Complete Controller Example (90-line production-ready code) - Model Scopes (.publishable, .secret, .active, etc.) - Owner Instance Methods (available_api_key_scopes, can_create_api_key?, etc.) - API Key Instance Methods (full method reference) - Token Session Helper (store, retrieve_once, available?) - Expiration Options Helper (for_select, parse, default_value) - Form Builder Extensions (with ERB examples) - View Helpers (status, type, environment helpers) - View Examples (index, form, success page code) - Best Practices (7 production-learned patterns) The guide provides copy-paste-ready code for building custom UIs while using the gem's model layer and business logic. Co-Authored-By: Claude <noreply@anthropic.com>
Pull Request Review: Headless Helpers for Custom Dashboard IntegrationsOverviewThis PR introduces a comprehensive set of helpers and utilities for building custom API key management UIs. The implementation is well-structured and follows Rails conventions effectively. ✅ Strengths1. Excellent Code Organization
2. Strong Documentation
3. Framework-Agnostic Design
4. Production-Ready Patterns
🔍 Code Quality IssuesCritical: Input Validation in within_key_type_limit While .where() calls use parameterized queries (safe), key_type and environment values come from user input without explicit validation at this point. The validation happens earlier in validate_key_type! and validate_environment!, but only when creating keys through create_api_key!. Risk: If someone creates a key directly via ApiKey.create! or bypasses the helper, unvalidated input could reach this validation. Recommendation: Add validation safeguards in the validation method itself to ensure key_type is in the list of known types. High: Missing Input Validation in View Helpers The api_key_environment_from_token method accepts user-provided tokens without length validation. While read-only, excessively long tokens could cause performance issues. Recommendation: Add basic input validation with a reasonable max length (e.g., 500 characters). Medium: Race Condition in can_create_api_key? This check happens without locking, so concurrent requests could bypass the limit. The actual within_key_type_limit validation uses locking, but the UI-facing can_create_api_key? doesn't. Impact: Low - worst case is showing a Create button that will fail validation. Not a security issue, just UX. Recommendation: Document this behavior with a comment explaining it's a best-effort check for UI purposes only. 🎯 Best Practices & Suggestions1. Expiration Options Edge Case 2. Form Builder Extension Safety 🧪 Test CoverageThe PR mentions all existing tests pass (145 runs, 0 failures) - great! However, I recommend adding tests for:
🔒 Security ConsiderationsOverall Security Posture: Good ✅
Recommendations:
📊 Performance ConsiderationsPositive:
Concerns:
📝 DocumentationExcellent additions:
Minor suggestions:
✨ ConclusionThis is a high-quality PR that significantly improves the gem's flexibility while maintaining security and following Rails conventions. The helper design is excellent and clearly informed by real production usage. Summary:
Recommended Actions Before Merge:
Overall Rating: 8.5/10 - Solid implementation with room for minor security improvements. Great work on this feature! 🚀 |
Cover TokenSession, ExpirationOptions, and ViewHelpers to bring branch coverage above the 60% CI threshold. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Address automated review feedback: - Add max length check (500 chars) in api_key_environment_from_token - Add max days limit (3650 / ~10 years) in ExpirationOptions.parse - Document can_create_api_key? as best-effort check for UI purposes - Add thorough boundary condition tests for all edge cases Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Pull Request Review: Add headless helpers for custom dashboard integrationsOverviewThis is a well-designed PR that adds comprehensive helper modules for custom API key management UIs. The code is clean, well-tested, and follows Rails conventions. The README additions are extensive and provide excellent documentation. Test Coverage: All 145 tests pass ✅ 🟢 Strengths
🟡 Issues FoundHigh Priority1. Race Condition in Key Limit Validation
|
Summary
Adds helper modules and model conveniences for building custom API key management UIs outside the built-in engine dashboard. This enables apps like LicenseSeat to create tailored experiences while leveraging the gem's core functionality.
.publishable,.secret,.active,.inactivecan_create_api_key?,available_api_key_scopesWhat's included
lib/api_keys/helpers/)lib/api_keys/form_builder_extensions.rb)Setup for custom integrations
Test plan
🤖 Generated with Claude Code