メモ書き-msal.jsでSSOしたらiOSやChromeでエラーが発生したので対応した話
はじめに
下記のライブラリのバージョンで発生したエラーに対する対応となります。
参照される時期によっては対応方法や対応不要になったりと状況が変わっている可能性がありますのでご留意ください。
- @azure/msal-browser: 2.13.1
(状況変わっているのが一番うれしい
iOSやChromeでSSOしたときにエラーが発生する
すでにadal.jsを使用しているWebアプリケーションやほかシステムでログインが行われている前提であった場合msal.jsの ssoSilent
はよく使用する機能かもしれません。
SSO SilentはセッションCookieを利用しているのですが
iOSやChromeのシークレットブラウザを使用した場合サードパーティCookieを参照できないため
ssoSilent
で下記のようなエラーとなってしまいます。
InteractionRequiredAuthError: login_required: AADSTS50058: A silent sign-in request was sent but no user is signed in. The cookies used to represent the user's session were not sent in the request to Azure AD. This can happen if the user is using Internet Explorer or Edge, and the web app sending the silent sign-in request is in different IE security zone than the Azure AD endpoint (login.microsoftonline.com).
シングル サインオン (MSAL.js) - Microsoft identity platform | Microsoft Docs
この問題に対する対応
対応策1:ChromeやiOSの設定を変更する
てっとりばやいのはChromeの下記設定をOffにする
iOSの下記設定をOffにすることです。
しかし、あえてセキュリティ上守られているものを解除する方に寄せていくのもよろしくありません。
ここの設定を変更するのは原因の切り分けを行うときくらいにとどめておいたほうが良いと思っています。
対応策2:エラー発生時にLoginRedirect/LoginPopupを行うように対応する
エラーの内容としては「ちゃんとログインしてくださいね」ということなので、別途ログインするだけで良さそうです。
UserAgentなどで特定ブラウザだけloginRedirect
することも考えましたが
対応しなければいけないブラウザが増える可能性もありますし
ssoSilent
したときでAADSTS50058
が出たときに対応したい。と状況は限定的なので
ssoSilent
でエラーをキャッチしそこでloginRedirect
なりloiginPopup
を行うように対応してみました。
Popupを使用する場合は↓な感じです。
async login(): Promise<AuthenticationResult> { const res = await this.client.ssoSilent({ loginHint: loginhint }).catch((err: AuthError) => { if(err.errorMessage.includes('AADSTS50058')) { return this.client.loginPopup(); } throw err; }); this.account = res.account; return res; }
AADSTS50058
発生しているときだけloginPopup
するようにしています。
Redirectを使用する場合は少し手間です。
RedirectされたときにキャッチするhandleRedirectPromise
をエラー発生時にだけ使用したいからです。
async login(): Promise<AuthenticationResult | null> { const errFlow = sessionStorage.getItem('errflowaction'); if (errFlow) { const res = await this.client.handleRedirectPromise(); if (res) { this.account = res.account; } sessionStorage.removeItem('errflowaction'); return res; } const res = await this.client.ssoSilent({ loginHint: loginhint }).catch((err: AuthError) => { if(err.errorMessage.includes('AADSTS50058')) { sessionStorage.setItem('errflowaction', 'dummy'); this.client.loginRedirect(); return null; } throw err; }); if (res) { this.account = res.account; return res; } return null; }
少し不格好ですが、Redirect前にsessionStorageにFlow状態を記録しておき
それによって動作を変える方法で逃げることにしました。
さいごに
実験に使ったコードは以下に格納しています。