Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
145 views
in Technique[技术] by (71.8m points)

javascript - How can I check an authenticated resource is available without causing the browser to prompt for credentials?

I'm trying to retrieve data from a web service on another server, but I don't know ahead of time if the user has an established session with that server. If they do, I'd like to automatically retrieve the data, but if they don't, I'd like to walk them through a login process using my own interface. What I want to avoid is showing the browser-native authentication dialog, at least until they click my "login" button.

There are several web services, and they use either Basic or Negotiate (token/certificate) auth, depending on which one. Both types can cause a modal / native popup to appear.

Note: I think my question is basically this one except 12 years later. I have tried adding an X-Requested-With header to the request, but the service I'm talking to does not drop the WWW-Authenticate header in response, and I don't own the backend so I'm not looking for advice on how to implement that approach.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I have found one approach that seems to work for certain use cases. If the protected service happens to also have some way to send a valid image -- maybe they also protected a "login complete" page that has images on it, maybe they protected the Swagger docs page, doesn't matter -- then you can do an img tag test like this:

// 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;
    });
}

As far as I can tell, it looks like all modern browsers will discard the 401 response without a login prompt when it's for a "subresource" in a different domain, as a means of phishing protection. Once I figured this out, handling customized login flow is easy:

    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
    }

I think it should degrade gracefully in legacy browsers, because the subresource load (img tag) would cause a native login prompt, then fail or succeed based on what the user does with it. It won't work if the server doesn't already provide some suitable protected resource, though, and it does require at least one spurious request for the image in question, so I'd welcome a better answer.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...