[Ruby on Rails]sorceryによる認証 – (5)APIでの認証 #1 実装の概要

2015.06.08

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

sorceryを使用してAPIでの認証を試してみました。メールアドレス、パスワードでユーザを認証する点については公式チュートリアル以前の記事と同様ですが、Webアプリとは異なる点も幾つかあります。何回かに分けて、その辺りを中心に書いて行きたいと思います。

今回の設計思想

通常のWebアプリとは異なり、以下のような設計思想でAPIを実装しました。

  • サーバはJSONを返却するものとする。合わせてリクエストURLは「〜.json」とする。
  • ログイン時にAccessTokenを生成し、クライアントに返却する。またAccessTokenの有効期限も設定する。
  • ログイン後、クライアントはリクエスト時にAccessTokenをヘッダーに付ける。サーバはAccessTokenよりユーザ情報の各チェックを行う(ログイン済みか、有効期限内か)。
  • ログアウトはAccessTokenを無効にすることで実現する。

テーブル設計

テーブルについては以下の2テーブルを用意しました。

  • usersテーブル・・・ユーザ情報を保持する
  • api_keysテーブル・・・ユーザ毎のAccessToken、ログインしているかどうか、ログインの有効期限を管理する


項目は以下の通りです。

usersテーブル

項目名 概要
id INTEGER ユーザのID
email 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の認証