Avoiding Popup Blocking when Authenticating with Google
When I recently implemented Google authentication in a Web app, I discovered that I fell victim to the browser’s built-in popup blocking mechanism, which would hide Google’s requisite login dialog. While popup blocking can be pretty good to have in the face of more or less malicious websites and intrusive ads, it’s a real drag when trying to implement something so beneficial, not to say fundamental, as authentication.
At least in the end, I learned something from overcoming this challenge. What I came to realize is that browsers distinguish between (likely) wanted and unwanted popups, by determining whether popups were initiated by user clicks. What this comes down to technically, in programming terms, is that the stackframe opening the popup (via window.open) must be close on the stack to the one handling the click event.
I haven’t found any hard and fast rule on how browsers associate popups with clicks wrt. how close corresponding stackframes have to be to each other, except that it wouldn’t work until I moved the Google authentication API call to the handler of the click event itself, so my advice would simply be to open the popup as close to the event handler as possible.
I’ve tested with Chrome, Firefox and Safari browsers. These have all behaved the same wrt. generally blocking the Google authentication popup, until moving the authentication API call into the click event handler.
Beneath is a snippet of code for authentication with Google, and posting the results to the app server for validation:
// A handler for mouse click events
onclick: () => {
// Initiate Google authentication, via popup
gapi.auth2.getAuthInstance().signIn()
.then(() => {
const user = gapi.auth2.getAuthInstance().currentUser.get()
const idToken = user.getAuthResponse().id_token
const userProfile = user.getBasicProfile()
const name = userProfile.getName()
const emailAddress = userProfile.getEmail()
// Log in on app server, which will validate the ID token against Google to verify
// that the user was actually authenticated
return ajax.postJson('/api/login', {
provider: 'google',
idToken,
})
})
}