[Ruby on Rails]sorceryによる認証 – (5)APIでの認証 #1 実装の概要
はじめに
sorceryを使用してAPIでの認証を試してみました。メールアドレス、パスワードでユーザを認証する点については公式チュートリアルや以前の記事と同様ですが、Webアプリとは異なる点も幾つかあります。何回かに分けて、その辺りを中心に書いて行きたいと思います。
今回の設計思想
通常のWebアプリとは異なり、以下のような設計思想でAPIを実装しました。
- サーバはJSONを返却するものとする。合わせてリクエストURLは「〜.json」とする。
- ログイン時にAccessTokenを生成し、クライアントに返却する。またAccessTokenの有効期限も設定する。
- ログイン後、クライアントはリクエスト時にAccessTokenをヘッダーに付ける。サーバはAccessTokenよりユーザ情報の各チェックを行う(ログイン済みか、有効期限内か)。
- ログアウトはAccessTokenを無効にすることで実現する。
テーブル設計
テーブルについては以下の2テーブルを用意しました。
- usersテーブル・・・ユーザ情報を保持する
- api_keysテーブル・・・ユーザ毎のAccessToken、ログインしているかどうか、ログインの有効期限を管理する
項目は以下の通りです。
usersテーブル
項目名 | 型 | 概要 |
---|---|---|
id | INTEGER | ユーザのID |
varchar | メールアドレス | |
name | varchar | ユーザ名 |
crypted_password | varchar | 暗号化したパスワード |
salt | varchar | 暗号化時のsalt |
created_at | datetime | 作成日時 |
updated_at | datetime | 更新日時 |
api_keysテーブル
項目名 | 型 | 概要 |
---|---|---|
id | INTEGER | ユニークキー |
access_token | varchar | ユーザがログイン時に送るAccessToken |
expires_at | datetime | ログインの有効期限 |
user_id | integer | ユーザID |
created_at | datetime | 作成日時 |
updated_at | datetime | 更新日時 |
active | boolean | ログイン済かどうか(true:ログイン状態、false:ログアウト状態) |
コントローラのアクションとURL
実装したアクションとURLの概要は以下の通りです。
UsersController
アクション名 | 動詞(GET,PUT,POST,DELETE) | URL | 概要 |
---|---|---|---|
index | GET | api/v1/users.json | ユーザの一覧を返却する |
show | GET | api/v1/users/ユーザID.json | 指定したユーザを返却する |
create | POST | api/v1/users.json | ユーザを作成する |
update | PUT | api/v1/users/ユーザID.json | ユーザを更新する |
destroy | DELETE | api/v1/users/ユーザID.json | ユーザを削除する |
UserSessionsController
アクション名 | 動詞(GET,PUT,POST,DELETE) | URL | 概要 |
---|---|---|---|
create | POST | api/v1/user_sessions.json | ログインを行う。api_keysテーブルにデータが無ければ作成し、あればactiveをtrueにする。 |
destroy | DELETE | api/v1/user_sessions/ユーザID.json | ログアウトを行う。api_keysテーブルのactiveをfalseにする。 |
SampleController
アクション名 | 動詞(GET,PUT,POST,DELETE) | URL | 概要 |
---|---|---|---|
public | GET | api/v1/sample/public.json | ログインの有無に関わらず、値を返却するアクション |
restrict | GET | api/v1/sample/restrict.json | ログインしている場合のみ、値を返却するアクション |
ApplicationBaseController
アクション名 | 動詞(GET,PUT,POST,DELETE) | URL | 概要 |
---|---|---|---|
なし | なし | なし | 上記コントローラのベースクラスとし、共通のロジックを記述する。 |
認証のフロー
今回作成する機能のイメージを掴みやすくするため、認証のフロー順にAPIを呼び出した結果を貼付けておきます。
1.ユーザの作成
ログインするためのユーザを作成します。APIなのでCurlで以下のようにリクエストを送ります。
$ curl -i -X POST http://localhost:3000/api/v1/users.json -d 'user[email]=sample@ggg.com' -d 'user[name]=sample' -d 'user[password]=password' -d 'user[password_confirmation]=password' HTTP/1.1 201 Created
Userテーブルには以下のように登録されます。
sqlite> select * from users; id|email|name|crypted_password|salt|created_at|updated_at 51|sample@ggg.com|sample|$2a$10$GgTd2XY45cadkMy7/rO8rOyVhnA4o4qwwNnGP02waTJK7ykOBfzk2|D6yPM8DkiiJphzEHLEPn|2015-06-02 22:54:45.890995|2015-06-02 22:54:45.890995
2.ログイン
作成したユーザでログインします。
$ curl -i -X POST http://localhost:3000/api/v1/user_sessions.json -d 'user[email]=sample@ggg.com' -d 'user[password]=password' HTTP/1.1 200 OK (中略) {"user":{"id":51,"email":"sample@ggg.com","name":"sample"},"access_token":"8835b89b52db281a9988847a0cda2e58"}
ログインに成功すると、ユーザ情報とAccessTokenがJSONで返却されてきます。
ログイン時に生成したAccessTokenはApiKeyテーブルに登録されます。
sqlite> select * from api_keys; id|access_token|expires_at|user_id|created_at|updated_at|active 7|8835b89b52db281a9988847a0cda2e58|2015-06-04 15:16:49.613691|51|2015-06-03 15:16:49.585322|2015-06-03 15:16:49.585322|t
3.ユーザ情報の取得
ログインできたので、ユーザ情報を取得してみます。先にも書いたとおり、AccessTokenをヘッダーに設定してリクエストを投げます。
$ curl -i -X GET -H 'ACCESS_TOKEN: 8835b89b52db281a9988847a0cda2e58' http://localhost:3000/api/v1/users.json HTTP/1.1 200 OK (中略) [{"id":51,"email":"sample@ggg.com","name":"sample"}] $ curl -i -X GET -H 'ACCESS_TOKEN: 8835b89b52db281a9988847a0cda2e58' http://localhost:3000/api/v1/users/51.json HTTP/1.1 200 OK (中略) {"id":51,"email":"sample@ggg.com","name":"sample"}
ユーザの一覧と、ユーザIDを指定しての取得です。一覧のほうは一件だけですが配列が返却されています。
4.ユーザ情報の変更
ユーザ情報を変更してみます。
$ curl -i -X PUT -H 'ACCESS_TOKEN: 8835b89b52db281a9988847a0cda2e58' http://localhost:3000/api/v1/users/51.json -d 'user[email]=rails@ggg.com' -d 'user[name]=rails' -d 'user[password]=passoword' -d 'user[password_confirmation]=passoword' HTTP/1.1 200 OK (中略) {"id":51,"email":"rails@ggg.com","name":"rails"}
Userテーブルを見ると、リクエストで投げた値で更新されています。
sqlite> select * from users; id|email|name|crypted_password|salt|created_at|updated_at 51|rails@ggg.com|rails|$2a$10$YJsfOjxL1qexVcVffnWNduGHTB6pRTls5Zowydon1MLckQYTmv/ni|8gD8yxJrLmCGpwaASvdJ|2015-06-02 22:54:45.890995|2015-06-04 14:33:06.374014
5.ログアウト
ログアウトしてみます。
$ curl -i -X DELETE -H 'ACCESS_TOKEN: 8835b89b52db281a9988847a0cda2e58' http://localhost:3000/api/v1/user_sessions.json HTTP/1.1 200 OK
ApiKeyテーブルを見ると、ログイン中を表す「Active」がFalse(f)になっています。
sqlite> select * from api_keys; id|access_token|expires_at|user_id|created_at|updated_at|active 7|8835b89b52db281a9988847a0cda2e58|2015-06-06 15:16:49.613691|51|2015-06-03 15:16:49.585322|2015-06-04 15:23:02.265679|f
6.ログアウト状態でのリクエスト
ログアウトした状態で、全ユーザに公開しているアクション(public)と、ログイン済みユーザしかアクセスできないアクション(restrict)にリクエストを送ってみます。
$ curl -i -X GET http://localhost:3000/api/v1/sample/public.json HTTP/1.1 200 OK (中略) {"message":"public"} $ curl -i -X GET -H 'ACCESS_TOKEN: 8835b89b52db281a9988847a0cda2e58' http://localhost:3000/api/v1/sample/restrict.json HTTP/1.1 401 Unauthorized
7.再びログイン
再びログインし、ログイン済みユーザしかアクセスできないアクションにリクエストを送ってみます。
$ curl -i -X GET -H 'ACCESS_TOKEN: 8835b89b52db281a9988847a0cda2e58' http://localhost:3000/api/v1/sample/restrict.json HTTP/1.1 200 OK (中略) {"message":"authorized"}
まとめ
概要について書いているだけで、それなりの文字数になってしまいました。次回以降、実装について書いていきたいと思います。
今回作成したソースコードは以下のGithubに置いてあります。全ソースを見たい方は参考にしてください。 sorcery_api_sample
参考サイト
今回は以下のサイトを主に参考にさせて頂きました。ありがとうございました。
Simple Password Authentication
Securing an API
Rails + Grape + API Keyの認証