SPAとバックエンドの認証の話
現在Spring BootでRESTなバックエンドを開発していて、かつフロントエンドのJavaScriptなどは別アプリとしてデプロイしている方に質問です。
— Tada🎉 (@suke_masa) 2023年4月27日
フロントエンド・バックエンド間の認証認可はどうしていますか?
このあたりは何がベストなんだろう、と常々考えていて、今回このアンケートを見ていいきっかけだと思ったので普段考えていることを吐き出しておく
3rdパーティに公開する可能性があるか
3rdパーティに公開する可能性がある場合、APIはOAuth2のリソースサーバーとして振る舞い、アクセストークンで保護する
その際、SPAアプリにアクセストークンやリフレッシュトークンを持たせたくないので、別途OAuth2のクライアントの役割をするバックエンドが欲しい(BFFと言っていいのか?)
SPAとBFFの間はcookieで繋ぐという形にする
( SPA -cookie-> BFF -access-token-> API )
BFFはcookieに紐づけてアクセストークンやリフレッシュトークンを管理する
別にBFFとAPIは同じアプリケーションにまとめてもいいと思う
アクセストークンが必要なので、認証にはOAuth2の認可コードフローを使うことになる
ユーザー管理がそのサービス内で閉じない場合
これは
- 別のサービスも公開していて(あるいは今後その予定)、そちらとIDを統合したい
- ソーシャルログインしたい
- 自分でパスワードとかを保有したくない(外部のIDaaSに任せたい)
などのパターンがあると思う
この場合OIDCを使って、認証は外部に移譲する
バックエンド側はOAuth2でいうところのクライアントの役割を担う
どちらでもない場合
上記どちらにも当てはまらない場合は、OAuth2やOIDCを採用するのは過剰に感じる
ID/パスワードを受け付けてcookieを発行するエンドポイントをAPIに用意すればいいと思う
cookieについて
どのパターンでもcookieを使っているが、色々考えた結果cookieが最高、という結論に落ち着いた
結局SPAとバックエンドの間のセッションは自前で維持しなくてはいけないわけで、そのために毎回API実行時になにかしらのトークンを付与しないといけない
HTTPヘッダーにつけるのも選択肢なのだが、以下の3点でcookieに比べて劣位があると感じている
- cookieならブラウザが自動でつけてくれる
- javascriptから見れない(http only)のでXSSされても抜き取られない
- 最初の受け渡しに困らない
特に3つ目が悩みポイントで、cookieを使わない場合、バックエンドからSPAへのトークンの受け渡しは
- index.htmlとかにレンダリングしちゃう
- SPAにリダイレクトする際にquery parameterで渡す
ぐらいしか方法が思いつかない
前者だとnextjsとか使ってSSR必須になるし、後者だとimplicitフローの問題点ふたたび、という感じがある
かといってそこをガチガチに固めていくと結局SPA側をクライアントにするのと何も変わらない、ということになる
さて、cookieを使おうとすると、SPAとバックエンドを同じドメインで配信しないといけない
以下の選択肢がとれる
- バックエンドにSPAアプリも同梱する
- 前段にwebサーバーをおいて特定のパス(/api/* など)だけバックエンドに流す
先日こちらもアンケートになっていた
現在Spring Bootで画面のあるWebアプリを開発していて、なおかつSpring側は全てREST+画面はJavaScriptフレームワークで作っている方に質問です。.jsファイルなどの静的コンテンツはどうしていますか?
— Tada🎉 (@suke_masa) 2023年4月24日
1️⃣JARに含めて同一アプリとしてデプロイしている
2️⃣別のアプリとしてデプロイしている
別でデプロイはしたいんだけど、ドメインは同じにしたいので、個人的には後者を推す
webサーバーと書いたが、AWSで構築するならCloudFrontを使う