ちょっと話題の記事

Developers.IO 2017セッション「基礎からの OAuth 2.0」でお話してきました #cmdevio2017

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

よく訓練されたアップル信者、都元です。クラスメソッドが運営するIT系技術ブログDevelopers.IOのカンファレンスイベントDevelopers.IO 2017にて、セッション「基礎からの OAuth 2.0」を発表しました。本エントリーはそのレポートです。

発表スライド

発表動画

セッション概要

システム開発をする以上、ほとんどの場合「認証と認可」は切っても切れない問題です。マイクロサービスが話題を集め、コンポーネントのWeb API化が急加速を見せる昨今。OAuth 2.0 という仕組みが継続的に注目を集めています。

しかし、いざその仕様を紐解いてみると Authorization code や Implicit 等、簡単には理解できない概念や選択肢が並んでおり、 自分が導入すべきなのはどのような仕組みなのか、判断が難しいのも確かです。

本セッションでは OAuth 2.0 の仕組みを基礎から解説し、今あなたに必要な認証と認可の仕組みを判断できるような知識をお伝えします。

セッション詳細

OAuth したい動機

と、いう感じで始まりましたが、まず「我々は、なぜ OAuth したいのでしょうか?」と問わせて頂きました。 動機としては大きく3つに分かれるのではないかと思っています。

  1. 認証:Twitter等の外部アカウントで、自分たちのアプリにログインさせたい。
  2. 属性取得:Twitter等が持っているユーザーの属性情報(名前やメアド等)が欲しい。
  3. 委譲:ユーザーに成り代わってツイートをPOSTしたい、DMが送りたい、読みたい!?

そして動機の大半は 認証 です、そうに決まってる[要出典]

であるとすれば、あなたが欲しいものは、実は OAuth ではないかもしれない。 OAuth は認証には一切関係がないプロトコルだからです…。

認証と認可の基礎知識

このあたりについては、以前本ブログでよくわかる認証と認可というエントリーを書きました。こちらを改めて読んでおいて頂ければと思います。

認証の委譲 (OpenID Connect)

ここで認証の委譲プロトコルである OpenID Connect についてざっとご紹介。登場人物は3者です。

  • End User:認証の対象になる。おおかた人間。
  • Relying Party:End Userの認証結果を知りたいが、面倒なので下請けに丸投げしたいシステム。
  • ID Provider:End Userの認証を肩代わりしてくれるシステム。

screenshot 2017-07-03 16.56.50

  1. まず End User が Relying Party (RP) に何らかのリクエストを送ります。
  2. すると RP の中で「この人は誰なんだ…? 知りたい…」という欲求が沸き上がります。
  3. RP は ID Provider に対して認証の肩代わりを依頼します。
  4. End User は ID Provider に対して ID / psss を提示する等します。
  5. 認証が成功したら、ID Provider は「ID token」というデータ(証明書)を RP に返します。

OpenID Connect における ID token では、JWT という技術を使っています。 一言で言えば、{"sub": "miyamoto", ... } というJSON(この人は miyamoto さんです、という意味) に ID Provider の秘密鍵で署名したものです。

ID token には電子署名がついているため、内容の偽造は出来ません。 RP は、ID Provider の公開鍵で署名を検証できれば、RP 上での認証の裏付けにも充分です。

JWTついては、以前本ブログでJWTによるJSONに対する電子署名と、そのユースケースというエントリーを書きました。こちらを改めて読んでおいて頂ければと思います。

APIとか、そのアクセス制御の話が出てこなかったと思います。 しかし、ここまでのしくみで、皆さんがやりたいことは意外と達成できたりしませんか?

神は誰か?問題

よくある Web+DB システムでは、アプリケーションが DB 及びその上のデータに対する全権を握り、 ユーザーの権限に応じてアプリケーションがアクセス制御をします。

screenshot 2017-07-03 16.57.48

データソースが DB ではなく API だったとしても同じようなスタイルがあります。

screenshot 2017-07-03 16.58.20

しかし、OAuth の世界では、神はアプリケーションではなく、ユーザーです。 アプリケーションが神託を受けて、APIに対するアクセス権を分け与えてもらう形です。

screenshot 2017-07-03 16.58.44

皆さんが OAuth を使いたくないであろう理由

つまりこれが、使いたくないであろう理由の1つ目になります。 従来、我々アプリケーション開発者は、データに対して神のポジションを取ってきました。 OAuth の世界では、この常識が通用しません。不安ではないですか? この不安を乗り越えてまで、 OAuth を使いたいでしょうか?

ただまぁ、自分たちが神であることに慣れすぎている、という問題提起もしたいと思っています。 本当は持つべきではない強大な権限を保持しておくモデルよりも、必要な時に必要なだけの 権限を得られるモデルの方が、実は健全なのかもしれません。

あともう一つ。OAuth というのは、APIリソースに対する権限を、アプリケーションに委譲するしくみです。 であるとすれば、そもそもアプリケーションとAPiリソースの主権者が異なっていなければ、 OAuth というのは大きな意味を持ちません。(無意味とは言いませんが…)

認可の委譲 (OAuth 2.0)

というわけで、ここからが本題。

Twitter等の外部システムが管理するリソースに、ユーザーのお許しを得た上でアクセスしたい ということであれば、これは OAuth の出番かと思います。

ここで認可の委譲プロトコルである OAuth 2.0 についてざっとご紹介。登場人物は4者です。

  • Resource Owner:リソースに対する全権を持つ神。おおかた人間。
  • Client:ROから権限を授かるシステム。
  • Authorization server (AS):RO と Client の間で、権限委譲の仲介をするシステム。
  • Resource server (RS):RO が所有するリソースを管理するシステム。

screenshot 2017-07-03 16.59.10

  1. まず Resource Owner (RO) が Client に何らかのリクエストを送ります。
  2. すると Client の中で「あぁ…、リソースにアクセスしたぁい…」という欲求が沸き上がります。
  3. Client は Authorization server (AS) に対して権限委譲手続きを依頼します。
  4. RO は AS に対して、委譲に対する同意の意思表示をします。
  5. AS は「access token」というデータ(鍵)を Client に返します。
  6. Client は、鍵を添えて Resource server (RS) にアクセスし、リソースの提供を受けます。

OpenID Connect における ID token(証明書)と違い、OAuth 2.0 にける access token は、 ただの「鍵」です。鍵はただの鍵なので、これに対して下記のように「誰?」を求めてはいけません。

  • 誰から鍵をもらったのか?(Resource owner は誰か?)
  • 誰が鍵を行使したのか?(Client は誰か?)

鍵の責務は「正しければ錠が開いて、正しくなければ錠が開かない」ことであって、 発行者・所有者・占有者・利用者など、その鍵にまつわる人々を提示・証明する機能は無いのです。

OAuth 2.0 のフロー4種

OAuth 2.0 において Client がアクセストークン(以下、AT)を得る手順は下記の通り4種類あります。

  1. Client credentials grant
  2. Resource owner password credentials grant
  3. Implicit grant
  4. Authorization code grant

上のフローほどシンプルですが、様々な要件を割り切っています。 下のフローほど複雑ですが、OAuth が考える理想的な要件を満たしています。

どれを使えばいいのかよくわからない、という声をよく聞きますが、大方下記の通りです。

1. Client credentials grant

このフローがやっていることは、結局「Client ID + Client secret」をアクセストークンに引き換えているだけです。無期限クレデンシャル (Client secret) が有期限クレデンシャル (access token) に変わるので、 一定のセキュリティ向上が見込めます。

しかしそもそも OAuth の本懐である「RO からの権限委譲」という観点を一切度外視したフローです。

ユースケースとしては、従来型の「アプリケーション(=Client)神パターン」か、もしくは 「ユーザーからの権限委譲が必要ない、Client単独でも実行し得る小さな操作」を実現するために 使うことになると思います。

2. Resource owner password credentials grant

このフローがやっていることは、結局「ROのユーザー名とパスワード」をアクセストークンに引き換えているだけです。ユーザーからの意思(パスワード)がClientに委譲される、というポイントは満たしています。

しかし、OAuthに は「ClientにROのパスワードを知られることなく、委譲したい」という理想があります。

これを満たしていないため、ユースケースとしては、公式クライアント等、信頼のおけるクライアントに限られます。

3. Implicit grant

Implicit grant は、ROからの承諾の下に、Clientに対してアクセストークンを渡す、という OAuth の理想に沿ったフローです。

ただしアクセストークンは、コールバック(Clientへの戻りのリダイレクト)時、そのURLのフラグメント部分に入ってきます。

これはどういうことかというと、サーバーサイドにはアクセストークンが伝わらない(URLのフラグメント部は HTTPリクエストとしてサーバーに送信しない)ということになります。

Implicit grant は、モバイルアプリやJSアプリケーション等、エンドユーザーの支配下にあるノードの上で 動くアプリケーション向けのフローです。

この意図に反して、JSでフラグメントのアクセストークンを読み取った上で、それをサーバーサイドに 転送する、という行為も可能ですが、お勧めしません。その場合は下記の Authorization code grant を 利用すべきです。

4. Authorization code grant

このフローには、OAuth の理想が詰まっています。フローは Implicit とほとんど変わらないのですが、 1点だけ、コールバック時のリダイレクトではアクセストークンではなく Authorization code という値を 返します。このコードを、アクセストークンに引き換える、というワンクッションが挟まっています。

OAuth 2.0 で行うHTTP通信は、大きくフロントチャネル(ユーザーのブラウザを介した通信)と バックチャネル(ユーザーが介在しない、サーバーサイド同士の通信)があります。

Authorization code というワンクッションを挟むことによって、アクセストークンが フロントチャネルに流れなくなるのです。

pasted-image-11770

まとめ

認証と認可の概念

  • 認証とは、相手の身元を確認すること。
  • 認可とは、権限を与えること。

401「身元を明らかにせよ」 / 403「分かった。だが断る。」

アクセストークンの意味

アクセストークンは単なる鍵。鍵に「付与した人」や「行使した人」を求めてはいけない。

認可コードの意味

Authorization code flow における Authorization code は、 アクセストークンが流れる経路をバックチャネルに寄せるためのしくみ。