Let an existing account sign in using a link delivered by your Rails app.
Enable email-link sign-in
After the quickstart prerequisites, run:
bin/rails generate latchkey:email_link
bin/rails db:migrate
bin/rails latchkey:doctorThis also adopts hardened sessions. Configure base_url, mail_from, mail delivery, a queue and a rate-limit cache. The deployment checklist separates a local trial from a durable setup.
What the user does
- Open
/sign-inand request a link for an existing account. - Check their email and open the link.
- Submit the confirmation form to finish signing in.
Opening the link alone does not consume it. Confirmation requires a POST, so email scanners and link previews do not sign the user in merely by fetching the URL. Links expire after 20 minutes by default and may be used once.
The request page uses a generic response for unknown, ineligible and throttled accounts. A success-looking request screen is not proof that a message was sent.
Scroll sideways on a small screen to read the full diagram.
View diagram source
sequenceDiagram
accTitle: From email request to signed-in session
accDescr: The user requests a link, opens the email, then confirms in the app. Opening the link shows a form. Submitting that form checks and consumes the link before creating a session.
participant User
participant App as Rails app
participant Mail as Email inbox
User->>App: Request a sign-in link
App-->>User: Show a generic response
App->>Mail: Send a link for an eligible account
User->>App: Open the email link (GET)
App-->>User: Show the confirmation form
User->>App: Confirm sign-in (POST)
App->>App: Check and consume the link once
App-->>User: Start the signed-in session
This diagram shows a successful confirmation. An expired, replaced or already-used link is rejected instead of creating a session.
Resend and expiry
Request a new link when one has expired or already been used. Use the newest message. Issuing a replacement invalidates the prior link, and resend requests are rate-limited.
Password and email-address changes made through the host’s Rails lifecycle invalidate outstanding links, including while email sign-in is temporarily disabled. Direct database updates bypass those lifecycle callbacks.
Choose browser binding
Cross-device sign-in is allowed by default. To require the browser that requested the link, set:
config.email_link.same_browser = trueA link opened elsewhere will be rejected. Test this with your actual mail client: in-app browsers and a device’s default browser can have different cookies.
Verify and troubleshoot
Follow a delivered link and confirm the new session under /sessions. Try the same link again; it should be rejected. With browser binding enabled, test a second browser too.
For no email, an old link or the wrong-browser state, use troubleshooting. Keep raw links and local mail files out of logs, screenshots and public support requests.