読者です 読者をやめる 読者になる 読者になる

ボクココ

サービス開発を成功させるまでの歩み

認証を含む API 開発で検討すべきこと

ども、@kimihomです。

API に関する基礎的な話で、なぜ API が重要なのか、APIの実装で注意する点について記述した。

今回はAPI開発において最も頭を悩ます、認証の問題について考えてみたい。

API における認証

よくあるログインが必要なページを考えてみていただきたい。

通常のWebアプリケーションであれば、Cookieという仕組みを使って毎回Webサーバーにアクセスするときにsession idというものを送信し、それとユーザー情報を紐付けたデータを取ってくることで、どんなユーザーからリクエストが来たのかをWebアプリケーション側で判断することができる。これにより、私たちはいつも閲覧しているWebアプリケーションが自分専用の画面として見れるようになっている。

これがAPIになると話は違ってくる。Cookieという仕組みが使えないのである。ということで、なんとかしてAPIにアクセスしたリクエストが、"自分であること"をサーバーに伝えないといけない。これがなかなか頭を悩ます問題なのである。

パスワード認証

一番簡単な方法が、通常のログイン画面をクライアント側で用意し、ログインに成功したら専用のトークンを発行する方法である。ここでいう"クライアント"とは、APIで連携する外部サービスや、自社で開発するスマホアプリなどを考えていただきたい。ログインに成功したらユーザーごとにトークンを発行して、それ以降はトークンがユーザーを示すidの代わりになるものとして利用する。以下のイメージだ。

f:id:cevid_cpp:20151220184251p:plain

パスワード認証が許される唯一のケースは、自社のスマホアプリなど、APIを利用するクライアントの提供元が自社であることが確実に証明できる場合 である。

理由は以下の2つの欠点があるためだ。

なりすまし問題

1つ目に、トークンが一度誰かに知られるとなりすましが簡単に行えるという点。このトークンはそのユーザーが自分であることを示す唯一の方法であり、これが誰かに知られると簡単になりすましが可能となる。この問題に対応するためにもし問題が起きた時にすぐトークンを再発行できるような仕組みにしたり、2週間で強制的にトークンの有効期限を切らせて再度ログインさせる、などの処置が行われているサービスがある。どちらの対応も不完全であることは言うまでもない。

email, passwordの入力が必要

2つ目に、クライアント側でemailやpasswordなどそのサービスの個人情報を入力しなければならない点にある。外部の組織が作ったアプリで、自社のサービスのemailやpasswordを入力させてしまうと、いとも簡単に個人情報を盗むことができる。これこそがパスワード認証における最大の問題である。

ということで次の方法である、事前にトークンを発行するやり方がある。

トークン認証

トークン認証はあらかじめ自社サービス内でトークンを発行してしまい、そのトークンだけを外部サービス連携時に使う、という方法である。割と有名なAPIでもよく使われている方法である。以下のイメージ。

f:id:cevid_cpp:20151220190421p:plain

この方法ならまず前述の問題は解決できるが、ユーザーはログインの代わりにその長いトークンを別で入力する手間ができる。ということで、トークンをクライアント側で入力させるこの方法は、API利用先は一つのユーザーのクライアントのみ という場合に効果的である。例えば外部システムが自社サービスの特定ユーザーのみの情報を扱いたいだけ、という場合に利用できる。

認証コードの発行

さて、パスワード認証、トークン認証それぞれで解決できなかった、多数の自社ユーザーが使えるアプリを、外部が開発できるようにするためのAPI を作るにはどうしたらよいのか。ここで認証コードの発行が登場する。ログインを自社の画面でやらせて、ログインに成功したら認証コードを発行するのである。以下のイメージ。

f:id:cevid_cpp:20151220191930p:plain

いきなり複雑になってしまったが、よくあるTwitterの連携画面を思い浮かべていただきたい。Twitterと連携するアプリを作る時、連携元サービス(クライアント)の"Twitterログイン"ボタンを押すと、ログインはTwitter内で行われ、許可を押すと連携元サービスに飛ばされる。

f:id:cevid_cpp:20151220192151j:plain

この方法により、emailやpasswordなどの個人情報をクライアント側で所持する必要がなくなるため、多数の自社ユーザーが使えるアプリを、外部が開発できるようにするためのAPI を実現することが可能になる。そうすると外部の開発者が、Twitterアプリや、Facebookアプリなどのような"アプリ"が作れるようになる。

さて、最後に残るのはこのトークン問題だ。トークンがいつまでも同じだと取られたら終わり、という問題に対応する方法がこの記事の最後になる。

アクセストークン、リフレッシュトークンの発行

今まで呼んでいた"トークン"は、基本的に変わらないトークンである想定だった。これを柔軟に定期的に変えられるようにすればセキュリティの問題もクリアできる。

アクセストークンは、有効期限の短いユーザーを識別するためのトークンである。リフレッシュトークンはアクセストークンよりも有効期限が長く、アクセストークンを更新するために使うトークンである。

一般的に先ほどの"認証コードを発行"のあるAPIであれば、認証コードを発行したら"トークン"として アクセストークン・リフレッシュトークンを返却するAPIが多い。これが OAuth2 と呼ばれている仕組みであるためだ。OAuthのより詳細は別の書籍などを参照していただきたい。

以下のイメージである。

f:id:cevid_cpp:20151220193256p:plain

アクセストークンとリフレッシュトークンを取得できたら、基本的にはアクセストークンでAPIとやりとりする。アクセストークンはできる限り短い有効期限で、しばらくすると期限切れになる。そしたらリフレッシュトークンを送って再度アクセストークンを再取得し、そのアクセストークンでまたAPIとやりとりするようにする。これによりトークンにおける問題を解決することができた。

終わりに

APIの認証は実装上最大のハードルと言ってもいい。これさえ上手く構築できればAPIの大部分の問題をクリアできたと言える。認証の方法は、想定するAPIの使われ方次第で実装が大きく変わる。上記のメリット・デメリットを理解した上で最適なAPIを設計していただきたい。

さて、これらの仕組みを全て作らないといけないのだろうか・・? もしあなたが Ruby on Rails を使っていれば、心強いサードパーティGemがあるから安心していただきたい。ひとまず次はその他のAPIの課題について考え、その後にRailsにおける認証を考えていきたい。