Class: MagicLinksController

Inherits:
ApplicationController
  • Object
show all
Defined in:
app/controllers/magic_links_controller.rb

Overview

Unified create-or-login email magic link (the passwordless email path).

POST /magic_link        — request a link (email [, return_to])
GET  /magic_link/:token — "Confirm sign-in" interstitial (does NOT consume)
POST /magic_link/:token — consume it: log in OR create the account

create-or-login: clicking the link IS proof of email ownership, so an email that collides with a Google/wallet-only account that was never email-verified is safely logged in here and stamped email_verified_at (unlike from_omniauth, which refuses that collision precisely because it lacked this proof).

This is the engine’s GENERIC base. Apps that need richer post-consume routing (e.g. turf-monster’s contest landing + picks rehydration + entry-tokens upsell) OVERRIDE this controller in the app and reuse the MagicLink service + the sign_in_existing / sign_up_new building blocks.

Instance Method Summary collapse

Instance Method Details

#confirmObject

GET /magic_link/:token is deliberately inert. Email link scanners and link preview clients frequently prefetch emailed URLs with GET/HEAD; if GET burned the token, the human’s first real click could already be invalid. The page renders a CSRF-protected form that a browser auto-POSTs to #consume.



39
40
41
42
43
44
45
# File 'app/controllers/magic_links_controller.rb', line 39

def confirm
  # strict-origin strips the token-bearing path from subresource Referer
  # headers while preserving a usable Origin header for Rails' CSRF origin
  # check on the consume POST.
  response.set_header("Referrer-Policy", "strict-origin")
  @token = params[:token]
end

#consumeObject

POST /magic_link/:token is the authoritative consume. This is the only place the single-use token is burned.



49
50
51
52
53
54
55
56
# File 'app/controllers/magic_links_controller.rb', line 49

def consume
  response.set_header("Referrer-Policy", "strict-origin")
  result = MagicLink.consume(params[:token])
  user = User.find_by(email: result.email)
  user ? (user, result) : (result)
rescue MagicLink::InvalidToken
  redirect_to , alert: "That sign-in link is invalid or has expired. Request a fresh one below."
end

#createObject

Respond uniformly for any well-formed email. Under create-or-login every address is “valid” (it logs in or signs up), so there is nothing to enumerate. A malformed email gets the same response with no mail sent.



23
24
25
26
27
28
29
30
31
32
33
# File 'app/controllers/magic_links_controller.rb', line 23

def create
  email = params[:email].to_s.strip.downcase
  if email.match?(URI::MailTo::EMAIL_REGEXP)
    token = MagicLink.generate(email: email, return_to: safe_path(params[:return_to]))
    Studio::Email.deliver(UserMailer, :magic_link, email, token, to: email)
  end
  respond_to do |format|
    format.json { render json: { success: true } }
    format.html { redirect_to , notice: "Check your inbox — we just emailed you a sign-in link." }
  end
end