ユーザ登録をするに当たって、多くのサービスは以下の流れになるかと思います。
・メールアドレスとパスワードを入力してもらう
・仮登録のメールを送る
・メールアドレスが本人であることを確認して本登録のURLをクリックしてもらう
・有効期限内で、適切なURLであれば登録が完了する
その際に必要となるワンタイムのトークンURLについて紹介したいと思います。下のサイトを参考にしました。
モデル
まずはワンタイムトークンを生成するモデルを作ります。
$ rails g model token
ユーザモデルとトークンモデルのアソシエーションを書きます。トークンには有効期限を設けるので、期限内に本登録を行わなかった場合などの理由で何度か生成する可能性がありますので『has_many』にしています。『has_one』にしてトークンを生成(ユーザ登録時)する度に上書きする方法でもいいかもしれません。
models/user.rb
class User < ActiveRecord::Base has_many :tokens end
models/token.rb
class Token < ActiveRecord::Base belongs_to :user end
db/migrate/20140823231004_create_tokens.rb
class CreateTokens < ActiveRecord::Migration def change create_table :tokens do |t| t.references :user, null: false # ユーザと紐付け t.string :uuid, null: false # トークン t.datetime :expired_at, null: false t.timestamps end add_index :tokens, :user_id end end
ルーティング
本登録するためのトークンURLをルーティングする。これでhttp://ドメイン/users/token/{uuid}にアクセスできる。
resources :users do # ログイントークン get 'token/:uuid', :to => 'users#token' end
コントローラー(トークン生成)
トークンの生成は『new』アクションでメールアドレスとパスワードを入力した後に、『create』アクションで生成してActionMailerなどでユーザのメールアドレスに送付します。トークンの生成には『SecureRandom.uuid』を用いる。作成する際のポイントは以下のとおり。プログラムには冗長なところがあります。
・有効期限を設ける(今回は24時間としている)
・本登録する前にもう一度仮登録した場合は前のトークンを使えなくする
・ユーザ登録しようとしたメールアドレスが登録済みか調べる
controllers/users_controller.rb
def create # 入力したメールアドレスのユーザが存在するか調べる tmp_user = User.find_by_email(user_params[:email]) if tmp_user && tmp_user.created # ユーザ作成済み flash.alert = "入力されたメールアドレスは登録済みです。" redirect_to :action => 'regist' elsif tmp_user # ユーザは既にあるが、本登録していない。一度トークンを全て使えなくする(するならhas_oneで上書く方がいいかもしれないが) @user = tmp_user Token.all.each do |token| # 有効期限を変更する token.update_attributes!(expired_at: Time.now) end # 新しいトークン生成 @token = SecureRandom.uuid # 有効期限は24時間 @user.tokens.create!(uuid: @token, expired_at: 24.hours.since) # メール通知(ActionMailer) @mail = TestMailer.user_create_mail(@user,@token).deliver # 仮登録成功ページヘ redirect_to :action => 'tmp' else @user = User.new(user_params) @user.created = false if @user.save # トークン生成 @token = SecureRandom.uuid @user.tokens.create!(uuid: @token, expired_at: 24.hours.since) # メール通知 @mail = TestMailer.user_create_mail(@user,@token).deliver redirect_to :action => 'tmp' else render 'new' end end end
コントローラー(本登録)
トークンURLにアクセスした時に有効期限内であれば本登録を行う。また、Userモデルに『:created』パラメータを用意して、本登録済みかどうかに用いている。
・ワンタイムトークンURLが有効なのは1回だけ。
def token # 有効期限の確認 token = Token.find_by_uuid!(params[:uuid]) # 有効期限を過ぎていないか確認 if token && token.expired_at > Time.now # 2回目アクセスできないように更新 token.update_attributes!(expired_at: Time.now) @user = token.user @user.update_attributes!(created: true) # 登録完了メール通知 flash.now.alert = "本登録が完了しました" @mail = TestMailer.user_complete_mail(@user).deliver # ログイン画面へ redirect_to :login else # トークンがない、もしくは2回目のアクセス -> 失敗を通知、ユーザ登録ページのリンクを貼る if token && token.user.created redirect_to :login, alert: "入力されたメールアドレスは本登録が完了しています。" else redirect_to :action => 'new', alert: "仮登録の有効期限が切れている。もしくは、URLが適切ではありません。" end end end
メーラー(仮登録)
ActionMailerのテンプレートには以下のサンプルのような感じで書けば良いでしょう。
ご利用いただきまして誠にありがとうございます。 ユーザ仮登録が完了しました。 本登録のため、下記のURLへのアクセスをお願いします。 http://ドメイン/users/token/<%= @token %>