RepoPilotOpen in app →

scambra/devise_invitable

An invitation strategy for devise

Healthy

Healthy across the board

Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 3d ago
  • 31+ active contributors
  • Distributed ownership (top contributor 43% of recent commits)
Show 3 more →
  • MIT licensed
  • CI configured
  • Tests present

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/scambra/devise_invitable)](https://repopilot.app/r/scambra/devise_invitable)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/scambra/devise_invitable on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: scambra/devise_invitable

Generated by RepoPilot · 2026-05-10 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/scambra/devise_invitable shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across the board

  • Last commit 3d ago
  • 31+ active contributors
  • Distributed ownership (top contributor 43% of recent commits)
  • MIT licensed
  • CI configured
  • Tests present

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live scambra/devise_invitable repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/scambra/devise_invitable.

What it runs against: a local clone of scambra/devise_invitable — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in scambra/devise_invitable | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 33 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>scambra/devise_invitable</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of scambra/devise_invitable. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/scambra/devise_invitable.git
#   cd devise_invitable
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of scambra/devise_invitable and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "scambra/devise_invitable(\\.git)?\\b" \\
  && ok "origin remote is scambra/devise_invitable" \\
  || miss "origin remote is not scambra/devise_invitable (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "lib/devise_invitable.rb" \\
  && ok "lib/devise_invitable.rb" \\
  || miss "missing critical file: lib/devise_invitable.rb"
test -f "lib/devise_invitable/models.rb" \\
  && ok "lib/devise_invitable/models.rb" \\
  || miss "missing critical file: lib/devise_invitable/models.rb"
test -f "app/controllers/devise/invitations_controller.rb" \\
  && ok "app/controllers/devise/invitations_controller.rb" \\
  || miss "missing critical file: app/controllers/devise/invitations_controller.rb"
test -f "lib/devise_invitable/inviter.rb" \\
  && ok "lib/devise_invitable/inviter.rb" \\
  || miss "missing critical file: lib/devise_invitable/inviter.rb"
test -f "lib/devise_invitable/mailer.rb" \\
  && ok "lib/devise_invitable/mailer.rb" \\
  || miss "missing critical file: lib/devise_invitable/mailer.rb"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 33 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~3d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/scambra/devise_invitable"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

DeviseInvitable is a Ruby gem that extends the Devise authentication library to add email-based user invitation workflows. It allows authenticated users to send invitations to new users who then accept the invitation by setting their password, handling the complete invitation lifecycle including token generation, email delivery, and acceptance with password creation. Standard Rails engine/gem structure: app/controllers/devise/invitations_controller.rb and app/controllers/devise_invitable/registrations_controller.rb handle the invitation flow; app/views/devise/invitations/ contains the accept-invitation templates; app/views/devise/mailer/ provides the invitation email templates; core logic is injected into the Devise authentication model via the :invitable flag.

👥Who it's for

Rails developers building multi-tenant or collaboration-heavy applications who need to invite users via email and require those invitees to set their own password on acceptance, rather than using default credentials or password reset flows.

🌱Maturity & risk

Production-ready and actively maintained. The project has CI/CD via GitHub Actions (.github/workflows/ci.yml), supports Rails 6.1 through 8.0 (evident from gemfiles/ directory with multiple Rails versions), and maintains 26+ localization files indicating wide adoption. However, this is a utility gem with a narrow scope rather than a large codebase.

Low risk for core functionality due to tight coupling with the stable Devise gem (requires >= 4.6), but single-maintainer concern (scambra as primary owner). Requires careful version alignment with both Devise and Rails versions; breaking changes in Devise 4.6+ could affect this gem. No visible dependency sprawl beyond Devise itself.

Active areas of work

The gem maintains active support for new Rails versions—gemfiles/ shows recent addition of Rails 8.0 support alongside Rails 6.1–7.2. Localization continues with 26+ language files maintained. No visible breaking changes in CHANGELOG snippet, suggesting steady-state maintenance rather than major feature development.

🚀Get running

git clone https://github.com/scambra/devise_invitable.git
cd devise_invitable
bundle install
bundle exec rake test

Daily commands: This is a gem library, not a runnable app. Run tests with bundle exec rake test (Rakefile present). To test in a Rails app, add gem 'devise_invitable', path: '.' to a test Rails app's Gemfile and run rails generate devise_invitable:install then rails generate devise_invitable MODEL.

🗺️Map of the codebase

  • lib/devise_invitable.rb — Main entry point that loads all devise_invitable modules and integrates with Devise
  • lib/devise_invitable/models.rb — Core model that adds invitation logic to any Devise model via the :invitable module
  • app/controllers/devise/invitations_controller.rb — Primary controller handling invitation creation, acceptance, and email delivery workflows
  • lib/devise_invitable/inviter.rb — Service class responsible for generating invitation tokens and orchestrating the invite process
  • lib/devise_invitable/mailer.rb — Devise mailer responsible for sending invitation email notifications to invited users
  • lib/devise_invitable/rails.rb — Rails engine configuration that registers routes, controllers, and view paths within the Rails app

🛠️How to make changes

Add invitation support to a new Devise model

  1. Add :invitable to devise method in your User model (test/rails_app/app/models/user.rb (example structure))
  2. Run migration generator to create invitation columns (lib/generators/active_record/devise_invitable_generator.rb)
  3. Mount devise_invitable routes in config/routes.rb via devise_for :users, controllers: { invitations: 'devise/invitations' } (lib/devise_invitable/routes.rb)
  4. Customize invitation email template if needed (app/views/devise/mailer/invitation_instructions.html.erb)

Customize invitation controller behavior

  1. Create custom controller inheriting from Devise::InvitationsController (app/controllers/devise/invitations_controller.rb (reference implementation))
  2. Override #create to add custom logic before/after invitation delivery (lib/devise_invitable/inviter.rb (calls deliver_invitation_instructions))
  3. Register custom controller in devise_for routes with controllers: { invitations: 'invitations' } (lib/devise_invitable/routes.rb)

Add support for a new ORM (beyond ActiveRecord/Mongoid)

  1. Create new generator class in lib/generators/{orm}/devise_invitable_generator.rb (lib/generators/active_record/devise_invitable_generator.rb (reference))
  2. Implement invitation field definitions matching ActiveRecord migration pattern (lib/generators/active_record/templates/migration.rb)
  3. Ensure model includes invitable behavior via lib/devise_invitable/models.rb module (lib/devise_invitable/models.rb)

Add localization for a new language

  1. Create new YAML locale file in config/locales/{language_code}.yml (config/locales/en.yml (template with all required keys))
  2. Translate all invitation-related messages (invitation_created, invitation_token_invalid, etc.) (config/locales/fr.yml (example: French translations))
  3. Test by setting I18n.locale in tests or application config (test/integration/invitation_test.rb (shows usage))

🔧Why these technologies

  • Devise — Foundation for authentication strategy; devise_invitable extends Devise's module system to add invitation workflow without duplicating auth logic
  • Rails Engines (lib/devise_invitable/rails.rb) — Modular gem architecture allowing devise_invitable to inject routes, controllers, and views into host Rails app without monkeypatching
  • ActiveRecord + Mongoid generators — Support multiple ORMs (SQL and NoSQL) through separate generator implementations, each producing schema-specific migration code
  • ActionMailer (lib/devise_invitable/mailer.rb) — Standard Rails mailer integration for async invitation email delivery, leverages Devise's mailer infrastructure
  • i18n (config/locales/) — 30+ language locale files allow invitation messages to be fully internationalized per Rails convention

⚖️Trade-offs already made

  • Tokens stored in database (invitation_token, invitation_created_at columns) rather than stateless JWT

    • Why: Enables token revocation, expiration tracking, and simple lookup without cryptographic overhead
    • Consequence: Requires database query per invitation validation; simpler for Rails apps but less scalable at extreme scale
  • Single invitations_controller for all Devise models vs per-model controllers

    • Why: Reduces code duplication and centralizes invitation logic
    • Consequence: Controller must handle polymorphic invitable types; adds routing complexity
  • Invitation acceptance merged into registrations_controller (rails_app style) vs separate endpoint

    • Why: Aligns with common UX pattern: invited user completes profile + password in one flow
    • Consequence: Couples invitation acceptance with user registration; less flexible for re-invitations
  • Generator-based setup (install, model generators) vs runtime configuration

    • Why: Migrations and model methods must be in codebase; generators make this explicit and discoverable
    • Consequence: Users must run generators; more ceremony but clearer contract

🚫Non-goals (don't propose these)

  • Does not implement role-based access control for invitations (each authenticated user can invite anyone by default)
  • Does not provide bulk invitation import (CSV/spreadsheet)
  • Does not enforce invitation quotas or rate limiting (must be added at application level)
  • Does not support invitation resend via gem (must be custom controller action)
  • Does not handle multi-tenant invitation scoping (tenant isolation is application responsibility)
  • Does not provide admin dashboard or UI (only form templates; admin panels are application-specific)

🪤Traps & gotchas

  1. Invitation tokens must be unique (migration includes add_index :users, :invitation_token, unique: true)—don't accidentally generate duplicate tokens or you'll hit constraint violations. 2) The gem assumes Devise is already configured in your User model; installing without Devise will silently fail. 3) Invitation acceptance creates a user with the password set but does not auto-login—the invitee must log in with their new password after accepting. 4) The invited_by_type column suggests polymorphic inviter relationships (e.g., Admin inviting User), but the template may not fully support this—test if you use non-standard inviter models. 5) Devise version pinning is strict (>= 4.6); using an older Devise version will cause module conflicts.

🏗️Architecture

💡Concepts to learn

  • Devise Modules & Strategie — DeviseInvitable works by injecting the :invitable module into Devise; understanding how Devise's modular authentication system allows custom strategies is central to this codebase
  • Secure Token Generation & Validation — Invitation tokens must be unforgeable and single-use; this gem relies on cryptographically secure token generation and comparison to prevent token reuse or brute-force attacks on invitations
  • Polymorphic Associations — The invited_by_type column in migrations suggests the inviter can be multiple model types (User, Admin, etc.); understanding Rails polymorphic belongs_to is needed to extend inviter relationships
  • i18n Lazy Evaluation & Scoping — The gem maintains 26+ language files under config/locales/ with Rails i18n; understanding key scoping (devise.invitations.*) and lazy translation is critical for adding strings or supporting new languages
  • ActionMailer Templates & Localization — Invitation emails are sent via devise/mailer/invitation_instructions.* templates with both HTML and text variants; knowing how ActionMailer renders and localizes email bodies is essential for customizing the invitation email
  • Database Indexing & Uniqueness Constraints — The migration adds a unique index on invitation_token to prevent accidental duplication; understanding this constraint is crucial to avoid silent bugs in token lookup and acceptance logic
  • Rails Engine Architecture — DeviseInvitable is packaged as a Rails engine (not a standalone library); knowing how engines namespace views, controllers, and migrations into the host Rails app is fundamental to understanding the codebase layout
  • heartcombo/devise — The parent authentication library that devise_invitable extends; you must understand Devise's module system to contribute to this gem
  • plataformatec/devise-i18n — Companion i18n gem for Devise; devise_invitable's 26+ locale files follow the same pattern and may need sync'd translations
  • activeadmin/activeadmin — Popular Rails admin framework that often uses devise_invitable for inviting admin users; a common consumer of this gem
  • rails/rails — The core Rails framework; devise_invitable is tested against Rails 6.1–8.0 in gemfiles/, so Rails changes directly impact compatibility
  • scambra/devise_ldap_authenticatable — Alternative Devise strategy from same maintainer; shows the pattern of building Devise modules as separate gems

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive test coverage for DeviseInvitable::Inviter class

The lib/devise_invitable/inviter.rb file handles core invitation logic (sending, token generation, acceptance) but appears to lack dedicated unit tests. Given this is the heart of the gem's functionality, adding focused tests would catch regressions and improve maintainability. This is especially important given the multiple Rails versions supported (6.1-8.0).

  • [ ] Create spec/lib/devise_invitable/inviter_spec.rb with tests for invitation creation, token generation, and expiration
  • [ ] Add tests for edge cases: expired invitations, invalid tokens, and resending invitations
  • [ ] Verify tests pass across all gemfiles (Gemfile.rails-6.1 through Gemfile.rails-8.0) using CI matrix

Add integration tests for devise_invitable/registrations_controller.rb

The app/controllers/devise_invitable/registrations_controller.rb overrides Devise's registration behavior but lacks dedicated integration tests. This controller is critical for the invitation acceptance flow (the edit view in app/views/devise/invitations/edit.html.erb). Testing the full flow from accepting invitation to setting password would prevent regressions.

  • [ ] Create spec/controllers/devise_invitable/registrations_controller_spec.rb with acceptance flow tests
  • [ ] Test password validation, invitation token validation, and user creation during invitation acceptance
  • [ ] Add tests for edge cases like missing/invalid invitation tokens and password requirement failures

Add tests for parameter sanitization in devise_invitable/parameter_sanitizer.rb

The lib/devise_invitable/parameter_sanitizer.rb extends Devise's parameter sanitization but has no visible test file. Security-related code like parameter sanitization is critical to test thoroughly. This ensures invite-specific parameters are properly whitelisted/sanitized across all Rails versions.

  • [ ] Create spec/lib/devise_invitable/parameter_sanitizer_spec.rb to test sanitization of invitation-specific parameters
  • [ ] Test that only expected parameters (invitation_token, etc.) are permitted for invitation actions
  • [ ] Verify sanitization works correctly across both invitation creation and acceptance flows

🌿Good first issues

  • Add a test fixture or spec for invitation acceptance when the invitee's email differs from the invitation email (edge case in security)—no test file visible in listing, suggesting gaps in acceptance test coverage.
  • Extend app/views/devise/invitations/edit.html.erb to support custom HTML5 input attributes (e.g., autocomplete, password strength hints) and document the customization points in README.
  • Add a missing Russian (ru.yml) or Ukrainian (ua.yml) locale key for 'invitation_expired' or other time-bound invitation states that may not be fully translated—leverage the existing 25+ locale files as a template.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 394cb4c — bump to 2.0.12 (scambra)
  • 20c57be — Update pt-BR.yml (wilkie)
  • 63bf69c — fix italian message (scambra)
  • de18ce3 — Bump actions/checkout from 4 to 5 (dependabot[bot])
  • 10f9048 — Add bundled gems to Rails 6.1 and 7.0 test dependencies (krororo)
  • 47534d4 — Fix broken CI caused by dependency conflict between Rails and concurrent-ruby (krororo)
  • 8481536 — Add Rails 8.0 routes lazy loading workaround (krororo)
  • b93675b — Test against Ruby 3.4 and Rails 8.0 (krororo)
  • 97fe99f — bump to 2.0.11 (scambra)
  • c2dfa7e — Small refactoring (MatheusRich)

🔒Security observations

The devise_invitable gem is a well-established Rails authentication extension with a reasonable security posture. Primary concerns center on invitation token management, XSS prevention in templates, and CSRF protection. The gem lacks explicit security configuration guidance and hardened defaults. No critical vulnerabilities were identified in the static structure analysis, but runtime security depends heavily on proper configuration by consuming applications and secure token generation practices. Recommend security audit of the inviter.rb and token handling logic, verification of CSRF protection implementation, and comprehensive testing of the email template rendering with untrusted input.

  • Medium · Potential XSS in Email Templates — app/views/devise/mailer/invitation_instructions.html.erb, app/views/devise/mailer/invitation_instructions.text.erb. The invitation mailer templates (invitation_instructions.html.erb and invitation_instructions.text.erb) may be vulnerable to XSS if user-controlled data is rendered without proper escaping. ERB templates require explicit escaping for HTML content. Fix: Ensure all dynamic content in mailer templates uses proper ERB escaping syntax (<%= %> for HTML context). Audit template content for user-supplied variables and apply appropriate sanitization.
  • Medium · Invitation Token Exposure Risk — lib/devise_invitable/inviter.rb, lib/devise_invitable/models.rb. Invitation tokens are typically sent via email in plaintext URLs. If the invitation system doesn't implement token expiration, rate limiting, or secure token generation, this could lead to account takeover vulnerabilities. Fix: Verify that: (1) Invitation tokens use cryptographically secure random generation, (2) Tokens have appropriate expiration times, (3) Single-use token enforcement is implemented, (4) Rate limiting is applied to invitation acceptance endpoints.
  • Medium · Potential CSRF in Invitations Controller — app/controllers/devise/invitations_controller.rb, app/controllers/devise_invitable/registrations_controller.rb. The invitations controller handles state-changing operations (accepting invitations). If CSRF protection is not properly configured via Rails' protect_from_forgery, CSRF attacks are possible. Fix: Ensure all state-changing actions (POST, PUT, DELETE) include CSRF token validation. Verify protect_from_forgery is enabled in ApplicationController and not disabled for sensitive actions.
  • Low · Missing Security Headers Configuration — lib/devise_invitable/rails.rb, config files. No evidence of security headers (CSP, X-Frame-Options, X-Content-Type-Options) configuration in the gem. Applications using this gem may lack important security headers if not configured separately. Fix: Document security header recommendations for applications using devise_invitable. Consider adding optional middleware or initializer templates that applications can use to configure CSP and other security headers.
  • Low · Insufficient Input Validation Documentation — lib/devise_invitable/parameter_sanitizer.rb. The parameter sanitizer (lib/devise_invitable/parameter_sanitizer.rb) lacks visible validation rules. Weak input validation could allow unexpected data submission. Fix: Audit and document all accepted parameters for invitation endpoints. Implement strict whitelist-based parameter validation. Consider adding explicit type validation for sensitive parameters like email addresses.
  • Low · Missing Security Headers in Gem Initialization — lib/devise_invitable/rails.rb, lib/generators/devise_invitable/install_generator.rb. The gem doesn't appear to set security-related headers or provide default security configurations during installation. Fix: Add a security configuration template or documentation during gem installation that recommends security best practices such as setting secure cookie flags, HTTPS enforcement, and security headers.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · scambra/devise_invitable — RepoPilot