Daily Archives: 2013年9月22日

Yesod-Auth の HashDB を使うサンプルできたよ

日々のすきをみてドン亀のごときペースで Haskell の勉強をしているのですが、入門書片手に学ぶのも飽きてきたので、実際になんか作ってみようとしています。

そこで Haskell の Web  アプリケーションフレームワーク「Yesod」に挑戦しています。

Yesod はデフォルト状態(プロジェクトを作成直後の状態)ですでにログイン認証の仕組みが使えるようになっているのですが、この認証方式が Google の仕組みを使う一種のシングルサインオン方式になっているのです。これはこれで技術的に興味深いのですが、しかしじゃあ実際にどれぐらい使うんだというと、実案件ではそうそう使うものではないと思います。実際によく使う方式といえばやはり DB にユーザ名とパスワードを保存しておき、入力値がそれにマッチするかを見る方式が多いのではないでしょうか?

Yesod には標準で Yesod-Auth という認証の仕組みがあって、このサブパッケージによって前述の Google を使った認証方法や BrowserID を使った認証方法など様々な認証方法を提供しています。そして当然 DB を使った認証方法も提供しています。それが Yesod.Auth.HashDB パッケージ(以下 HashDB)です。

しかし、これを使うためのドキュメントでわかりやすいのがなかなか見つからないのです! 英文のリーディング自体がたどたどしい Haskell と Yesod の初学者にはイバラの道以外の何物でもないような状態だと私は感じました。しかしなんとか最低限何をすればいいのかまとめることができましたので紹介します。

なお、今回使用した Yesod のバージョンは 1.2.3.3 です。この記事に関するソースコードは Github で公開しています。diff を見てみるとどこに手を付けたのかわかりやすいかもしれません。

手を付けるべきファイルは3つ

そもそもデフォルトの状態から最低どのファイルに手をいれるべきかもなかなか定かにならなかったのですが、以下の3つが修正対象です。

  • config/models
  • Models.hs
  • Foundation.hs

config/models

まず、デフォルトでもユーザ情報を格納するためのテーブル定義を行っているのですが、HashDB で使用するには若干手直ししなければなりません。ポイントは以下の点です。

  • パスワードは必須項目でなければなりません。
  • salt カラムが必要です。

デフォルトでは password カラムに Maybe がついているのでパスワードが必須項目になっていません。また、HashDB ではパスワードは SHA-1 でハッシュ化して扱います。その際に salt の値を使います。具体的な例をあげると、パスワードが “secret”、salt が “shio” だとすると “shiosecret” をハッシュ化した値を使用するわけです。この salt を保存するカラムが必要になります。

以上を踏まえると、config/models の User 定義は以下のようになります。

1
2
3
4
5
6
User
    ident Text
    password Text
    salt Text
    UniqueUser ident
    deriving Typeable

Model.hs

config/models の定義によってモデルが User 型が定義されますが、HashDB で使用するには HashDBUser 型クラス制約を満たさなければなりません。それのための修正を Model.hs で行います。まず、HashDB を Import します。

5
import Yesod.Auth.HashDB (HashDBUser(..))

次に型インスタンスの作成です。

17
18
19
20
21
22
instance HashDBUser User where
  userPasswordHash = Just . userPassword
  userPasswordSalt = Just . userSalt
  setSaltAndPasswordHash s h p = p { userSalt = s
                                  , userPassword = h
                                  }

Foundation.hs

最後の Foundation.hs で認証方式を設定します。まず、BrowserID 認証方式と GoogleEmail 認証方式を使用しないので、以下の Import は削除してかまいません。

7
8
import Yesod.Auth.BrowserId
import Yesod.Auth.GoogleEmail

かわりに HashDB のための Import を追加します。

7
import Yesod.Auth.HashDB (authHashDB, getAuthIdHashDB)

getAuthId と authPlugins を修正します。

124
125
126
127
    getAuthId = getAuthIdHashDB AuthR (Just . UniqueUser)
 
    -- You can add other plugins like BrowserID, email or OAuth here
    authPlugins _ = [authHashDB (Just . UniqueUser)]

以上で完了です。

その他、使い方等は Github にあげている readme.md を参照してください。