【Laravel8】メールアドレスの変更APIを作ってみる。email verify

今回はメールアドレスの変更APIを作成してみます。

普通にユーザーに対してupdateしたらよいんじゃないか?って声は聞こえてきそうですが、そうはいかないのです。

なぜなら…?

伝えたいこと: メールアドレス変更API作成の方法

まず、なぜメールアドレスの変更を行うのか。基本的にwebサービスではユーザー登録を行った際にemail verifyが行われますよね?

email verifyとは、「登録したメールアドレスってちゃんとあなたが使っていますよね?」って確認するやつです。

ユーザー登録を行うと、メールが届いてurlにアクセスしてください。っていうのが基本的ですがそれです。

そのemail verifyが行われたメールアドレスから別のアドレスに変更する際に、普通にユーザー情報の更新と一緒にemail verify無しで更新しちゃったら…

ダミーメールアドレスかもしれないですよね?

それを防ぐためにも、メールアドレスを変更するときにはそれ専用のAPIが必要となってくるわけです。

そして今回メールアドレスを変更する流れは以下です。

  1. ユーザーがメールアドレス変更の申請を行う。(新メールアドレスを送信)
  2. 新メールアドレスに対してtokenを発行して、新メールアドレスにメールを送る。(今回メールを送る機能は作りません。)
  3. ユーザーがメールから届いたurlにアクセスするとメールアドレスの更新は完了。

この流れをAPI的に作ってみましょう。

前提条件

以下を前提として進めていきますね。

  • Laravel8
  • APIをつかった方法
  • メール送信の機能は作成しない
  • すでにuserテーブルは作成されていると仮定
  • 認証機能は作成済み(sanctum)

メールアドレス変更の申請API

今回はこちらの記事を参考にさせていいただいています。

新メールアドレスとtokenを保存するテーブルを作成

ほい、それでは作っていきましょうか。

まず作りたいのは、まずは新メールアドレスを保存するテーブルを作らないといけませんね。

最終的にここで作成するテーブルを見てユーザー情報を更新します。

user_email_resetsテーブルを作成しましょう。

php artisan make:migration create_user_email_resets_table
Code language: CSS (css)

ではテーブルの中身を書いていきます。

<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateUserEmailResetsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { //userがメールアドレスを変更する時に利用するテーブル Schema::create('user_email_resets', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('user_id')->comment('users.id'); $table->string('new_email')->comment('ユーザーが新規に設定するメールアドレス'); $table->string('token')->unique()->comment('email address変更用token'); $table->dateTime('expired_at')->nullable()->comment('tokenの有効期限'); $table->timestamps(); $table->foreign('user_id')->references('id')->on('users')->onUpdate('cascade')->onDelete('cascade'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('user_email_resets'); } }
Code language: HTML, XML (xml)

こんな感じで作ります。

正直tokenの有効期限はcreated_atを見てそこから何時間か計算したりしてもよいんですが、今回は有効期限用のカラムも準備しました。そっちのほうが個人的にシンプルに見える!

そしたらテーブルを作成っと

php artisan migrate

モデルを作成

先ほど作成したテーブルに付随するモデルを作成しましょう。

php artisan make:model UserEmailReset
Code language: CSS (css)

モデルには今回大したことは書いていません。

<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class UserEmailReset extends Model { use HasFactory; protected $fillable = [ 'user_id', 'new_email', 'token', 'expired_at', ]; }
Code language: HTML, XML (xml)

メールアドレス変更を申請するControllerの作成

php artisan make:controller Change
Code language: CSS (css)
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; //追加 use App\Http\Requests\EmailReset\CreateTokenUserEmailResetRequest; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Str; use App\Models\UserEmailReset; use App\Models\User; class ChangeUserEmailController extends Controller { public function createToken(CreateTokenUserEmailResetRequest $request) { //token生成 $token = hash_hmac( 'sha256', Str::random(40), config('app.key') ); //メール変更したいユーザー取得 $user_id = Auth::id(); //createメソッドを使ってレコードを追加する。 $reset_info = UserEmailReset::create([ 'user_id' => $user_id, 'new_email' => $request->email, 'token' => $token, 'expired_at' => date("Y-m-d H:i:s", strtotime("+1 day", time())), //tokenの有効期限は作成日から24時間 ]); //成功をレスポンスで返す。 return response()->json([ 'success' => true, 'message' => 'Create email reset token success!', 'details' => $reset_info->token ]); } }
Code language: HTML, XML (xml)

とりあえずtokenの有効期限は1日にしておきました。1時間とかのほうが良いんかなぁ…?

hash_hmac()の使い方

FormRequest

一応バリデーションはformrequestに書いていきます。

php artisan make:request EmailReset/CreateTokenUserEmailResetRequest

ほんでバリデーション

<?php namespace App\Http\Requests\UserEmailReset; use Illuminate\Foundation\Http\FormRequest; //追加 use Illuminate\Validation\Rule; class CreateTokenUserEmailResetRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'email' => ['required', 'string', 'email', 'max:255', Rule::unique('users')], ]; } }
Code language: HTML, XML (xml)

今回の場合は変更後のメールアドレスをリクエストパラメータに入れて送られてくるので、emailというパラメータをバリデーションしましょう。

これでOK後はroutingに追加。

api.php(routing)に追加

use App\Http\Controllers\ChangeUserEmailController; //sanctum認証 Route::middleware('auth:sanctum')->group(function(){ Route::post('user/email-reset', [ChangeUserEmailController::class, 'createToken']); });
Code language: PHP (php)

なんで認証入れてるのかというのは、controllerのところでAuth::id()を利用しているからですね~

認証済みのユーザーからユーザー情報を取得しています。

リクエストパラメータ

今作ったAPIで送るリクエストは、新しくしたいメールアドレスをリクエストパラメータに入れて送ります。

POST api/user/email-reset

{ email: new_email@email.com }
Code language: CSS (css)

レスポンスには、アクティベート用のtokenが返ってきます。

その返ってきたtokenを使って次のAPIでアクティベートをかけます。

メールアドレス変更を完了するAPI

ChangeUserEmailControllerに追記

そしたら次はemailを変更する機能を作りましょう。

public function resetEmail($token) { //送られてきたtokenが存在しない場合エラーを返す if(!UserEmailReset::where('token', $token)->exists()){ return response()->json([ 'success' => false, 'message' => 'token does not exist', 'details' => $token, ]); } $email_reset = UserEmailReset::where('token', $token)->first(); //tokenが存在&tokenの期限が切れていない場合はuser_email_resetsテーブルをみて //new_emailを対象のユーザーのメールアドレスとして更新する。 if($email_reset && $email_reset->expired_at >= now()) { $user = User::where('id', $email_reset->user_id)->first(); $user->email = $email_reset->new_email; $user->save(); //updateできたらレコードを削除する UserEmailReset::where('token', $token)->delete(); return response()->json([ 'success' => true, 'message' => 'Update email address success!', 'details' => $user ]); }else{ //対象のレコードのtokenの期限が切れているとレコードを削除する。ユーザーは再設定が必要。 UserEmailReset::where('token', $token)->delete(); return response()->json([ 'success' => false, 'message' => 'your token has expired', 'details' => $email_reset, ]); } }
Code language: PHP (php)

今回はGETパラメーターに先ほど作成したtokenが送られてくるのでそのtokenから情報を取得してユーザーのメールアドレスをupdateさせます。

まず最初に以下の動作を行います。

  1. リクエストで送られてきたtokenがuser_email_resetsテーブルに存在するか確認
  2. tokenの有効期限は切れていないか確認

問題がなさそうだったら、user_email_resetsテーブルにuser_idカラムがあるのでusersテーブルのidカラムと一致するデータを取得します。

そして最後にメールアドレスをuser_email_resets.new_emailからメールアドレスを取得して、users.emailにupdateします!

そうするとアクティベートの完成!

アクティベートができたらuser_email_resetsの対象レコードを削除しておきます!

routing (api.php)

routingに以下を追加します。

Route::get('user/email-reset/{token}', [ChangeUserEmailController::class, 'resetEmail']);//新規メールアドレスに更新
Code language: PHP (php)

リクエストはGETリクエストでGETパラメータに先ほどのresetのAPIで作成したtokenを入れて下さい。

まとめ

意外とシンプルにできましたね。

laravelってデフォルトでメールアドレス変更する用の機能ないんかなぁって思いました。

あったらすみません。

コメントを残す

メールアドレスが公開されることはありません。

CAPTCHA