Skip to content

Member invitations via email link - #13

Merged
almokhtarbr merged 1 commit into
mainfrom
user-invitations
Sep 7, 2026
Merged

almokhtarbr merged 1 commit into
mainfrom
user-invitations

Conversation

@almokhtarbr

Copy link
Copy Markdown
Owner

Today an admin has to type a password for every teammate and pass it along out of band. Now they can leave the password field blank and Tally emails an invitation instead.

Flow

  • UsersController#create — blank password → stash an unusable SecureRandom.base58(24), save, InvitationMailer.invite(user, current_user).deliver_later. A typed password keeps the old behaviour and marks the user active immediately.
  • InvitationsController — GET/PATCH /invitations/:token. User.find_by_token_for!(:invitation, token); the accept sets name + password, stamps invitation_accepted_at, and starts a session.
  • User — generates_token_for :invitation, expires_in: 7.days keyed on [password_salt, invitation_accepted_at], so the link is single-use and dies on acceptance or a later password change. pending_invitation scope + invitation_pending?.
  • Team list shows an Invite pending badge and a Resend invite button (POST /users/:id/resend_invitation, admin-only).
  • Migration adds users.invitation_accepted_at and backfills existing rows to created_at; SetupController stamps the first admin.

Tests — request specs for the invite/accept/expiry/replay paths, UsersController invite-vs-direct + resend + non-admin guard, and an InvitationMailer spec. Suite 289 green, rubocop clean, brakeman 0. db/structure.sql regenerated.

Admins can now invite a teammate without typing a password for them.
Leaving the password blank on the new-user form stashes an unusable
random password and emails a 7-day link (generates_token_for :invitation)
to set their own. The link is single-use — accepting stamps
invitation_accepted_at, which both flips the 'Invite pending' badge and
invalidates the token.

- User: :invitation token generator + pending_invitation scope + #invitation_pending?
- InvitationsController: token-guarded edit/update, signs in on accept
- InvitationMailer + text/html views
- UsersController#create branches on a blank password; #resend_invitation
- SetupController stamps the first admin as accepted
- migration backfills existing users to created_at
@almokhtarbr
almokhtarbr merged commit e0d2651 into main Sep 7, 2026
2 checks passed
@almokhtarbr
almokhtarbr deleted the user-invitations branch September 7, 2026 02:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant