はじめに
この記事は「こうすればパスワードリセット機能作れる!」じゃなくて、「こういう仕様にしたけど漏れ無いかな?」ぐらいのノリで作っているので、参考にする場合は自己責任でおねがいします。(そんなこと書かずとも基本的には自己責任なんだけど)指摘歓迎です。(そんなこと書かずとも基本的には指摘歓迎なんだけど)
作ったパスワードリセット機能は、メールアドレスをアカウント登録時に用意しておき、うっかりパスワード忘れてしまった人向けに、パスワードリセット用のURLを送付してリセットしてもらうものです。
TrainStampRally https://train.ponkotuy.com という自作のゲームに、独自のアカウント認証があるので、これに対して実装しました。
使ってるもの
APIサーバはよくあるScala + Playframeworkプロジェクトで、JSONを返すだけのAJAX的な実装。クライアントはCoffeeScript + Vue.jsでレンダリングしてます。
認証はplay2-auth pluginを使ってBasic認証しており、パスワードはBCryptで保存しているので、生パスワードは開発者も知ることはできない状態になっています。
メールを送信する必要があります。様々な方法があると思いますが、今回はAmazonSESを使っています。一定量は無料で使えるので、この程度ならほぼ無料で使えるでしょう。
仕組み
まずはユーザからパスワードリセット要求があります。
PUT /api/password_reset
このとき現行仕様ではEmailアドレスを要求しますが、Emailアドレス忘れてもユーザ名とかで検索できる仕様にすることは可能だと考えています(単に面倒くさかっただけ)
リセット要求がきたらmailアドレスからアカウントを検索、該当アカウントにUUIDを発行します。臨時のアクセスキーみたいなものですね。該当アカウントが既にUUIDを発行していた場合は削除します。java.util.UUIDのrandomUUIDメソッドで発行し、パターンは完全ランダムです。(サーバや日付に依存した値は入らない)
その後、UUID付きのURLが書いてあるメールを送信します。
※このメールはAmazonSESによって送信されています
TrainStampRallyのパスワードリセットメールです。
心当たりのない方は無視するか返信で教えていただけると幸いです。
以下のURLにてパスワードを再設定することができます。
https://train.ponkotuy.com/auht/password_reset.html?secret=a0202ae2-4c77-4f87-a5fe-d0dd0fbdecd9
有効期間は1日間のみです。それ以降は再送してください。
内容はこのようなものです。
記載URLでは新しいパスワードを設定するフォームがあります。このページはsecretの文字に関係なく表示されますが、この値を用いてAPIサーバに
DELETE /api/password_reset
を発行します。当然先のUUIDと新しいpasswordのデータを入れます。1日以内に発行されたUUIDと照合し、ヒットすれば新しいpasswordが設定され、UUIDは削除されます。
以上でパスワードリセットが完了するという仕組みです。
AmazonSES補講
今回使ったAmazonSESですが、リリースしてから、外部にメールを出すには申請が必要で申請に1営業日掛かると気付きました。アホす。
AmazonSESは1通辺りが安いですが到達率を一定以上に保つ必要があるなど、色々条件が多いので、大した数にならないパスワードリセットでは他のサービスを使った方が楽かもしれません。