Remix AuthのOpenID Connect対応ストラテジを作った
はじめに
RemixというReactをベースにしたWebフレームワークにおいて認証機能を入れたい時には、Remix Authと呼ばれるライブラリを使うと簡単にできるようです。Remix Authはストラテジパターンで拡張できるため様々な派生ライブラリがあります(代表的なものだとRemix Auth OAuth2)が、きちんとOpenID Connect対応した拡張がなかったので作りました。
なぜ作ったのか
前述したようにRemix Authを拡張したライブラリがいくつかあり、Remix Auth OAuth2をベースにしたOpenID Connect対応を謳うライブラリも存在しました。しかし、ソースを読んでみると単にscopeにopenidを追加しただけでnonceパラメータを使っておらず、トークンレスポンスで返ってきたIDトークンを確認せずアクセストークンを使って/userinfoエンドポイントにアクセスしてユーザー情報を取り、その情報を丸呑みして認証するような所謂OAuth認証[1]になっていたので、ちゃんとOpenID Connectに対応した拡張を作りました。
どうやって作ったか
IdPを作るよりRPを作る方がはるかに簡単とはいえ、一からパラメータをパースしたりOAuthやOIDCの膨大な仕様を誤りなく実装しきるのは時間がかかるし難しいので、node-openid-clientを使って対応しました。こちらのライブラリはOpenID FoundationのCertificationもとっているので安全安心に利用することができます(利用方法を誤らなければ)。
Remix Authストラテジの実装
Remix Authストラテジは、公開されているStrategy
を継承したクラスを作り、そこにauthenticate
メソッドを実装すれば、利用者がauthenticator.use(strategy, "strategy-name")`というシグネチャで簡単に利用することができます。Remix Auth OAuth2で実装されている認可コードフローの流れを踏まえながら、nonceパラメータを追加したりIDトークンのチェック項目を拡張できるようにしたりしつつ、node-openid-clientを使ってリクエストを組み立てていけば100行くらいで実装することができました。
実装したRemix Authの利用
実装したストラテジを実際に利用できるか、簡単なサンプルアプリを作って確かめました。
Remix Authのインターフェースに則っているので、以下のようなコードを書くだけでアプリから非常に簡単に利用することができます。手元でKeycloakを動かして実際にログインして、トークン類も取得できることを確認しました。
おわりに
とりあえず認可コードフローが動くRPであれば既存のライブラリを使いながら簡単に作れるよなというのが改めての感想でした。Remix Authがサーバーサイドで動くWebアプリの認証ソリューションなのでハイブリッドフローなど認可コードフロー以外のフローに対応する必要もない気がしますし、ほとんどnode-openid-clientに丸投げしているラッパーみたいなストラテジになったので、自分で仕様対応しないといけないことも現時点で見つけられてないのですが、最近ドラフトから晴れてRFCになったDPoPを使えるのか等は色々と試したい気持ちがあります。
あと、某IdPはクライアントID/シークレット方式のクライアント認証と他の認証方式(JWTベアラーなど)の応答時間の差がものすごく大きいので、そうしたクライアント認証方式のバリエーションは試してみたい。
-
元のRemix Auth OAuth2が認可コードフロー+PKCEを前提としているので、よく考えるとOAuth認証で懸念される認可コード横どり攻撃やアクセストークン置換攻撃への耐性はあります。しかし、それは単にOAuthをできるだけ安全に利用しているにすぎず、OpenID Connectに対応しているわけではない(例えばIDトークンを見ていないのでmax_ageのような認証結果の連携ができない)ため、そうしたライブラリがRemix Auth OIDCみたいな冠をつけているのは誤っていると考えています。 ↩︎
Discussion