クライアント認証をしないOAuth2

OAuthの重要な考え方の中に、クライアント認証というものがある。(OAuthじゃなくてもあるけど)
OAuthはユーザ認証とクライアント認証を組み合わせて認可トークンを発行するプロトコルだという言い方をしても過言でないと思う。実際OAuth2.0の仕様(Draft16)を読んでも、クライアント認証が前提となっているように読める。しかし、一方でこのクライアント認証について説明をしているSection3には、以下の一説がある。

http://tools.ietf.org/html/draft-ietf-oauth-v2-16#section-3 

In addition, the authorization server MAY allow unauthenticated access token requests when the client identity does not matter (e.g. anonymous client) or when the client identity is established via other means. For readability purposes only, this specification is written under the assumption that the authorization server requires some form of client authentication. However, such language does not affect the authorization server's discretion in allowing unauthenticated client requests.

加えて、クライアントのIdentityがどうでもいいとき(たとえば匿名クライアントなど)や、クライアントのIdentityが他の方法で確立できるときは、認可サーバは未認証のアクセストークン要求を許可することもできる(MAY)。読みやすさだけを目的として、本仕様は、認可サーバが何等かのクライアント認証を必要とする仮定で記述している。しかし、そのような記述があるからといって未認証のクライアントリクエストを許可するというサーバ側判断の自由を妨げるものではない。

即ち、クライアント認証をしないOAuth2という形態があり得るということだし、さらに大事なことには、仕様書上 client_id: REQUIRED なんて書かれていても、クライアント認証しない実装をしたい人はキニスンナ(必須じゃなくてもいい)って言っているようにも読めるということだ。

OAuth2でのクライアント認証

そもそもクライアント認証についての記述はどうなっているか。Section3の冒頭から読んでみる。

Client credentials are used to identify and authenticate the client. The client credentials include a client identifier - a unique string issued to the client to identify itself to the authorization server. The client identifier is not a secret, it is exposed to the resource owner, and MUST NOT be used alone for client authentication. Client authentication is accomplished via additional means such as a matching client password.

クライアント資格情報(credentials)は、クライアントを識別特定(identify)し認証(authenticate)するために使われるものです。クライアント資格情報は、クライアント識別子(identifier)を含みます。クライアント識別子(identifier)は認可サーバに対してクラアントが自分が何者であるかを示すために発行される一意性をもった文字列です。クライアント識別子は秘密ではなく、リソース所有者に開示されるものであり、それ単体でクライアント認証に使ってはいけません(MUST NOT)。クライアント認証は、たとえば対応するクライアントパスワードなどの追加的手段をもってはじめて達成されます。

identification と authenticationは違う概念ですが、ちゃんとそこを使い分けて説明しています。

The methods through which the client obtains its client credentials are beyond the scope of this specification. However, the client registration process typically includes gathering relevant information which is used to educate the resource owner about the client when requesting authorization.

クライアントが自分のクライアント資格情報を取得するための方式は本仕様の範囲外です。しかし典型的なケースとしては、クライアント登録プロセスの中で、認可要求時にリソース所有者にクライアントについて教えるために使われるクライアント関連情報を集めることとなります。

なるほど、クライアント資格情報(id/pwみたいなの)をどう発行するかは、実装で決めてくれということか。
しかし確かに「クライアント登録プロセス」でクライアントの関連情報を集めつつ、そこで見返りにid/pwを発行するというのがふつうの実装になるのだろう。

Due to the nature of some clients, the authorization server should not make assumptions about the confidentiality of client credentials without establishing trust with the client. The authorization server SHOULD NOT issue client credentials to clients incapable of keeping their credentials confidential (typically determined during the client registration process).

一部のクライアントの特性を考えれば、認可サーバはクライアントとの信頼を確立することなしに、クライアント資格情報の機密性が保たれるという前提を置くべきではありません。認可サーバは、自分の資格情報の機密に保つ能力のないクライアントに対しては、クライアント資格情報を発行すべきではありません。(典型的な実装としては、これはクライアント登録プロセスの中で決定します)

  • まあ、クライアント資格情報を発行しても、クライアントがそれを秘密にしておけなかったら、なりすまされちゃうもんね。
  • これは「キャッシュカードに暗証番号をマジックで書いちゃうような人にはキャッシュカードを発行すべきじゃない」みたいな話で、実際のところはなんともならんこともあるだろうなぁ。(実際、銀行はそんなコントロールはしていないはず。注意喚起はしてるけど)

そして、最後に先ほどの「クライアント認証なしでもいいよ」という文章が入ってSection 3の導入部分はおしまい。

3.1 クライアントパスワード認証 (Client Password Authentication)

冒頭に思いっきり。

Pending Consensus

まだ合意形成できてないのね・・

で、基本はBasic認証でクライアントIDとクライアントパスワードを送り付けろと書いてある。
確かにHTTPSでID/PW認証するならBasic認証が一番シンプルだ。

3.2 その他クライアント認証方式 (Other Client Authentication Methods)

あんまりおもしろいことは書いていない。

Basic認証やDigest認証の置き換えとしてのOAuth2

さて本題に戻って、「クライアント認証しないOAuth2」ということを考えてみる。クライアント認証しない場合、ユーザ認証しかしないこととなる。認可判定基準が2要素ではなく1要素であるという意味で考えると、マクロに見ればBasic認証やDigest認証と同じわけで、本質を比較してみるのも面白いかもしれない。(二要素認証とは違うので、二要素認可とでも呼べばいいのだろうか・・)今までBasic認証でやってたところを素直にOAuth2に置き換えるケースを考えてみる。

Basic認証の置き換えとしてのOAuth2というと、「4.3. Resource Owner Password Credentials」にそのような記述がありこれを使う方式が有力だ。

The resource owner password credentials grant type is suitable in cases where the resource owner has a trust relationship with the client, such as its computer operating system or a highly privileged application. The authorization server should take special care when enabling the grant type, and only when other flows are not viable.

The grant type is suitable for clients capable of obtaining the resource owner credentials (username and password, typically using an interactive form). It is also used to migrate existing clients using direct authentication schemes such as HTTP Basic or Digest authentication to OAuth by converting the stored credentials with an access token.

一方で、一要素という意味では4.4のClient Credentialも捨てがたい。

4.3のリソース所有者のPassword Credential はフォームで受け付けるのに対して、4.4のClient Credentials はBasic認証で受け付ける仕様である。4.3については1要素判断となるケースはまれなケース(認可サーバが匿名クライアントを許可する場合など)に限られる。一方で4.4のClient Credentials のケースでは認可は1要素判断固定だ。その意味で、こうなるのは理解可能だ。

Basic認証を振り返る

Basic認証を使う場面を考えると、ふつうはID/PWに実際にエンドユーザのID/PWをいれる使い方をするものだが、開発者はクライアントプログラムを識別・認証するためのID/PWを入れるような「応用」も普通にみんなやってる。すなわちBasic認証はクライアント認証もユーザ認証にも使うことができていたということとなる。しかし確かにBasic認証ではクライアント認証とユーザ認証の2要素を両方同時にやりたいと思ったら、ちょっとつらかった。

HTTPがブラウザからのアクセスしか考えなくてよかった時代にはクライアント認証なんてやる必要はなかったから、そういう仕様でBasic認証は出来上がったのだろう。そして、それをプログラムからのアクセスに応用するときはadhoc的にプログラムをユーザと見立ててなんとかまわっていた。しかし、ブラウザ以外の多彩なクライアントからHTTPでWeb APIが叩かれる世界になると、Web API提供者は2つの認証情報すなわちユーザとクライアントの認証に応じて認可判断をしたくなり、一要素認証文化であるBasic/Digest/WSSEでは足らなくなっちゃった。OAuthの成り立ちってそういうところにある。

クライアント認証を必要としないOAuthって、そういう風に考えると何の意味があるのかよくわからないようにも見える。が、OAuthはクライアントとユーザの2つの認証をするためだけのものではなく、分散アーキテクチャで認証・認可サーバとAPIのサーバが分離しているときにどのようにサーバ感で認可状態を持ちまわるかという課題を解決している側面もあるので、決して無意味なものではない。

さて、気になるのは、そういう背景を再認識したうえで、Basic認証の置き換えをしようとしたときに、本当に仕様書Draft16でいうところの「4.3. リソース所有者パスワード資格情報」で置き換えるべきかということ。1要素か2要素かということが本質的な違いであれば、

「一要素認可のときは、やっぱりBasic認証でいいんじゃない?」

という素朴な考えが浮かぶ。ユーザ認証であろうと、Client認証だろうと、一要素で認証してトークンを発行するときはやっぱり
Basic認証でID/PW送ってね」であるほうが仕様がシンプルになるんじゃないのかなぁということ。

でもそういう仕様だとBasic認証で送られてきたIDがクライアントのIDなのかリソース所有者のIDなのか認可サーバとしては分からなくなっちゃうといけないので、このような仕様になっているという理解&納得は可能だ。


結論としては、Basic認証を置き換える場合、置き換える前のBasic認証のIDがクライアントのものであったら4.4の世界にマップすべきだし、そうでなくエンドユーザのIDであったら4.3の世界にマップするのかな。ただし、ここでいう4.3の世界にはClient IDが存在しないケースとなるので、ここの冒頭で書いたように、仕様書にはclient_idがREQUIREDって書いてあるけど、そこは実際は送らなくても認可サーバはエラーを返さない実装にするべきなのだと思う。すなわちREQUIREDって書いてあるからしょうがないからダミーのclient_idを送るとか、そういう必要はないのではないかということ。
(要確認)