Я нашел один подход, который, кажется, работает для определенных случаев использования. Если у защищенной службы также есть способ отправить действительное изображение - возможно, они также защитили страницу «Завершение входа в систему», на которой есть изображения, возможно, они защитили страницу документов Swagger, а не имеет значение - тогда вы можете выполнить тест тега img
следующим образом:
// Use a hidden `<img>` tag to test if the provided (protected) resource URL
// can be loaded. Resolves `true` if the image loads, or `false` if the image
// fails to load. Rejects if the provided timeout elapses before resolution.
function testAuthWithImage(imgUrl: string, timeoutMS: number): Promise<boolean> {
return new Promise((resolve, reject) => {
const canary = document.createElement("img");
function cleanup() {
window.clearTimeout(timeout);
// Must remove event listeners so GC can clean up canary
canary.removeEventListener("load", loaded);
canary.removeEventListener("error", failed);
}
async function loaded() {
cleanup();
resolve(true);
}
async function failed() {
cleanup();
resolve(false);
}
const timeout = window.setTimeout(() => {
cleanup();
reject("Connection timed out");
}, timeoutMS);
canary.addEventListener("load", loaded);
canary.addEventListener("error", failed);
// Assigning ths will cause the image to load or fail
canary.src = imgUrl;
});
}
Насколько я могу судить, похоже, что все современные браузеры отбрасывают ответ 401
без приглашения входа в систему, когда это для «подресурса» в другом домене, как средство защиты от фишинга. Как только я это понял, обработка настраиваемого потока входа в систему становится простой:
protected async checkLogin(promptForPass: boolean = false): Promise<UserIdentity | undefined> {
if (await testAuthWithImage(this.TEST_IMG_ENDPOINT.url, this.timeoutMS)) {
// The image-test worked so we have an existing session; just check the profile.
try { return await this.fetchUserInfo(); }
catch (err) {
// If it was an HttpErrorResponse, throw the `message`
throw err.message || err;
}
} else if (promptForPass) {
// If the test failed but we're prompting, show user/pass dialog immediately
return await this.doLogin();
}
// If we got here, we're not prompting so return undefined
}
Я думаю, что он должен постепенно ухудшаться в устаревших браузерах, потому что загрузка субресурса (тег img
) вызовет собственное приглашение входа в систему, а затем потерпеть неудачу или добиться успеха в зависимости от того, что с ним делает пользователь. Это не сработает, если сервер еще не предоставил какой-либо подходящий защищенный ресурс, и для этого требуется хотя бы один ложный запрос на рассматриваемое изображение, поэтому я бы приветствовал лучший ответ.