OpenID Connect

概要
OAuthをベースとしたHTTPベースの認証・認可1を提供するプロトコル
 OAuth2.0
  2012年から公開されたプロトコル
  MCP2でも使われている。
  ユーザではなくアプリに対して認可を行う
  ID・パスワードで認可を行い、アクセストークを用いてアクセスする
  ★ユーザ情報を取得する使用が定まっておらず、独自に実装しなくてはいけない
OAuthに対し、OIDCでは、
 認証時のユーザ情報を記載したIDトークンの追加し、
 ユーザ情報を取得するためのエンドポイントを定めた
アクセストークンとIDトークンを使う
 IDトークンはクライアント側のみで使う|ユーザ情報が含まれる
 アクセストークンでもユーザ情報を取得できるが、IDトークンに含まれるため使わないケースがある

OIDC上の主な登場人物
 ユーザ:利用者自身
 IdP:アクセス、IDトークンを発行するサーバ
 Client:認証、認可を利用するアプリ
 ResourceServer:アクセストークンを使ってアクセスされるサーバ
 SPA(Single Page Application):JavaScriptでブラウザでページ切り替え
 MPA(Multi Page Applicatioin):サーバからページ送信
 BFF(Backend For Frontend):SPAでページ切り替え apiでデータ送信
 ClientID:ClientのID
 ClientSecret:Clientのパスワード

 OIDCではClientは主に2種類に分けられる
 コンフィデンシャル:MPA、BFFなどサーバ側でClientが動作する
           ClientIDとClientSecretが発行され、ClientSecretを使って認証する
 パブリック:SPAやネイティブアプリなどユーザ端末でClientが動作する
       ClientIDのみでClientSecretは発行されない。

エンドポイント
ClientやIdPがアクセスを受ける場所|IdPのエンドポイントは仕様でルール化されてる
認可エンドポイント
 ユーザの認証を開始するエンドポイント
 例.メルカリでGoogleでログインで認可ポイントにリクエストが送信されGoogleの認証画面を表示
リダイレクションエンドポイント
 IdPの画面でログインした時にClientにリダイレクト3される時のエンドポイント
 このリクエストでトークン取得のための情報、もしくはトークンを得る
 認可レスポンスと呼ぶ
トークンエンドポイント
 ClientがIdPからトークンを取得するためのエンドポイント
 ClientからIdPへのアクセスをトークンリクエスト、逆の応答をトークンレスポンスと呼ぶ

フロー
エンドポイントを使うデータのやり取り
認可コードフロー
 トークン取得前に認可コード(ランダムな値)をClientに送信
 Clientは認可コードでトークンを取得する
 ①ブラウザからIdPの認可エンドポイントへアクセスしてスタート
 ②ログイン画面で認証すると、IdPから認可コードをブラウザに送信
 ③ブラウザはClientのリダイレクションエンドポイントへ認可コードを送信
 ④ClientはIdPのトークンエンドポイントに認可コードを送信
 ⑤IdPはトークン発行送信、Clientが取得

MPAの場合の認可コードフロー

インプリシットフロー
 SPAのClientで使用することを想定し
 JSが他ドメインURLにアクセスできなかったため作られたがCORS4が出てきておkになった
 認可レスポンスにトークンを含めるためClientからIdPへのアクセスがなくなり、
 トークンエンドポイントを使わないようにできるが、セキュアでないためRFC9700より推奨されていない
 OAuth2.1では仕様から外されている
ハイブリッドフロー
 認可コードとフローは同じ
 リダイレクションエンドポイントにトークンを含めることができる点が異なる
 RFC9700よりトークン漏れの可能性があるあるため推奨されていない
 ただ、Max-Up攻撃対策で使われる
クライアントクレデンシャルフロー
 ユーザは登場せず、Client自身をIdPに認証してもらう
 ClientIDとSecを渡してアクセストークンを取得する(IDトークンはなし)
リソースオーナークレデンシャルパスワードフロー
 Clientにユーザクレデンシャルを渡して、そこからトークンリクエストを行う方法
 Clientに高セキュリティが求められる
 IdPのログイン画面でなく、Clientの画面になるためユーザビリティは高い
 インプリシット、ハイブリッドと同様推奨されていない

トークンの種類
アクセストークン
 ClientがRSサーバにアクセスするために必要なクレデンシャル
 ハンドル型
   IdPの内部データと紐づけるトークン
   RSサーバはちんぷんかんぷんなランダム文字列
   オパークトークンとも呼ばれる
 アサーション型
  Clientを特定するIDや有効期限の情報が含まれ、RSサーバでも理解可能
  電子署名されてるのでIdPにアクセスせずに正当性を証明できる
  主にJWT形式が使われる

 オパークトークン
  RSサーバはClientから送信されたアクセストークンをIdPに送信して検証する
  IdPで検証するのでその時の有効性がわかる
  ネットワーク負荷が高まる|IdP停止時に検証できない
 JWT
  RFC7519で標準化されているデータ形式
  Claimと呼ばれるキーと値をJSON形式で記載
  base64URL5で電子署名される
  Claimは標準化されてるが独自で決めることもできる
  ヘッダにメタデータ|ペイロードに実データ|.で区切って上二つを電子署名したデータを繋いで作られる
  RSサーバは起動時にIdPの公開鍵をダウンロードしておいてそれで電子署名を検証する
  IdPにアクセスせず検証できるため、ネットワーク負荷やIdP停止時も大丈夫
  リアルタイムでアクセストークンの有効性がわからない
IDトークン
 オパークは使われない
 ユーザの認証時の情報を含んだJWT形式のデータ
 Clientが知るためのものでRSサーバに送られない
 アクセストークンとしても使用できるが、セキュリティに関するプラクティス6も変わるため特別な理由がない限りはそんな使い方しない
 ユーザの権限
  ID、アクセストークンに含めれるが、IdPの独自画面やAPIでの制御が必要、ロックイン7になる
 RSサーバやClient、もしくは第三のシステムで集中管理する手もあるが、実装に負担
リフレッシュトークン
 IdPにアクセストークンを再発行してもらうトークン
 オパーク、JWT両方使われる
 アクセストークンの有効期限が切れた時に使われる
 アクセストークンより長めに有効期限がある

認可コードフローの詳細
認可リクエスト
 まずこれから始まる
 IdPに対してフローを始めるための情報、何のフローかアクセスしたい範囲などを含める
 一般的にはGETが使われ、URLエンコーディング8されている
 パラメータ
  scope:ユーザのどの情報へのアクセスを許可してほしいかを指定
   IdPの認可画面に表示される
   ユーザの認可にてScopeの情報がアクセストークンを介してRSサーバに渡される
   RSサーバによってアクセス制御する
  response_type:認可レスポンスで欲しいデータ、フローがここで指定される
  client_id:IdPがClientを登録する時に一意に定めるID
   IdPによっては開発者によって決められることもある
   Client開発者によって認可リクエストに含める
  redirect_uri:Clientリダイレクションエンドポイントを指定
   認可レスポンス(リダイレクトレスポンス)で指定してもらう
   IdPに事前に登録しておく
  stateパラメータ:Clientでランダム生成
   リダイレクトレスポンスに含めてもらうことで正当性を確認
   nonceパラメータ:Clientでランダム生成
   IDトークンにnonce Claimとして含めてもらう
  code_challenge,
  code_challenge_method:PKCEで使われる
認可レスポンス
 ユーザがIdPにログインし認可された後返されるリダイレクトレスポンス
 code(認可コード)とstateが含まれる
リダイレクションエンドポイントへのリクエスト
 ブラウザからClientのリダイレクションエンドポイントへアクセスされる
 ClientがBFFの場合、リクエスト時のstateとURLのstateを検証する
トークンリクエスト
 Clientが認可コードを取得した後はトークンリクエストを行う
 HTTPではPOSTが使われる
 ClientがBFFの場合、コンフィデンシャルなClientとしてIdPで認証される
  Basic認証でClientIDとSec(b64エンコード)が使われる
トークンレスポンス
 トークンのデータはIdPからのトークンレスポンスにJSON形式で返される
 ClientはIDトークンの正当性を確認する
 チェックはライブラリがある
PKCE(Proof Key Code Exchange)
  認可コードが漏洩した場合の対策
  OAuth2.1から必須
  トークンリクエストにClientしか知らないcode_veriferを含め、IdPがそれをチェック
アクセストークン取得後RSサーバへのアクセス
 アクセストークンを渡す必要がある
 Bearer9トークンで渡す

RSサーバ側の処理
アクセストークンの検証
 JWTの場合電子署名をIdPからの公開鍵を使って検証
 オパークトークンの場合IdPのイントロスペクションエンドポイントへ都度検証
  イントロスペクションリクエスト
   HTTPのPOSTメソッドを使う
   Keycloakでは不正アクセス対策でRSサーバをbasic認証する
  イントロスペクションレスポンス
   アクセストークンが有効か否かの結果と有効期限(UNIX時間10)などが返される
アクセストークンのリフレッシュ
 アクセストークンの再発行にはリフレッシュトークンを利用する
 トークンレスポンスにアクセストークンと一緒に送られる
 リフレッシュリクエスト
  トークンエンドポイントにリフレッシュトークンを送信
  コンフィデンシャルなClientの場合は認証がいる(Basic認証など)
 リフレッシュレスポンス
  リフレッシュトークンがJSON形式で返される
 リフレッシュローテーション
  リフレッシュトークンを発行した時に前のリフレッシュトークンを無効にする
  リフレッシュトークン漏洩対策
ユーザ情報の取得
 アクセストークンに合わせて取得するIDトークンに含まれる
 IdP側でユーザ情報の更新があった場合まずい、情報も限られてる
 RSサーバはIDトークンを受け取らないためユーザ情報を取得するには仕組みが必要
  ユーザ情報リクエスト
   IdPへBearerトークンでアクセストークンを指定してリクエスト
  ユーザ情報レスポンス
   JSON形式でユーザ情報が返される

ログアウト
 ClientかIdPからログアウトする
 ClientでログアウトしてIdPでログインしている場合、Clientがログインできなかったりする
 Clientがログアウトする時にIdPに通知する必要がある
 OpenID Connect RP Initiated Logout
  ログアウトイベント時にClientからIdPへログアウトリクエストを送信する
  Clientのログアウトはリクエスト前後どちらでも良い
  IdPはブラウザへレスポンスし、ブラウザはポストログアウトリダイレクトURIへリダイレクトし、Clientをログアウトする
シングルログアウト
 一つのアプリでログアウトすると他のアプリもログアウトする
 OpenID Connect Front-Channel Logout
  SPAやネイティブアプリのようにClientがユーザ端末で動作する場合に使用
  IdPからのレスポンスに他のClientにアクセスする
 OpenID Connect Buck-Channel Logout
  MPAやBFFみたいにClientがサーバで動く場合に使用
  IdPから他ClientのログアウトURIへアクセスする

Keycloak
オープンソースで開発されているIdP
OIDCやSAMLに対応してる
LDAPやADとの外部連携、シングルログアウト機能あり
ユーザやClientをRealmという単位でグルーピング

検証
KeycloakとClientをローカルマシンで起動し、
SPA,MPA,ネイティブ,Batchアプリでシングルサインオン、シングルログアウトを確認した。

所感
認可コードフローについて詳しく学べたのが良い知見となった。
Kecloakの操作もできたので今後に活かせると考える。

  1. 認証と認可の違い:認証とはその人が本人か確認すること、認可は権限付与、アクセス制御のこと ↩︎
  2. MCP(Model Context Protocol):AIアプリが外部ツールやデータを呼び出す仕組み ↩︎
  3. 特定のURLにアクセスした時に、別のURLへ転送する技術 ↩︎
  4. CORS(Cross-Origin Resource Sharing):JSによる他ドメインへのリクエストを許可する仕組み ↩︎
  5. Base64でエンコードしたものをURLに変換 ↩︎
  6. 手法、慣行 ↩︎
  7. 特定の対象に頼りきりになるみたいな ↩︎
  8. URLに使えない日本語などの文字を使える文字に置き換えること ↩︎
  9. Web APIやアプリケーションで認証・認可に使用される、一時的でランダムな文字列(トークン) ↩︎
  10. 1970/1/1からの経過秒数 ↩︎

コメント

タイトルとURLをコピーしました