Improve Google Login with Multiple Accounts

First and foremost I love Discourse: thanks for all of your hard work on this project.

I’m using Discourse as a private course forum. I create accounts for active students using a script, set course staff as moderators, and disable accounts if students drop the class. All of that works swimmingly.

The one hitch is integrating with Google’s authentication. The accounts I create for students use their university email address, for obvious reasons, and we do have Google authentication enabled for our domain. (Although students have to opt-in.)

The problem is that last time I checked the default Discourse Google OAuth flow doesn’t handle multiple accounts very well. A very typical case is that a student will already be signed in to Google using their non-university account. When they try to log in to the forum, rather than providing an option to switch accounts, the Discourse forum login just fails and says that they are not authorized. It also doesn’t say which account wasn’t authorized, or provide a switch accounts dialog, furthering the confusion. Then I get dozens of repetitive emails about this, which is the entire problem that the forum is supposed to be helping me solve :slight_smile:. The workaround, which seems clunky, is to have students visit another Google page (like Google.com) and go through the add account dialog there.

Originally I thought that this was just a Google problem, but recently I built something using Meteor. It’s Google authentication plugin allows you to force the use of a specific Google Apps domain, meaning that even if I am logged in to other Google accounts, or haven’t configured any, it forces me through my university provider. Which is exactly what I want. The flow isn’t perfect: first I get sent to my university login page (good), but then I get sent back to a dialog where I can choose which Google account to use… except that there is only one option. So that’s fine, but unnecessary. Overall it’s an improvement though, which leads me to believe that Discourse can do a bit better here.

Steps to reproduce this problem:

  1. Visit my closed forum in a incognito window.
  2. Try to log in, but use the wrong Google account.
  3. “Sorry, access to this forum is by invitation only.”
  4. Click the “Login with Google” button, no add account dialog, just the same message again.
  5. Now I’m stuck.

Anyway, it seems like there are a few options here, in order from best to worst:

  • Have the Google OAuth flow return the user to a switch or add accounts dialog when a login attempt fails. This is probably the most general-purpose fix.
  • Implement Google’s domain restriction so that I have to log in using my domain-specific provider
  • Add some explanation to the login failed page telling users how to fix the problem. (I can do this, but it’s a hack.)

Happy to help with this if someone can point me in the right direction, and to test any attempted fixes.

9 Likes

Another solution would be to always prompt a user to select a Google account, even if they’re only logged into one.

Both that, or:

are possible with a handful of lines of code.

Discourse uses the omniauth-google-oauth2 gem for authentication, and as per its documentation:

  • the first can be done by setting the prompt setting to select_account
  • the second can be done by setting the hd setting to the G Suite domain

You pass in these options here, like the skip_jwt parameter:

https://github.com/discourse/discourse/blob/master/lib/auth/google_oauth2_authenticator.rb#L49

I imagine the core team will graciously accept a PR doing one or the other if you put them behind a site setting. If you want this right now, or are not tracking tests-passed with your instance, then you could implement this in an plugin which registers an auth provider which inherits Auth::GoogleOAuth2Authenticator and redefines the register_middleware method (or I guess you could even monkey patch that method in your plugin before it’s called instead).

Hope this helps :slight_smile:

8 Likes

Yes, totally fine to add a couple of site settings here for domain and prompt.

4 Likes

Thanks for all of the feedback! PR issued: https://github.com/discourse/discourse/pull/5488.

7 Likes

Merged with slight changes in

https://github.com/discourse/discourse/commit/f74d6bb605f0395f4cba5e69d3c32206ca7c39a8

Thank you @Geoffrey_Challen!

5 Likes

@tgxworld can you reply with a screenshot of this in action? I am a little unclear what it does?

These are just additional parameters that we send to the authorization server to determine the way it would authenticate the user when the Google dialog pops up. I think it was mostly unclear because the PR was missing the description for the site settings.

https://github.com/discourse/discourse/commit/ef1b82a2261a1de59580edc5d2d3e326b090b513

2 Likes

We had to revert this cause it caused lots of breakage

Happy to help debug this if you can describe what went wrong. I’ve been using it for a while no with no breakage, but I’m sure whatever went wrong is not specific to my site.

Hey Geoffrey - when we added the new code clicking on any 3rd party auth button did nothing. I didn’t actually see it myself, but one of our engineers who did see it can likely give you more information. I’ll let them reply when they’re next online.

That seems very strange—why would this affect other 3rd party authentication providers? Perhaps the default settings are incorrect.

There was two problems:

  • the action on the buttons did nothing - @zogstrip fixed this in https://github.com/discourse/discourse/commit/0210a7f2bf251cffceba656fa945e2315eb3d77f

  • once the buttons were fixed, an attempted Google login immediately failed (before even logging into Google):

    “Sorry, there was an error authorizing your account. Perhaps you did not approve authorization?”

    even when tried with an Incognito window. The error message from /logs was:

    (google_oauth2) Authentication failure! immediate_failed: OmniAuth::Strategies::OAuth2::CallbackError, immediate_failed

I tried setting google_oauth2_prompt to all three options to see if that helped, but nothing changed.

9 Likes

Thanks for fixing @zogstrip. I noticed the social buttons in sign ups were not responding to the click action and my fix resulted in the login buttons being broken. Moral of the story is to not make typos :grin:

I should have read the docs clearly before merging because setting to ‘none’ by default clearly said

it will return an error if the user is not already authenticated and has not pre-configured consent for the requested scopes.

Anyway, I’ve reintroduce the site settings in

https://github.com/discourse/discourse/commit/24d0a7a4c71ba5147a5741c7a19dd061296098a0

10 Likes

Uh, please provide documentation or a better name for _hd because “hd” does not explain what that means at all :sweat_smile:

Google Apps Hosted domain that the sign-in will be limited to. See OpenID Connect  |  Google Identity Platform  |  Google Developers for more details.

6 Likes