Open-source project
scambra/devise_invitable avatar
scambra/devise_invitable

devise_invitable: email invitations for Devise models

An invitation strategy for devise

2,674 stars542 forksRubyMIT

At a glance

What is it?
devise_invitable adds an :invitable module to Devise so an authenticated user can invite someone by email and that person sets a password on acceptance. It is a small gem with a wide configuration surface, and the defaults hide two behaviours worth knowing before you ship.
Who is it for?
Adopt devise_invitable when you already run Devise and want invitation emails tied to your existing authentication, confirmation and mailer setup. Do not adopt it if you need invitation links that work without an account record, or if you cannot add columns and a unique index to your users table.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 52 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What devise_invitable adds to a Devise model

Devise handles authentication: sign in, sign out, password reset, confirmation. It has no concept of an account that does not exist yet. devise_invitable fills that gap. The README states it "adds support to Devise for sending invitations by email (it requires to be authenticated) and accept the invitation setting the password." The inviter must already be signed in. The invited person receives a token by email and, on accepting, sets a password and becomes a normal Devise record.

The audience is Rails teams that already run Devise and want a second way to create accounts, alongside public registration or instead of it. The gem is a Devise module, not a standalone service. It reuses Devise's mailer, its routes and its model callbacks, which is why setup is a generator plus a migration rather than a new subsystem. If your app does not use Devise, this gem is not the place to start.

The :invitable flag, the token column and the invited_by association

The mechanism is deliberately thin. You add :invitable to the devise call in your model, and the gem adds invitation state as columns on that same table. The invitation itself is a token string plus timestamps: invitation_created_at, invitation_sent_at, invitation_accepted_at. There is no separate invitations table in the README's migration, so invitation history lives in the user row and is overwritten when a new invitation is sent.

The README shows the association is declared for you: `belongs_to :invited_by, polymorphic: true`. Two columns back it, invited_by_id and invited_by_type, which means an invitation can come from more than one model class (a User, an Admin) without a join table. The unique index on invitation_token is what makes token lookup a single indexed read.

The configuration surface is where the real behaviour sits. invite_for sets the validity period and defaults to 0, which the README describes as "the invitation won't expire." invitation_limit defaults to nil, meaning "users can send as many invites as they want, there is no limit for any user, invitation_limit column is not used." invite_key controls which existing users are matched before an invitation is sent, and its default looks users up by email validated against Devise.email_regexp. resend_invitation is enabled by default, so re-inviting someone in invited status sends another email rather than failing.

Installing devise_invitable and sending a first invitation

Add the gem to your Gemfile. The README pins the current line at `~> 2.0.0` and states the latest version works with Devise >= 4.6; for Devise between 4.0 and 4.6 it points at version 1.7.5.

ruby
gem 'devise_invitable', '~> 2.0.0'

Run the install generator to add the configuration block to config/initializers/devise.rb, then the model generator with your class name. The README uses User as the example.

shell
rails generate devise_invitable:install
rails generate devise_invitable User

The second command adds the :invitable flag to the model's Devise modules and creates a migration file if your ORM supports them. If you prefer to wire it by hand, the README's manual path adds the module directly and then the columns:

ruby
class User < ActiveRecord::Base
  devise :database_authenticatable, :confirmable, :invitable
end
ruby
add_column :users, :invitation_token, :string
add_column :users, :invitation_created_at, :datetime
add_column :users, :invitation_sent_at, :datetime
add_column :users, :invitation_accepted_at, :datetime
add_column :users, :invitation_limit, :integer
add_column :users, :invited_by_id, :integer
add_column :users, :invited_by_type, :string
add_index :users, :invitation_token, unique: true

After migrating, a signed-in user with the invitable module can send an invitation and the recipient gets a Devise mail with the token link. If you want to change the wording, run `rails generate devise_invitable:views` to copy the packaged views into your application. One upgrade note from the README: if an older setup put a `:limit` on invitation_token, remove it with `change_column :users, :invitation_token, :string, limit: nil`.

Mongoid needs fields declared by hand

ActiveRecord users get migrations. Mongoid users do not, and the README is explicit that you define the fields and indexes yourself inside the invitable model: invitation_token as String, the three timestamps as Time, invitation_limit as Integer, plus background indexes on invitation_token and invitation_by_id. The README notes you do not need to declare the belongs_to relationship, since the gem does it, but you do need to create the indexes in MongoDB after deploying, with `rake db:mongoid:create_indexes`.

That last step is easy to forget, and forgetting it means the unique constraint on the token is not enforced in the database. The gem will still work in development. The failure mode appears later, as duplicate tokens or slow lookups as the collection grows.

Two defaults that change your security posture

The README documents two options that are enabled by default and are worth reading twice. allow_insecure_sign_in_after_accept signs the user in automatically after they set a password. require_password_on_accepting requires a password when the invitation is accepted, and the README says to disable it "if you don't want to ask or enforce to set password while accepting, because is set when user is invited or it will be set later." Disabling it means an accepted invitation can leave an account whose password was set earlier or not at all.

The second issue is expiry. Because invite_for defaults to 0, an invitation link does not expire unless you set it. Combined with resend_invitation being on by default, an old invitation email remains usable indefinitely. For a consumer app this may be fine. For anything where an old inbox is a plausible attack path, set invite_for explicitly, either in the initializer or inline as the README shows:

ruby
devise :database_authenticatable, :confirmable, :invitable, invite_for: 2.weeks

A third, quieter limit: because invitation state is stored on the user row, re-inviting someone replaces the previous token. You cannot audit a sequence of invitations from those columns alone, and you cannot hold two live invitations for the same email.

devise_invitable versus building invitations yourself

The alternative is not another gem so much as a hand-rolled flow: a separate invitations table with its own token, a mailer, a controller, and a create-user step on acceptance. That approach costs more code but buys things this gem does not offer. A separate table keeps invitation history, allows multiple pending invitations per email address, and lets you invite an address that has no user row yet in a schema where users are created only on acceptance.

The difference in approach is where the token lives. devise_invitable puts it on the user record and treats the invited person as an existing, unconfirmed user. A hand-rolled flow usually treats the invitation as its own object and creates the user at the end. If your product needs invitation analytics, resend history, or invitations to addresses that should never become users, the separate table fits better. If you just want Devise-shaped accounts created through an email link, the gem removes a mailer, a controller and a migration from your plate.

Maintenance, licence and upgrade cost

The repository is not archived, and the last push was on 2026-08-08. The README carries CI and Code Climate badges, and the repository has a CHANGELOG.md, a Rakefile, a gemspec and a test directory, so changes are tracked and tested rather than dropped in.

The licence is MIT. That is permissive: you can use the gem in commercial and closed-source applications, and you keep the copyright notice. It is not legal advice, and if your organisation has a policy on dependency licences, the LICENSE file in the repository is the thing to read.

Upgrade cost tracks Devise, not this gem. The README ties the current line to Devise >= 4.6 and points older Devise versions at 1.7.5, so a Devise major upgrade is the event that forces you to look at devise_invitable. The one migration the README calls out is removing the legacy `:limit` on invitation_token, which is a schema change you should schedule rather than run blind on a large users table.

Editorial conclusion

Adopt devise_invitable when you already run Devise and want invitation emails tied to your existing authentication, confirmation and mailer setup. Do not adopt it if you need invitation links that work without an account record, or if you cannot add columns and a unique index to your users table. Before shipping, verify three things in your own app: whether invite_for is 0 (never expires) or a finite period, whether allow_insecure_sign_in_after_accept is still enabled, and whether your invitation_limit is nil (unlimited) or a real number. Those three config keys decide the security posture far more than the gem's presence does.

Frequently asked questions

Does devise_invitable work with Devise 4.6 and later?

Yes. The README states the latest version works with Devise >= 4.6, and that for Devise releases from 4.0 up to but not including 4.6 you should use version 1.7.5.

Do invitations sent with devise_invitable expire?

Not by default. The README says invite_for is 0 by default and that in that case the invitation won't expire; you can set it in the Devise initializer or inline as invite_for: 2.weeks.

How do I customize the invitation email views in devise_invitable?

Run the views generator, `rails generate devise_invitable:views`, and it copies all the packaged views into your application so you can edit them. The README also notes the generator can produce scoped views.

How can I use the devise gem in Rails?

This question is about Devise itself rather than devise_invitable, and the README here does not cover Devise's own setup. devise_invitable assumes you already have a Devise model and adds the :invitable module on top of it.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. scambra/devise_invitable on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/scambra-devise-invitable.svg)](https://hysenlabs.com/projects/scambra-devise-invitable)