LoginSignup
1
1

Rails7.1から追加されたgenerates_token_forで期間限定のダウンロードリンクを作る

Posted at

はじめに

本記事は錆びかけたRailsの知識を頑張ってアップデートするアドベントカレンダー18日目です。

今回は、Rails7.1で追加されたメソッドであるgenerates_token_forを使った実装を試してみました。

generates_token_forとは?

トークン(簡易的な鍵のようなもの)を簡単に生成できるメソッドです。

これを使うことで、例えばユーザーのパスワード変更などを行うためのトークンを発行することができます。

generates_token_forを使いダウンロード時にトークンで認証する

背景

Railsガイドの例では、このメソッドを使ってパスワード変更を行うための認証トークンを作っていました。

generates_token_forの例
class User < ActiveRecord::Base
  has_secure_password

  generates_token_for :password_reset, expires_in: 15.minutes do
    # `password_salt`(`has_secure_password`で定義される)は、
    # そのパスワードのsaltを返す。パスワードが変更されるとsaltも変更されるので、
    # パスワードが変更されるとこのトークンは無効になる。
    password_salt&.last(10)
  end
end

user = User.first
token = user.generate_token_for(:password_reset)

User.find_by_token_for(:password_reset, token) # => user

user.update!(password: "new password")
User.find_by_token_for(:password_reset, token) # => nil

しかし、Railsでユーザー認証機能といえばdeviseがあるため、パスワード変更をわざわざこちらで作らずともdeviseの用意した方法を使えば良いのでは?と思ってしまいました。

そこで、期間限定のダウンロードリンクを作れるようにできたら面白いかも?と考え、作ってみました。

前提

  • deviseでユーザー認証を実装済み
  • ユーザーは、自身がすでにアップロードしているファイルを指定して、それを誰でもダウンロードできるリンクを発行できる
  • このリンクは期間限定で、リンクを発行する際にその時間を決めることができる

ダウンロードリンクを任意のユーザーが作成できるようにするのはリスクがあります。こちらの機能はアプリケーションの管理者など限られた人が利用する想定です。

実装

まず、ユーザーがファイルと有効期限を指定してリンクを生成できるようにし、次にそのリンクを介してファイルがダウンロードされる際に期限をチェックできるようにします。

まず、DownloadLinkモデルを作成し、それにexpires_atfile_pathカラムを持たせます。file_pathはダウンロードさせたいファイルのパスを保存します。

モデル

download_link.rb
class DownloadLink < ApplicationRecord
  belongs_to :user
  generates_token_for :access, expires_in: 15.minutes

  def expired?
    Time.current > expires_at
  end
end

コントローラー

download_links_controller.rb
class DownloadLinksController < ApplicationController
  before_action :authenticate_user!

  def new
    @download_link = current_user.download_links.new
  end

  def create
    @download_link = current_user.download_links.new(download_link_params)
    if @download_link.save
      token = @download_link.generate_token_for(:access)
      session[:download_token] = token
      redirect_to @download_link, notice: "Download link created successfully."
    else
      render :new
    end
  end

  def show
    @download_link = DownloadLink.find(params[:id])
    @token = session[:download_token]
  end

  def download
    @download_link = DownloadLink.find_by_token_for(:access, params[:token])

    if @download_link.nil? || @download_link.expired?
      redirect_to root_path, alert: "This download link has expired or is invalid."
    else
      send_file @download_link.file_path
    end
  end

  private

  def download_link_params
    params.require(:download_link).permit(:expires_at, :file_path)
  end
end

ルーティング

routes.rb
resources :download_links, only: [:new, :create, :show]
get '/download/:token', to: 'download_links#download', as: 'download'

使い方

ユーザーはdownload_links#newで期間限定のダウンロードリンクについての情報を入力します。ダウンロードリンクや、ダウンロードできる期限を設定できます。

その後はshowにリダイレクトされ、ダウンロード用のリンク(トークン付き)が表示されます。

表示されたトークンを含むURLでリクエストすることでdownload_links#downloadが動き、トークンの検証が成功すればダウンロードが開始されます。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1