5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【 Devise × Resend 】独自ドメインでパスワードリセット機能を実装した話

Posted at

自己紹介

はじめまして。 おーがと申します。
未経験でのエンジニア転職を目指して Rails を学習中です。

現在は Web アプリ開発に取り組んでおり、
本記事は、その中で実際に経験した問題と、解決までの流れをまとめたものになります。

初心者目線での試行錯誤をそのまま書いているため、
同じような段階の方の参考になれば幸いです。

この記事の対象者

本記事は、以下のような方を対象としています。

  • Devise の パスワードリセット(recoverable) を実装したい方
  • Render の無料プランを利用している方
  • Resend API を Rails で使う具体的な設定例を探している方
  • 独自ドメインでメール送付したい方
  • SMTP を使わずに、API ベースでメール送信を行いたい方

「Render 無料枠で運用しつつ、Devise のパスワードリセットを実装したい」
という目的を持つ方に向けた記事です。

はじめに

本記事では、Devise × Resend を使って、
Rails アプリに パスワードリセット機能を実装した手順を解説します。

Devise には recoverable という機能があり、
ユーザーがパスワードを忘れた場合でも、メール経由で安全に再設定が可能です。

本記事では、

  • Resend のドメイン設定
  • Rails 側のメール設定
  • Resend API を使ったメール送信

といった実装部分を中心に解説します。

本記事で使用している開発環境は以下のとおりです。

  • Ruby:3.3.6
  • Ruby on Rails:7.2.2
  • Render / neon
  • Devise

注意書き

本記事は、Rails 学習中の初心者が実装・検証した内容をもとにしています。

  • より良い設計や別の選択肢が存在する可能性があります
  • 商用サービスでは、追加のセキュリティ検討が必要になる場合があります

あくまで、

Devise と Resend を使ってパスワードリセットを動かす

ことを目的とした記事です。

環境

実装背景

当初は、SMTP を利用したメール送信で
Devise のパスワードリセット機能を実装していました。

しかし、本番環境(Render の無料プラン)へデプロイしたところ、
メール送信時にタイムアウトが発生し、正常に送信できない問題が起きました。

Net::OpenTimeout: execution expired

調査の結果、これは Rails や Devise の設定不備ではなく、
Render 無料枠における外部 SMTP の通信制限が原因であると分かりました。

SMTP を経由せずにメール送信ができる方法として、 HTTP API を使用すると良いと分かり、
その中でも Resend API は、

  • 個人使用できる
  • 月3,000通まで無料かつクレカ登録無し
  • Rails 導入の公式ドキュメントがあり、gem を配布している

点から、最適だと判断しました。

本記事では、Resend API を使った構成で
パスワードリセットを実装する方法を、Rails 側の設定を含めて解説します。

Devise × Resend の全体構成

本記事で採用した構成では、
Devise の仕組みはそのままに、メールの送信手段だけを Resend API に置き換えています。

パスワードリセット時の流れ

  1. ユーザーがパスワードリセット画面からメールアドレスを送信
  2. Devise の recoverable がリセット用トークンを生成
  3. Devise が内部のメール送信機構を利用してメール送信を行う
  4. Resend API が実際のメール配信を行う
  5. ユーザーがメール内リンクからパスワードを再設定

Devise 側のロジックや挙動は一切変更していません。

役割分担

役割 担当
パスワードリセットのロジック Devise
トークン生成・検証 Devise
メール送信の呼び出し ActionMailer
メール配信 Resend API(Devise 経由)

ActionMailer の送信先を Resend に切り替えることで、
Devise から送信されるメールはすべて Resend 経由になります。

Resend 側の設定

Resendにアクセスします。

ドメイン追加

新規登録・ログインが完了したら画面左から Domains へアクセスします。

画面中央に Add domain とボタンがあるのでクリック。
ドメイン追加画面に遷移するので、取得しているドメインを入力してください。

image.png

Cloudflare でドメインを取得している場合は、Cloudflare へのログインボタンが表示されるので、指示に従うと、自動で DNS レコードを設定してくれます。
Authorizeを押して作成してください。

名称未設定のコピー (2).png

Cloudflareにアクセスし、DNS レコードが作成されていることを確認してください。

あとは、時間を置いて STATUS が Verified になるのを待ちましょう。
大体数分~30分以内に適用されます。

名称未設定のコピー (3).png

API Key の取得

次に API Key を取得します。
画面左から API Keys を選択。
API 管理画面に遷移するので、右上の Create API Key を選択してください。

名称未設定のコピー (5).png

API Key 追加画面がモーダルで開きます。

image.png

Nameに任意の名前を入力します。
Permission は権限の選択です。内容は以下となります。

  • Full access: API キーを使用して、あらゆるリソースを作成、削除、取得、更新
  • Sending access: API キーにメールの送信のみを許可

今回はメール送信のみなので Sending access を選択。

Domain は複数ドメインを使って送信したい場合は All Domains を選択してください。

Addボタンを押すと、API Key が発行されます。

image.png

ここで表示される API Key は後ほど記入する必要があるので、メモしてください。
この画面を閉じてしまうと、二度と API Key が確認できなくなってしまいます。
もしメモの前に画面を閉じてしまった場合は、もう一度発行し直してください。

以上で Resend 側の設定は完了です。
次に Rails 側を設定していきます。

Rails 側の設定

Devise の設定

Devise の recoverable が有効になっていることを確認します。

devise :database_authenticatable,
       :registerable,
       :recoverable,
       :rememberable,
       :validatable

これで、パスワードリセットに必要なロジックは Devise 側で提供されます。

Routing の設定

Devise のpasswords が有効になっていることを確認します。

  devise_for :users, controllers: {
    registrations: "users/registrations",
    sessions: "users/sessions",
    passwords: "users/passwords",
    omniauth_callbacks: "users/omniauth_callbacks"
  }

Resend API の設定

Gem の追加

resend 公式gemが配布されているのでインストールします。

# Gemfile
gem "resend"
bundle install

API キー・メールアドレスの設定(Rails側)

Resend の管理画面で 発行した API Key を設定します。
xxxxxxx部分をメモした API Key に差し替えてください。
MAIL_FROMに記入したメールアドレスからパスワードリセット用のメールが届きます。
登録したドメインを使用して任意のメールアドレスを記入してください。

※.env に書くことで情報が秘匿されます。後述するファイルに直接書かないでください。

# .env
RESEND_API_KEY=xxxxxxxx
MAIL_FROM=no-reply@example.com

initializer の作成

Resend に API Key を読み込ませます。
.env に設定した API Key を読み取ります。

# config/initializers/resend.rb
require "resend"

Resend.api_key = ENV.fetch("RESEND_API_KEY")

devise.rb の設定

devise 側で送信メールアドレス名を設定します。
.env に設定したメールアドレスを読み取ります。

# config/initializers/devise.rb
config.mailer_sender = ENV.fetch("MAIL-FROM")

次に以下の項目がコメントアウトされているので有効化します。

config/initializers/devise.rb
config.paranoid = true

有効化されていると、

  • メールアドレスが存在する場合
  • 存在しない場合

のどちらであっても、
同じメッセージを返す仕様になります。

これは ユーザー列挙攻撃(User Enumeration Attack) を防ぐ用途があります。
この挙動はセキュリティ対策として重要なため、
特別な理由がない限り変更しないことを推奨します。

development 環境の設定

config/environments/development.rb

config.action_mailer.delivery_method = :resend

config.action_mailer.default_url_options = {
  host: "localhost",
  port: 3000,
  protocol: "http"
}

production 環境の設定

config/environments/production.rb

config.action_mailer.delivery_method = :resend

config.action_mailer.default_url_options = {
  host: "kocolog.com",
  protocol: "https"
}

API キー・メールアドレスの設定(Render側)

render を開いてください。

自身の WEB SERVICE を開いた後、左欄から Envieronment を選択してください。
Environment ページ中の Environment Variables に.envと同じ内容を記述して保存します。

image.png

最後に

本記事では、Devise × Resend API を使って
パスワードリセット機能を実装した手順を紹介しました。

  • SMTP に依存せず、HTTP API ベースでメール送信を行う構成
  • Render の無料プランでも本番環境でメール送信が可能
  • Devise の機能をそのまま活かし、設定のみで切り替え

本記事が、
「ローカルでは動くのに本番でメールが届かない」
と悩んでいる方の助けになれば幸いです。

5
2
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
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?