h4ckthepl4net
h4ckthepl4net

Reputation: 681

Window.closed = true while the window is opened [Twitter OAuth2 in popup window]

Information

I'm trying to implement OAuth with Twitter in a popup window, same implementation works for Google and Discord, currently what I am doing is, in a function named openOAuthWindow I am creating a new window popup with OAuth URL, then creating a Promise and saving resolve and reject functions in window object. After that, in the child window when the service redirects to my redirect_uri, my page calls saved resolve or reject in parent window (by reference from window.opener) depending on OAuth result and closes OAuth window. After child is closed I'm clearing resolve and reject functions from parent window and the Promise is correspondingly resolved or rejected. Also I've set an interval with setInterval which checks if oAuthWindow.closed === true (which indicates if the window has been closed). In case of oAuthWindow.closed === true and the Promise is not fulfilled that indicates that user closed the OAuth window manually and an OAuthError is returned as the result of Promise.


The problem

As I've mentioned earlier, everything is fine with Google and Discord, the problem is with Twitter. When the Twitter OAuth page is fully loaded oAuthWindow.closed returns true (while the window is still opened), and my logic fails (because of the error that I'm throwing indicating that user closed the OAuth window manually). Also when I go to DevTools in oAuthWindow, the window.opener property equals to null. So my reference to the parent window is also lost.


The actual questions

  1. Is there an alternative way to check if window is opened or closed? (I've tried to find some solution, but couldn't find one)

  2. Are there any alternative solutions to implement OAuth in popup window? (Any offers are appreciated)

  3. Also are there any security drawbacks to avoid using OAuth 2 in popup? (I've done research on this, I've done investigation on OAuth security, the only security problem I have found could have been the access to child window from website, but that link is not possible due to same origin policy)


public static async openOAuthWindow(url: string, windowFeatures: string = "width=500,height=600,popup=true"): Promise<CodeAndState> {
    if(!windowFeatures.includes("noopener")) {
        const oAuthWindow: Window = window.open(url, "_blank", windowFeatures)!;

        let fulfilled = false;

        const removePromiseCallbacksAndFullfill = () => {
            delete window.returnAuthCode;
            delete window.returnOAuthError;
            fulfilled = true;
        };

        let resultPromise: Promise<CodeAndState> = new Promise<CodeAndState>((resolve, reject) => {
            window.returnAuthCode = resolve;
            window.returnOAuthError = reject;
        }).then((codeAndState: CodeAndState) => {
            removePromiseCallbacksAndFullfill();
            return codeAndState;
        }).catch((error) => {
            removePromiseCallbacksAndFullfill();
            return Promise.reject(error);
        });

        let windowAliveInterval = setInterval(() => {
            if (oAuthWindow.closed) {
                clearInterval(windowAliveInterval);
                if(!fulfilled) {
                    const error = new OAuthError("User closed the window", ERROR_STATUSES.USER_CLOSED_OAUTH_WINDOW);
                    window.returnOAuthError!(error);
                }
            }
        }, 500);

        return resultPromise;
    }
    throw new Error("OAuth window sends code to the parent window, so it must be opened without 'noopener' feature");
}

Thank you for your efforts, any answer/comment is appreciated!

Upvotes: 5

Views: 978

Answers (1)

Jeff Chen
Jeff Chen

Reputation: 11

I recently had this problem as well, where opening the Google OAuth prompt in a popup window was incorrectly considered "closed".

One solution you can consider looking at is to use a broadcast channel: When the parent window opens the oauth popup, also open a broadcast channel. After the OAuth login, redirect to a page that sends the parent page a message before the popup window closes itself.

If the parent window does not receive any messages on the broadcast channel then that indicates the login was closed before it succeeded.

Upvotes: 1

Related Questions