2022/09/17

OAuth2をおおまかに理解する

はじめに

前回の記事で認証と認可について触れ、OpenID Connectのざっくりとした仕組みについて紹介しました。
今回は認証、認可においてよく聞くOAuth2について、調査してみようと思います。
今回の記事のゴールは、「OAuthという単語を聞いたことがある」くらいのレベルから「なんとなくOAuthについて説明することができる」レベルになることです。
具体的なフローについてはまた別の記事としたいと思います。

以下の動画を中心にその他参考に挙げているブログをもとにまとめたいと思います。
https://www.youtube.com/watch?v=PKPj_MmLq5E

OAuthとは

複数のサービスを連携して 動作させるために使われる仕組みです。

OAuthを使用しない場合、WebサービスごとにユーザーIDとパスワードを入力してユーザーを認証する必要があります。
OAuthを利用することで、IDやパスワードを入力することなく、アプリケーション間の連動ができます。
例えば、オンラインアルバムサービスに写真を投稿すると、同時にツイッターで投稿されたことをつぶやく事ができます。

OpenID Connectとの違い

よく併せて登場するOAuthとOpenID Connectは何が違うのでしょうか。
OpenID ConnectはGoogleのようなある一つのサービスで利用している認証用のユーザーIDとパスワードを複数のWebサービスで利用するというものでした。 認証の役割を一つのサービスに任せるような形です。 Webサービスの増加に伴って、複数のWebサービスを連動させることでWebサービスごとに別々にユーザー登録をする煩わしさを防ぐための仕組みです。
OpenID Connectは認証の仕組みなので、その人物が誰であるかという認証を行うことはできますが、どのような操作ができるかという認可ではありません。 つまり、ログインすることはできても、操作はあくまでもユーザーが別々に行う必要があります。

OAuthの場合、認証を与えたサービスの保持しているリソースを、認証が与えられたサービスが利用することができます。 どのような利用が可能かについても、OAuthの認証のなかで明確に記述されています。

OAuthの流れ

登場人物

  • クライアントアプリ
    データを利用したいアプリ
  • リソースサーバ
    データを提供するサーバ
  • 認可サーバ

リソースサーバはAPIを介してクライアントアプリにデータを提供します。
悪意あるクライアントアプリがAPIにアクセスしてきた場合、データを利用されてしまいます。
そのため、何らかの方法でAPIを保護する必要があります。
このAPIを保護する仕組みのベストプラクティスが、「アクセストークンをクライアントアプリに持たせる」ことです。
クライアントアプリは、アクセストークンをリクエストに含んだ状態でリソースサーバーにアクセスし、リソースサーバーはアクセストークンをチェックしてAPIにアクセスしていいアプリかどうかを判定します。

上記仕組みを実現させるためにはクライアントアプリにアクセストークンを渡す役割が必要になります。
それが認可サーバーと呼ばれるものです。

アクセストークンを発行する際にユーザーに許可を取りに行きます。
クライアントアプリがアクセストークンの発行を認可サーバーに依頼します。
そこで認可サーバーはユーザーに対して「クライアントアプリに権限を与えるかどうか」確認します。
ユーザー側が許可すれば認可サーバーはアクセストークンを生成し、クライアントアプリに発行します。

上記太字箇所の流れを標準化したものがOAuth2.0です。

認可サーバーはユーザーに対してクライアントアプリに権限を与えるかどうかを確認する際に認可画面(どのアプリに、なんの画面を与えるかという情報と、ログイン情報の入力画面)を表示します。
ここで実行されるログイン処理が認証です。

何に対してなんの権限を誰が与えるのか、この3つの要素を含んだものが認可です。

RFC6749について

以下の4つの認可フロー(アクセストークンの発行手順)が定義されています。

  • 認可コード
    一時的に発行される認可コードとアクセストークンの交換フロー

  • インプリシット
    認可エンドポイントからアクセストークンが直接発行されるフロー

  • リソースオーナー・パスワード・クレデンシャルズ
    ユーザーのIDとパスワードをクライアントアプリに渡すフロー

  • クライアント・クレデンシャルズ
    ユーザー認証なし。クライアントアプリの認証のみが必要なフロー

認可コード

  1. ユーザーがサービスと連携したいとクライアントアプリに対して要求する
  2. クライアントアプリは認可サーバーの認可エンドポイントに認可リクエストを送信する
  3. 認可サーバーは認可画面をユーザーに表示します。
  4. ユーザーはその画面を見て、何に対して,なんの権限を与えるのかを確認した上で認証情報を入力します。
  5. 認可サーバーは認可コード(一時的なコード)をクライアントアプリに対して発行します。
  6. クライアントアプリは認可コードを持ってトークンエンドポイントにアクセスし、認可コードとアクセストークンを交換します。
  7. アクセストークンを持ってリソースサーバーのWebAPIを利用することが可能になる

OAuth2.0の仕様書で定義されているのは上記のフローのうち、2,5,6のみです。それ以外に関しては言及がないので自由に作成できます。 つまり、上記フローで行われる認証にOpenID Connectを使用しているという形になります。

OpenIDConnectはOAuth2.0は流れがかなり似ています。
これはOpenID ConnectがOAuth2.0の上に作られたアイデンティティレイヤのため、わざと似せて作られているようです。
これによってサーバーはOpenID プロバイダと認可サーバーの役割を併せて持つ事ができます。
OpenID Connectで必要なIDトークンとOAuth2.0で必要なアクセストークンを同時に生成して発行することができます。

OAuth フロー

認可サーバーは以下の2つのエンドポイントを提供する必要があります。

  • 認可エンドポイント
  • トークンエンドポイント

RFC6749でエンドポイントの動作を定義しています。(OAuth2.0) OAuthのフローの種類によってこのエンドポイントを使用するかどうかが決まっている。

  • 認可コード
    認可エンドポイントとトークンエンドポイントの両方を使用する
  • インプリシット
    認可エンドポイントのみ使用する
  • リソースオーナーパスワードクレデンシャルズ
    トークンエンドポイントのみ使用する
  • クライアントクレデンシャルズ
    トークンエンドポイントのみ使用する
  • リフレッシュトークン
    トークンエンドポイントのみ使用する

使用されるデータ形式について 

OpenID Connectについてまとめた際にも出てきましたが、OAuthでも使用される形式なので改めてまとめたいと思います。

仕様書略称名称
RFC7515JWSJSON Web Signature
RFC7516JWEJSON Web Encryption
RFC7517JWKJSON Web Key
RFC7518JWAJSON Web Algorithms
RFC7519JWTJSON Web Token

JWS

ヘッダー.ペイロード.署名

の形式。それぞれの部分はBASE64URL形式でエンコードされています。

ヘッダーについて

{"kid": "1e9gdk7", "alg":"RS256"}

algの部分は使用しているアルゴリズムの種類を示していて、これはRFC7518、先程の表にも出てきたJWAにて定義されています。

ペイロードについて

任意のバイト列が入ります。 ペイロードにはバイナリが入ればよくて、JWSの仕様書ではペイロードにJSONがはいることを特に策定しているわけでは無いです。 ただ、JSONが入るように決めているのはRFC7519のJWTの仕様書で定められています。

ここまでのまとめ

JWS...署名
JWT...トークン

JWSを発展させたのがJWTで、更にそこから発展したのがIDトークンという関係性。

ただ、JWSだけでなくJWEという形式から発展したJWTもあります。

JWE

ヘッダー.暗号化されたキー.初期ベクター.暗号文.認証タグ

の形式。JWS同様にBASE64URL形式でエンコードされている。

  • 暗号化されたキーについて
    なんで暗号キーではなくて暗号化されたキーなのか?についてですが、JWEは「共有鍵を非対称鍵で暗号化」しています。
    つまり、キー自体を暗号化するという二段階の暗号化が行われていることになります。

この二段階暗号化は暗号化においては一般的によく使われる手法らしいです。

二段階暗号化の流れ

暗号化する側は公開鍵
復号化する側は秘密鍵
を持ちます。

暗号化する側は平文を共有鍵を用いて暗号化します。
共有鍵は暗号化する側でランダムに生成します。
公開鍵を使用して共有鍵を暗号化します。

暗号化する側は暗号化された共有鍵と暗号文をセットで復号化する側に渡します。

復号化する側は受け取った暗号化された共有鍵を秘密鍵を用いて復号化します。
そして共有鍵を用いて暗号文を復号化します。

二段階で暗号化することのメリット

  • 公開鍵の処理速度の問題

公開鍵はいくらでも公開してよく、鍵の共有が楽だというメリットがあります。
ただ、公開鍵暗号化の処理は時間がかかります。
特に暗号化された文書が大きいものの場合は時間がネックとなります。

  • 鍵の配布問題

共有鍵形式の場合は暗号処理自体は高速ですが、鍵の共有方法が難しくなるデメリットがあります。

二段階暗号化は、文書自体の暗号処理には共有鍵形式を使い、鍵の公開には公開鍵形式を用いることで双方の形式のいいとこどりができます。

二段階暗号化の際のJWE

ヘッダー

{"alg":"RSA-OAEP", "enc":"A256GCM"}

のような形式。

algが共有鍵の暗号アルゴリズム
encが平文の暗号アルゴリズム

を示しています。アルゴリズムの形式についてはJWAの仕様書に書かれているものです。

JWT

JSON形式で表現されたクレームの集合をJWSもしくはJWEに埋め込んだもの
先程出たように、JWS形式のJWTJWE形式のJWTがあります。

JWS形式のJWT

JWSの形式

ヘッダー.ペイロード.署名

JSON形式で表現されたクレームの集合をBASE64URLエンコードして、JWSのペイロード部分に格納したもの。
署名をしたい場合はこちらを使用します。

JWE形式のJWT

JWEの形式

ヘッダー.暗号化されたキー.初期ベクター.暗号文.認証タグ

JSON形式で表現されたクレームの集合を暗号化して、暗号文をBASE64URLエンコードする。
それを暗号文部分に格納したもの。
暗号化したい場合はこちらを使用します。

署名と暗号化をしたい場合

署名と暗号化を両方行いたいケースも考えられます。
その際には、

  • JWSをJWEでくるむ
  • JWEをJWSでくるむ

のどちらかのパターンを採用することになります。 この形式のJWTNested JWTと呼びます。

OpenID Connectで使用するIDトークンには署名が必須と仕様書で定められています。 暗号化は任意だが、暗号化する際には「署名後に暗号化」という流れにしないといけないという仕様となっています。

JWTクレームとは

{
    "クレーム名": クレーム値,
    "クレーム名": クレーム値
}

詳細はRFC7519に記載があります。

所感

今回は前回の復習 + OAuth2.0について大まかな流れを理解できたかと思います。
暗号化の仕組みや署名の仕組みについて、情報処理技術者試験の対策として勉強した事はあったので全く知らない内容ではなかったのですが、具体的なフローについては初めて聞く単語も多く、理解するのに結構苦労しました。
仕組み的にもかなり複雑なところなので少しずつ理解を進めていきたいです。
また別の記事でOAuth2.0の4つのフローについての具体的な流れや、リクエストやレスポンスの中身についてもまとめたいと思います。

参考

https://qiita.com/TakahikoKawasaki/items/200951e5b5929f840a1f

https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be

https://www.youtube.com/watch?v=j0pIlZdD7-A

https://www.youtube.com/watch?v=PKPj_MmLq5E

https://www.tdk.com/ja/tech-mag/knowledge/147