验证过期链接是否有效

问题描述 投票:0回答:1

我正在构建 Rails API 应用程序,我正在使用设计无密码库。

如果我向用户发送魔法链接,而用户等待太久才来到我的应用程序,我如何验证使用的令牌是否有效并生成/发送新令牌?

我正在寻找类似的东西:

User.find_by(
    email: magic_link_request_params[:email], 
    old_token: magic_link_request_params[:old_token] )
.send_magic_link(true)

可以肯定的是,用户之前已经被授权的管理员/经理邀请过。如果是这样,我希望能够重新发送魔术链接,否则我不想让用户进入我的应用程序,因此仅通过电子邮件查找他是不够的。

  • 红宝石 3.2.2
  • 轨道 7
  • ActionController::API
  • 设计::控制器::Rails7ApiMode
  • 设计
  • 设计无密码
devise ruby-on-rails-7 password-less
1个回答
0
投票

默认情况下,Devise 不会在任何地方保存令牌。

编码和解码令牌而不依赖于用户记录。

它也没有方法 just 生成令牌 without 通过电子邮件发送它,因此您将对这个 gem 进行相当多的修改以获得您的行为。

首先,您需要迁移以将字段添加到您的用户对象。在我的示例中,我使用

last_magic_link
last_magic_link_sent_at

如果您愿意编写自己的模块并

prepend
ing 它们,最干净的解决方案是创建一个 initializer 更改
send_magic_link
以在生成令牌和发送电子邮件之间更新您的
User
记录发送:

# /config/initializers/devise/passwordless/mailer_override.rb
# from: https://github.com/abevoelker/devise-passwordless/blob/master/lib/devise/passwordless/mailer.rb#L6

# we need `require "devise/passwordless"` and `require "devise/mailer"`
# I believe we get them all by requiring all of "devise"
require "devise"

# a custom module to override Devise::Passwordless::Mailer#magic_link
# NOTE: I pasted this untested and untried code from SO, I should check to make sure this works as intended
module MailerAndRecordUpdater
  def magic_link(record, token, remember_me, opts = {})
    @token = token
    @remember_me = remember_me
    devise_mail(record, :magic_link, opts)
    # record is a User record
    record.update(last_magic_link: @token, last_magic_link_sent_at: Time.now)
  end
end

[Devise::Passwordless::Mailer, Devise::Passwordless::Mailer.singleton_class].each do |mod|
  mod.prepend MailerAndRecordUpdater
end

而且,作者通过添加一个名为

after_magic_link_authentication
的方法帮了您一个忙,一旦他们成功登录,您就可以使用该方法从
User
中删除这些记录属性:

class User < ApplicationRecord
  def after_magic_link_authentication
    # intentionally leaving :last_magic_link_sent_at for history
    update(last_magic_link: nil)
  end
end

现在您将在生成和使用令牌时保存和删除令牌。

但是您仍然有您的主要用例:如果令牌和电子邮件已过期,请查看它是否匹配。

不幸的是,gem 引发了一个非常广泛的错误:

InvalidOrExpiredTokenError
并且它没有告诉你实际情况是什么:无效或过期。

您可以在

Devise::Passwordless::LoginToken#decrypt
上写一个类似的覆盖,如果令牌过期则向
raise InvalidOrExpiredTokenError
添加一条消息:

      created_at = ActiveSupport::TimeZone["UTC"].at(decrypted_data["created_at"])
      if as_of.to_f > (created_at + expire_duration).to_f
        raise InvalidOrExpiredTokenError, "expired" #<- custom message
      end

然后,在

Devise::Passwordless::MagicLinksController
(你必须ask Devise生成)你可以从那个错误中解救出来:

  def show
    begin
      self.resource = warden.authenticate!(auth_options)
    rescue InvalidOrExpiredTokenError -> error
      # check to see if the error's message is "expired"
      # check to see if the token matches the user record
      # then do what you want if both of the above are true
    end
    set_flash_message!(:notice, :signed_in)
    sign_in(resource_name, resource)
    yield resource if block_given?
    redirect_to after_sign_in_path_for(resource)
  end
© www.soinside.com 2019 - 2024. All rights reserved.