mikker / passwordless

🗝 Authentication for your Rails app without the icky-ness of passwords
MIT License
1.26k stars 85 forks source link

Allow re-sending of magic link emails #137

Closed DaanVanVugt closed 1 year ago

DaanVanVugt commented 1 year ago

When the config setting Passwordless.allow_token_resend = true a visit to /users/sign_in/TOKEN_URL for an expired or already claimed user will return a page with a button which can be used to resend an email with this token. Extra care is taken to record a query parameter destination_path into the current session, so opening the new email immediately from the same browser redirects you to the proper location.

mikker commented 1 year ago

I think this solution complicates things too much. This problem could be solved, slightly worse but way simpler, with just providing a description and link to do the sign in from the start again.

DaanVanVugt commented 1 year ago

The reason that was not enough for us, is that we send transactional emails containing magic links with destinations (an update on a ticket, for instance). Just re-requesting through the login page would drop the location information.

mikker commented 1 year ago

That use case makes sense. Could you not create a new session for each notification?

DaanVanVugt commented 1 year ago

We do, like this (in ApplicationMailer, to save as @magic_link and use in templates)

  def magic_link(user, object)
    session = Passwordless::Session.new({
                                          authenticatable: user,
                                          user_agent: 'ApplicationMailer',
                                          remote_addr: 'unknown'
                                        })
    session.save!
    @magic_link = send(Passwordless.mounted_as).token_sign_in_url(session.token)
    @magic_link = "#{@magic_link}?destination_path=#{polymorphic_url(object)}" if object
  end
mikker commented 1 year ago

Two ideas:

  1. Make the session's expiry super long, 3 months / 1 year / whatever. This makes them unlikely to have expired and problem solved without this.
  2. Build something custom, a new controller, maybe a db model too, that handles all your logic for this. It's fairly easy to still use Passwordless with Passwordless::ControllerHelpers. Maybe outright copy Passwordless' SessionsController to use as a starting point.

I'm not convinced this is common enough (?) to include as an option in Passwordless.

mikker commented 1 year ago

Just my opinion of course, but, your example from above seems to go out of its way to use Passwordless' bundled functionality. I think you could have something simpler by only using Passwordless when necessary, like the call to sign_in and handling the session going forward and all that.

Like, there's no reason to do send(Passwordless.mounted_as).token_sign_in_url in your app. You know what it's mounted as.