Oauthとは?
現場でOAuth2.0を使用することになったので学習がてらまとめてみました。
OAuthとはめっちゃ簡単にいうとユーザーIDやパスワードを教えることなく、リソースを使用するための仕組みです。
OAuthの登場人物は4人(コードグラントによる)
登場部人物は基本的には4つあります。これをそれぞれロールと呼んだりもします。
- リソースオーナー
- 認可サーバー
- クライアント
- リソースサーバー
ちょっとややこしいですが、クライアントとはユーザーのことではなく、何かしらのリソース、例えば、写真やプロフィール情報などを利用したいアプリケーションのことです。
ちなみにユーザーはリソースオーナーです。
ざっくりしたOAuthの流れ
簡単に流れを説明すると、
あるウェブサイトを使っていて、そこでGooglePhotoに保存している写真を使いたいなーとなった場合を想定してみましょう。
ユーザーであるリソースオーナーは、クライアントであるウェブアプリケーションに対して、リソースを使いたい!と伝えます。
すると、認可サーバーというアクセスの許可を与えるためのサーバー(認可エンドポイント)が「君のリソースを使いたがってるんだけど、使わせてもいいのか?」と確認してきます。
そこで、リソースオーナーが「使ってもいいよ」と許可すると、
認可サーバーはクライアントに認可コードという、「リソースを使ってもいいことも認められた証明書」を発行するわけです。
で、その認可コードを使って、再度認可サーバーの中の別の場所(トークンエンドポイント)に
「自分はリソースを使ってもいいと許可もらってるんで、情報を取得させてください」と伝えます。
そこで、認可サーバーがそれを確認できたら、リソースにアクセスできるようなトークンを発行し、返してあげます。
クライアントはこのトークンをリソースサーバーに伝えると無事欲しい情報を取得できるようになるわけです。
補足説明
②
以下のようなクエリパラメータを付与している
GET /authorize
?response_type=code
&state=xyz
&scope=read
&redirect_url=https%3A%2F~/callback
HTTP/1.1
Hots: hoge.example.com
⑧
Locationヘッダーには3番で指定したリダイレクトURIが指定される。
このURIにはパラメータとして、認可コードとstateがセットされる。
認可コードの有効期限は基本仕様で10分以内。
stateは基本仕様では不要だが、クロスサイトリクエストフォージェリを防ぐために推奨
stateは1番で生成されたランダム文字列と9番でのセッションの値が同じか確認するために必要
⑩
POST /token HTTP/1.1
Host: hoge.example.com
Authorization: Basic gad32fgaE2RTga43tgsDH3w
ContentType: application/x-www-forkm-urlencoded
grant_type=authorization_code
&code=SplcBeWEg35GasgasA
&redirect_uri=https%3A3gagasdgG~2Fcallback
ここでのAuthorizationはclient_idとclient_secretを:でつないで、 Base64エンコードしたもの。
しかし、client_idとclient_secretをボディに含む形も認められている。その場合、Basic認証のためのAuthorizationヘッダーは必要ない。
-
grant_tyoe
値にauthorization_codeを指定 -
code
9番で取得した認可コードの値を設定 -
redirect_uri
3番のリクエストで指定したredirect_uriを設定 -
client_id
クライアントの登録時に発行されたクライアントIDの値を入れる -
client_secret
クライアントの登録時に発行されたクライアントsecretの値を入れる
11
HTTP/1.1 200 OK
ContentType: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token" : "ASt3tgdL",
"token_type" : "Bearer",
"expires_in": 3600,
"refresh_token" : "as2rweaDFgaehAEH"
}
-
access_token
そのまま、アクセストークンが返ってくる -
token_type
さっきはBearerで指定したので、それを示している -
expires_in
アクセストークンの有効期限が秒単位で入っている -
refresh_token
基本仕様では任意なので返ってくるかは分からない。
access_tokenの再発行に必要
コードグラント
今回説明したフローは認可コードグラントというものです。
実はこれ以外にも色々とコードグラント(要はフローのパターン)があります。
-
インプリシットグラント
インプリシットグラントは推奨されていないコードグラントです。 -
クライアントクレデンシャルグラント
-
リソースオーナーパスワードクレデンシャルグラント
認可コードグラント + PKCE
認可コードグラントの場合、悪質なアプリケーションによって認可コードが横取りされてしまう危険性がある。
それを防ぐ仕組みがPKCE(ピクシー)である。
横取りは以下のような条件で起こりうる
- リソースオーナーが所持するデバイスにネイティブアプリと悪質なアプリがインストールされている
- 悪質なアプリはネイティブアプリと同じカスタムスキームが設定されている
- 悪質なアプリはネイティブアプリのクライアントIDを知っている
PKCEの登場人物
基本的には認可コードグラントのプロセスなのですが、いくつか新しい登場人物がいます。それは以下の3つです。
- code_verifier
長さが43文字、最大128文字までの間の[A-Z]/[a-z]/[0-9]/"-"/"."/"_"/"~"からなるランダムな文字列 - code_challenge
code_verifierに対して次のcode_challenge_methodの計算をほどこして算出された値 - code_challenge_method
code_challenge_methodの値はplainまたはS256
※2つあるが、基本的にはS256を使用する。
値 | 計算方法 |
---|---|
plain | code_challenge = code_verifier |
s256 | code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier))) |
で、何が違うかというとこれらを認可コードグラントのプロセスに追加する点です。
まず、code_challengeとcode_challenge_methodを認可リクエストに追加します。
GET /authorize
?response_type=code
&state=xyz
&scope=read
&redirect_url=https%3A%2F~/callback
&code_challenge=Egasgd~省略
&code_challenge_method=S256
HTTP/1.1
Hots: hoge.example.com
で、先にこの2つを渡しておいて、トークンリクエストの時にcode_challenge_methodで変換する前の生データであるcode_verifierをトークンエンドポイントに渡します。
POST /token HTTP/1.1
Host: hoge.example.com
Authorization: Basic gad32fgaE2RTga43tgsDH3w
ContentType: application/x-www-forkm-urlencoded
grant_type=authorization_code
&code=SplcBeWEg35GasgasA
&redirect_uri=https%3A3gagasdgG~2Fcallback
&code_verifier=asgARher~省略
そして、トークエンドポイントではこれらを使って検証をし、成功すればトークンを返します。
こうすることで、もし悪意のあるアプリケーションが認可コードを奪ったとしても、code_verifierの検証に失敗するのでトークンを取得できないということですね。