はじめに
こんにちは、マネーフォワード ID 開発チームのMapduと申します。 この記事はMoney Forward Engineering 2 Advent Calendar 2022 の1日目の投稿になります。
マネーフォワード IDはマネーフォワード社、およびグループ会社の各サービスのID基盤、認証基盤となるWebアプリケーションです。
今日は、サードパーティーアプリケーションによるHTTPサービスへの限定的なアクセスを可能にする認可フレームワークである OAuth 2.0 について紹介します。
※ この記事では、経験に基づいて要約しています。詳しくはこちら「RFC 6749」をご覧ください。
※ The OAuth 2.0 Authorization Framework (English / Japanese)
記事の構成
- OAuth 2.0 Roles
- Protocol Flow
- Authorization Grant
- 最後に
1. OAuth 2.0 Roles
※ 「RFC 6749 Session 1.1. Roles」
OAuth では、以下の4つのロールを定義します。
Resource Owner (リソースオーナー): 保護されたリソースへのアクセスを許可するエンティティーです。リソースオーナーが人間の場合、エンドユーザーと呼ばれます。ユーザーのアカウントへのアプリケーションのアクセスは、付与された承認の「scope」に制限されます。
Resource Server (リソースサーバー): 保護されたリソースをホストし、アクセストークンを用いて保護されたリソースへのリクエストを受理してレスポンスを返すことのできるサーバー。
Client (クライアント): リソースオーナーの認可を得て、リソースオーナーの代理として保護されたリソースに対するリクエストを行うアプリケーションです。 その前に、ユーザーによって承認される必要があり、承認は API によって検証される必要があります。
Authorization Server (認可サーバー): リソースオーナーの認証とリソースオーナーからの認可取得が成功した後、アクセストークンをクライアントに発行するサーバー。
OAuth 2.0 の優れた設計上の決定の 1 つは、承認サーバーの役割を API サーバーから明示的に分離することでした。 このため、認可サーバーとしてスタンドアロン コンポーネントがあり、このコンポーネントはユーザーから認可を取得し、クライアントにトークンを発行します。
認可サーバーと API サーバーを物理的に別のサーバーに配置することも、異なるドメイン名に配置することもできます。 これにより、システムの各部分を個別にスケーリングできます。
例:マネーフォワード ID「MFID」は認可サーバー とクライアントの両方です。
Google を使用してMFIDでマネーフォワード MEにログインした場合、マネーフォワード MEから見ると、MFIDは認可サーバーです。しかし、Googleから見ると、MFIDはクライアントです。
Figure 1: Money Forward ID is both Authorization server and Client
2. Protocol Flow
※ 「RFC 6749 Session 1.2. Protocol Flow」
Figure 2: Abstract Protocol Flow
上の図で示されたOAuth 2.0のフロー概要は、4つのロール間での相互作用と以下のステップについて記載しています。
(A):クライアントはリソースオーナーに対して認可を要求する。その際 (図のように) リソースオーナーに直接認可要求を行うことも出来るが、認可サーバーを経由して間接的に行うことがのぞましいです。
(B):ユーザーは要求を受け入れるか拒否することができます。 ユーザーがリクエストを承認した場合、アプリケーションは承認付与を受け取り、それ以外の場合はエラーを受け取ります。
(C):クライアントは認可サーバーに対して自身を認証し、認可グラントを提示することで、アクセストークンを要求します。
(D):認可サーバーはクライアントを認証し、認可グラントの正当性を確認する。そして認可グラントが正当であれば、アクセストークンを発行します。
(E):クライアントはリソースサーバーの保護されたリソースへリクエストを行い、発行されたアクセストークンにより認証を行います。
(F):リソースサーバーはアクセストークンの正当性を確認し、正当であればリクエストを受け入れます。
3. Authorization Grant (認可グラント)
※ 「RFC 6749 Session 4. Obtaining Authorization」
認可グラントは、リソースオーナーによる (保護されたリソースへのアクセスを行うことに対する) 認可を示し、クライアントがアクセストークンを取得する際に用いられる。
本仕様ではクライアントクレデンシャルの4つのグラントタイプを定義しており:
- Authorization Code(認可コード)
- Implicit(インプリシット)
- Resource Owner Password Credentials(リソースオーナーパスワードクレデンシャル)
- Client Credentials(クライアントクレデンシャル)
※ この記事では、認可コードフローの仕組みの説明のみに焦点を当てています。
3.1. 認可コードフロー
※ 「RFC 6749 Session 4.1. Authorization Code Grant」
認可エンドポイントに認可リクエストを投げ、応答として短命の認可コードを受けとり、その認可コードをトークンエンドポイントでアクセストークンと交換するフローです。
次の図は、認可コードフローの概要を示しています。
Figure 3: Authorization Code Flow
(2):認可コード フローは、クライアントがユーザーを /authorize
エンドポイントにリダイレクトさせることから始まります。 これはフローの対話部分であり、ユーザーが操作します。 この要求で、クライアントは、scope
パラメーターで、ユーザーから取得する必要のあるアクセス許可を指定します。
GET {認可エンドポイント} 例:/oauth/authorize ?response_type=code // 必須 &client_id={クライアントID} // 必須 &redirect_uri={リダイレクトURI} // 条件により必須 &scope={スコープ群} // 任意 &state={任意文字列} // 推奨 HOST: {認可サーバー}
- response_type: 認可コード フローでは
code
を指定する必要があります。 - client_id: セクション 2.2 で説明されている登録プロセス中にクライアントに発行されたクライアント識別子です。
- redirect_uri: アプリの
redirect_uri
。ここでは、認可応答をアプリで送受信できます。 - scope: 認可エンドポイント(2)およびトークンエンドポイント(5)では、クライアントは
scope
リクエストパラメーターを用いて要求するアクセス範囲を明示することができます。 - state: 認可要求に含まれ、認可応答に含まれ返される値。 任意の文字列を指定することができます。 クロスサイト リクエスト フォージェリ攻撃を防ぐために通常、ランダムに生成された一意の値が使用されます。 また、この値で、認可要求の発生前のアプリにおけるユーザーの状態に関する情報のエンコードができます。 例:表示していたページまたはビューをエンコードできます。
(3):この時点で、ユーザーは認証と認可フローを完了するよう求められます。ユーザー名とパスワードを入力したり、ソーシャル ID でサインインしたり、ディレクトリにサインアップしたりするなど、いくつかの手順が必要なことがあります。そして、クライアントへのアクセスを提供します。
(4):認証と認可フロー(3)を完了すると、認可サーバー はユーザーが redirect_uri に使用した値でアプリに応答を返します。
HTTP/1.1 302 Found Location: {リダイレクトURI} ?code={認可コード} // 必須 &state={任意文字列} // 認可リクエスト(2)に state が含まれていれば必須
- code: アプリが要求した認可コード。 アプリは、認可コードを使用してターゲットリソースのアクセストークンを要求できます。 認可コードの有効期間は非常に短時間です。 通常、約 10 分で期限が切れます。
- state: 詳細については前のセクションの表を参照してください。 要求に
state
パラメーターが含まれている場合、同じ値が応答にも含まれることになります。 アプリは、要求と応答のstate
値が同一であることを検証する必要があります。
(5):認可コードを取得した後、クライアントはそのコードを使用してアクセストークンと交換します。
POST {トークンエンドポイント} 例:/oauth/token Host: {認可サーバー} Content-Type: application/x-www-form-urlencoded grant_type=authorization_code // 必須 &code={認可コード} // 必須 認可エンドポイント(4)のレスポンスに含まれる値を指定 &redirect_uri={リダイレクトURI} // 認可リクエストに redirect_uri が含まれていれば必須
- grant_type: 認可コード フローでは
authorization_code
を指定する必要があります。 - code: ステップ4に取得した認可コードです。
(6):認可コードを確認したら、認可サーバー は示すとおりアクセストークンおよび任意でリフレッシュトークンを発行します。
HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Pragma: no-cache { "access_token":"{アクセストークン}", // 必須 "token_type":"{トークンタイプ}", // 必須 "expires_in":{有効秒数}, // 任意 "refresh_token":"{リフレッシュトークン}", // 任意 "scope":"{スコープ群}" // 要求したスコープ群と差異があれば必須 }
※ これで認可コードフローは終了です。ステップ(7)から(10)まで は、認可コードフローの範囲外です。
4. 最後に
この記事内ではOAuth2.0で定義されているフロー の1つ、認可コードによる付与(Authorization Code Grant)についてまとめています。
これから、認可コードフローについてより詳しく理解できるように、OAuth Playgroundというツールを使ってみることができます。
※ 次は何ですか?
次回できればPKCEかOIDCについて紹介していきたいです。
最後まで読んでいただきましてありがとうございます
マネーフォワードでは、エンジニアを募集しています。 ご応募お待ちしています。
【会社情報】 ■Wantedly ■株式会社マネーフォワード ■福岡開発拠点 ■関西開発拠点(大阪/京都)
【SNS】 ■マネーフォワード公式note ■Twitter - 【公式】マネーフォワード ■Twitter - Money Forward Developers ■connpass - マネーフォワード ■YouTube - Money Forward Developers