🎩

OAuth2.0認可サーバーを実装したので解説する②【設計編】

13 min read

OAuth2.0とは

認可のフローを標準化したフレームワークです。
APIを外部に公開する等の際、OAuth2の仕様に従って認可サーバーを実装すれば高いセキュリティを確保出来ます。

RFC6749The OAuth 2.0 Authorization Framework に仕様が定義されてます。
OpenID Foundation Japan 様より、日本語訳もされているのでこちらも参考になると思います。

設計編について

こちらの記事では、OAuth2認可サーバーを実装するにあたって、サンプルアプリを例にDBのテーブル設計や仕様に焦点を当てて解説していきます。

認可のフロー

以下はサンプルアプリのシーケンス図です。この図を見ながら読み進めるとよりイメージが湧きやすいと思います。

OAuth2フロー

テーブル一覧

テーブル一覧を出力します。

mysql> show tables;
+----------------------------+
| Tables_in_tsubuyaki        |
+----------------------------+
| access_tokens              |
| apps                       |
| apps_scopes                |
| authorization_codes        |
| authorization_codes_scopes |
| clients                    |
| redirect_uris              |
| refresh_tokens             |
| schema_migrations          |
| scopes                     |
| statuses                   |
| users                      |
+----------------------------+

ER図

ER図

どのテーブルが何の役割を果たしているかについては以下で解説していきます。

App

認可するアプリケーションを管理します。サンプルアプリの例では、にゃーんサーバーをこのテーブルに登録します。

mysql> desc apps;
+------------+-------------+------+-----+-------------------+-----------------------------+
| Field      | Type        | Null | Key | Default           | Extra                       |
+------------+-------------+------+-----+-------------------+-----------------------------+
| id         | int(11)     | NO   | PRI | NULL              | auto_increment              |
| name       | varchar(25) | NO   |     | NULL              |                             |
| user_id    | int(11)     | NO   | MUL | NULL              |                             |
| deleted_at | datetime    | YES  |     | NULL              |                             |
| created_at | datetime    | NO   |     | CURRENT_TIMESTAMP |                             |
| updated_at | datetime    | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+------------+-------------+------+-----+-------------------+-----------------------------+

以下で登場するclientはこのappに帰属します。

Client

OAuth2サーバーとクライアント(にゃーんサーバー)の認可の通信は、このClientで認証(Client ID, Client Secret)した上で行います。

mysql> desc clients;
+------------+-------------+------+-----+-------------------+-----------------------------+
| Field      | Type        | Null | Key | Default           | Extra                       |
+------------+-------------+------+-----+-------------------+-----------------------------+
| id         | int(11)     | NO   | PRI | NULL              | auto_increment              |
| name       | varchar(25) | NO   | UNI | NULL              |                             |
| secret     | varchar(50) | NO   | UNI | NULL              |                             |
| app_id     | int(11)     | NO   | MUL | NULL              |                             |
| deleted_at | datetime    | YES  |     | NULL              |                             |
| created_at | datetime    | NO   |     | CURRENT_TIMESTAMP |                             |
| updated_at | datetime    | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+------------+-------------+------+-----+-------------------+-----------------------------+

Client IDはテーブルではnameというカラム名で定義されているので注意してください。(勿論、idClient IDを入れる設計でも問題ありませんが、今回はプライマリーキーはnumberにしました。)

カラム 説明
id Primary Key
name Client ID
secret Client Secret

RedirectURIとScope

RedirectURIは認可画面からクライアントにリダイレクトさせる時のエンドポイントを指定します。このリダイレクトする時に認可コードを受け取ることになる重要なURLです。

mysql> desc redirect_uris;
+------------+--------------+------+-----+-------------------+-----------------------------+
| Field      | Type         | Null | Key | Default           | Extra                       |
+------------+--------------+------+-----+-------------------+-----------------------------+
| id         | int(11)      | NO   | PRI | NULL              | auto_increment              |
| app_id     | int(11)      | NO   | MUL | NULL              |                             |
| uri        | varchar(255) | NO   |     | NULL              |                             |
| created_at | datetime     | NO   |     | CURRENT_TIMESTAMP |                             |
| updated_at | datetime     | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+------------+--------------+------+-----+-------------------+-----------------------------+

scopeとは認可する権限のことです。
サンプルアプリでは、scopeテーブルには最初からreadwriteの値が入ってます。

mysql> desc scopes;
+------------+--------------+------+-----+-------------------+-----------------------------+
| Field      | Type         | Null | Key | Default           | Extra                       |
+------------+--------------+------+-----+-------------------+-----------------------------+
| id         | int(11)      | NO   | PRI | NULL              | auto_increment              |
| scope      | varchar(255) | NO   |     | NULL              |                             |
| deleted_at | datetime     | YES  |     | NULL              |                             |
| created_at | datetime     | NO   |     | CURRENT_TIMESTAMP |                             |
| updated_at | datetime     | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+------------+--------------+------+-----+-------------------+-----------------------------+

mysql> select * from scopes;
+----+-------+------------+---------------------+---------------------+
| id | scope | deleted_at | created_at          | updated_at          |
+----+-------+------------+---------------------+---------------------+
|  1 | read  | NULL       | 2021-11-03 11:12:37 | 2021-11-03 11:12:37 |
|  2 | write | NULL       | 2021-11-03 11:12:42 | 2021-11-03 11:12:42 |
+----+-------+------------+---------------------+---------------------+
mysql> desc apps_scopes;
+----------+---------+------+-----+---------+-------+
| Field    | Type    | Null | Key | Default | Extra |
+----------+---------+------+-----+---------+-------+
| app_id   | int(11) | NO   | PRI | NULL    |       |
| scope_id | int(11) | NO   | PRI | NULL    |       |
+----------+---------+------+-----+---------+-------+

AuthorizationCode

発行された認可コードを管理するテーブルです。認可コードとは、認可画面からRedirectURIに戻った時に受け取るcodeです。

is_used

認可コードは一度しか使えないので、使われたら必ずis_usedtrueにし、2回目以降は使えないようにする必要があります。

client_id

authorization_codeからaccess_tokenを発行する際、Clientを認証し、authorization_codeに紐づくClientか確認するのに必要です。

user_id

どのユーザーによる認可かを保存します。

mysql> desc authorization_codes;
+------------+-------------+------+-----+-------------------+-----------------------------+
| Field      | Type        | Null | Key | Default           | Extra                       |
+------------+-------------+------+-----+-------------------+-----------------------------+
| id         | int(11)     | NO   | PRI | NULL              | auto_increment              |
| client_id  | int(11)     | NO   | MUL | NULL              |                             |
| code       | varchar(50) | NO   | UNI | NULL              |                             |
| user_id    | int(11)     | NO   | MUL | NULL              |                             |
| is_used    | tinyint(1)  | NO   |     | 0                 |                             |
| created_at | datetime    | NO   |     | CURRENT_TIMESTAMP |                             |
| updated_at | datetime    | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+------------+-------------+------+-----+-------------------+-----------------------------+

以下はauthorization_codescopeを結びつけるテーブルです。ユーザーにどの権限を認可されたのかを管理するためのテーブルです。

mysql> desc authorization_codes_scopes;
+-----------------------+---------+------+-----+---------+-------+
| Field                 | Type    | Null | Key | Default | Extra |
+-----------------------+---------+------+-----+---------+-------+
| authorization_code_id | int(11) | NO   | PRI | NULL    |       |
| scope_id              | int(11) | NO   | PRI | NULL    |       |
+-----------------------+---------+------+-----+---------+-------+

AccessTokenとRefreshToken

認可コードの検証が成功したら、access_tokenを発行します。access_tokenを更新するためのrefresh_tokenも一緒に発行します。

client_id

access_token, refresh_tokenがどのClientに発行されたものなのか保存します。
特に、アクセストークンをリフレッシュする時、Clientの認証を行い、refresh_tokenがそのClientに発行されたものであることをチェックする時に必要です。 RFC6749 10.4. Refresh Tokens

authorization_code_id

access_tokenはどの権限が認可されているのかを管理する必要があるため、authorization_codeidを持っています。(refresh_tokenも同様にどの権限を持ったaccess_tokenを発行するかを管理するためauthorization_codeidを持ちます。)

is_revoked

access_tokenrefresh_tokenは無効にするAPIを提供する必要があり、無効にされたかどうかはis_revokedカラムで管理しています。

mysql> desc access_tokens;
+-----------------------+-------------+------+-----+-------------------+-----------------------------+
| Field                 | Type        | Null | Key | Default           | Extra                       |
+-----------------------+-------------+------+-----+-------------------+-----------------------------+
| id                    | int(11)     | NO   | PRI | NULL              | auto_increment              |
| client_id             | int(11)     | NO   | MUL | NULL              |                             |
| token                 | varchar(50) | NO   | UNI | NULL              |                             |
| user_id               | int(11)     | NO   | MUL | NULL              |                             |
| authorization_code_id | int(11)     | NO   | MUL | NULL              |                             |
| is_revoked            | tinyint(1)  | NO   |     | 0                 |                             |
| created_at            | datetime    | NO   |     | CURRENT_TIMESTAMP |                             |
| updated_at            | datetime    | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-----------------------+-------------+------+-----+-------------------+-----------------------------+

mysql> desc refresh_tokens;
+-----------------------+-------------+------+-----+-------------------+-----------------------------+
| Field                 | Type        | Null | Key | Default           | Extra                       |
+-----------------------+-------------+------+-----+-------------------+-----------------------------+
| id                    | int(11)     | NO   | PRI | NULL              | auto_increment              |
| client_id             | int(11)     | NO   | MUL | NULL              |                             |
| token                 | varchar(50) | NO   | UNI | NULL              |                             |
| user_id               | int(11)     | NO   | MUL | NULL              |                             |
| authorization_code_id | int(11)     | NO   | MUL | NULL              |                             |
| is_revoked            | tinyint(1)  | NO   |     | 0                 |                             |
| created_at            | datetime    | NO   |     | CURRENT_TIMESTAMP |                             |
| updated_at            | datetime    | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-----------------------+-------------+------+-----+-------------------+-----------------------------+

access_tokenrefresh_tokenの有効期限について

サンプルアプリでは、access_tokenの有効期限はcreated_atからプログラム的に計算しています。
今回の実装では、access_tokenの有効期限は発行されてから60分間にしています。refresh_tokenは、有効期限はなく、明示的にrevokeしない限り永久に使用可能にしています。
access_tokenの有効期限が過ぎたら、refresh_tokenを使って新たにaccess_tokenを発行します。

記事一覧

https://zenn.dev/akhr_s/articles/11331882be7b8d

https://zenn.dev/akhr_s/articles/bc75705d3438fc

https://zenn.dev/akhr_s/articles/78ca9d20907f6b

Discussion

ログインするとコメントできます