Identifying an Authenticated Google User's "Work Account" - authentication

We have an Learning Management System (LMS) which we roll out to organisations. All users reside under an "organisation account" of which there are a few thousand "organisation accounts" on our system.
One of our clients use Google as their main account/authentication system and so we are implementing OAuth2 as a means to provide Single Sign On for those clients.
I've created a Google Project for our platform and can successfully use Google to get a user to authenticate (https://accounts.google.com/o/oauth2/auth) in the standard way using the openid email profile scopes. Assuming the user authenticates with Google then I can get an Access Token back.
My problem however is that the list of claims that come back aren't enough for me to determine which Organisation the Google Account belongs to. I thought the "Hosted Domain" claim might uniquely identify the google account as being our client's work account however if the user logs in using an alias email address rather than the Work's "primary email" address then the hosted domain won't be correct.
I need a means of discovering which "Work Account" an authenticated Google Account belongs to so that I can map it to the right Organisation on my platform.
An example will help I think:
Our client is called "ABC Ltd"
They have a Google Work Account whereby everyone's primary email address (domain) is "...#abc.com". Users also have email aliases ("...#anotherdomain.com", "...#gmail.com" etc.)
If a user authenticates as "john#abc.com", the "hosted domain" claim comes back as "abc.com" and we can identify both the User and the Work account (yay)
However, if the authenticate with an email alias that they have such as "john#gmail.com" the hosted domain comes back as (blank) or "gmail.com" and not "abc.com". Nor do I seem to have access to their primary email address "john#abc.com" (fail).
Any suggestions on how I should discover the "Work Account" of the authenticated user? Some other "scope" to request? or a different API?
Thanks.

If a user authenticates as "john#abc.com", the "hosted domain" claim
comes back as "abc.com" and we can identify both the User and the Work
account (yay)
However, if the authenticate with an email alias that they have such
as "john#gmail.com" the hosted domain comes back as (blank) or
"gmail.com" and not "abc.com". Nor do I seem to have access to their
primary email address "john#abc.com" (fail).
For "work accounts", a user can not get a gmail.com address as an alias. Gmail doesn't support this. john#abc.com and john#gmail.com are separate accounts. So the feature is working as designed. not sure where you got the "alias" information but what happens is that we support multi-login. A user can log-in into multiple accounts at the same time and switch easily between them. When they go through the Oauth approval first time, they can select the account they want. Next time, it depends on the oauth request parameters.
If you know the hosted domain of site where a user is trying to sign-in, you can pass that hd=abc.com to google auth requests and the right account will get selected.

Related

Signing in with Google using an existing account - The correct way

Our website allows users to create a new account using a registration page where we collect username, email, passwords, avatar, etc. The users activate their account and then login and browse, buy, comment, etc like normal.
We recently added the Login with Google button to our site as an additional option. Currently the system does the following:
Get a post request from Google
Verify the signature on the JWT and prepare the credentials
Do some security checks on the request
Check if the email exists with an existing user in our system - if it does, authorize them and login
If the email and sub don't exist, create a new account and load the data from Googles credential POST to make a new account
The conflict here is whether or not we should be doing step 4 on existing accounts that were not created using Google or if those accounts should be converted to Google only accounts when they login.
For example,
I register with john#gmail.com as my account name with a password created on the register page. One day, I accidentally, or on purpose, click Sign in with Google. The system sees my email from the oauth login and finds my account already in the system.
Should it:
Log me into the account without checking password, since its already my Google account, and keep everything else the same.
Give me an error that my email is already in use on another account and abort the login process.
Convert the account to a Google only sign-in and remove the password to prevent me from logging in without using Google in the future.
Update the account with the Google sub id but keep the password option and allow them to reset their website password independently from Google should they wish to "unlink" their Google account in the future.
I believe step 4 would be the most logical, but as we have not implemented this before we want to follow the standard that most other developers would use - or maybe there's an even better way.

same gmail account has the same user ID in Auth0 even after deletion

I am implementing a functionality of removing a user account (the user will have the possibility to delete its account).
I am using Auth0 as authentication provider. If I log in the app using a Gmail account, Auth0 will create the account with user ID value of XXXXX. After I delete the user (through API or from users management section), if I try to access the app again with the same Gmail account then the user ID will still be XXXXX.
This is a problem in my case because I need to anonynimize the data and the other details should never be visible.
I can solve the problem by changing the the ID in my database but for the moment I would like to keep it.
Is this the normal behavior of auth0?
Good morning tzortzik! I work with the Auth0 Community team and after confirming with one of our senior engineer, some connections the user ID is built based on details originating from the external IDP (Google in this case). That being said, if you remove a user in Auth0 for this instance, the very next time the user logs in/signs up they will receive the same ID that originated from the external IDP. I hope this helps clear some things up. Thanks!

creating a "login with" provider pseudocode if account already exist

I am trying to figure out how to address the issue of what to do when the email already exists in certain situations. I am currently using firebase, but I believe it would be the same problem no matter what software you use. Is there a standard way to do this?
Example:
Login with Email / Register
if "email" exists then error( EMAIL_EXISTS )
else create account & login
user must verify email
EMAIL_EXISTS = "Please login with your <%provider%> account, your email already exists"
Login with Provider (google, fb, twitter, etc) / Register
If "email" exists then add provider to account providers
else create account (possibly ask for new password) & login
Login with Email
Click Connections / Providers
Add Google / Facebook / Twitter etc to account
Is there a standard way to do this? I feel like I am missing some steps. I keep seeing many apps that do not address the issue and you must have a different account for each provider which is different than your email account. Obviously a good programmer thinks about these things.
How should I go about this?
The first 4 steps are pretty much how you should implement it with Firebase Authentication if you have the One account per email address setting enabled (which it is by default) in your Firebase Authentication console.
But if you want this flow in your app, I highly recommend checking if there is a FirebaseUI library with Auth for your platform. It exists for iOS, Android, and Web, and implements many common auth flows in a consistent way.

Access extended user information using GSuite Open ID Connect

I'm integrating some of our company's internal applications to use our GSuite instance for authenticating our employees when they log in, using GSuite's support for Open ID Connect.
However, our applications uniquely identify the users using our internal "employee ID" to log in, instead of our GSuite email address. I see that it is possible to store/manage an "employee ID" within gsuite, however I'm not sure how I can retrieve this information after the user has successfully authenticated.
Currently, I'm getting back a list of "claims" from the userinfo endpoint, but these claims only include the name and email address of the user. I also need the phone number and employee ID which we've configured on GSuite.
Is it a matter of requesting additional scopes to start off with? Or do I need to make a separate request to retrieve the additional user information?
I see that there is an Admin Directory API but it appears that would be for accessing the information of ALL users within the domain, whereas I'm only interested the extended details of the CURRENT user.
Ultimately, I created a service account with scope https://www.googleapis.com/auth/admin.directory.user.readonly to call GET https://www.googleapis.com/admin/directory/v1/users/<current_user>. I had to delegate domain-wide authority to the service account, as well set it up to impersonate an admin user.
It seems like overkill, since I only need extended details of the current user, for which I already have an OAuth2 access token.
I found this question quite helpful in resolving the issue.
If anyone can provide an answer that makes use of the OAuth2 access token, I'd still appreciate it

External Login Account vs. Native Login Account

I am brand new to Visual Studio 2012 and MVC 4, and I've been working with the SimpleMembershipProvider via the WebMatrix.WebData library.
I'd like to integrate Facebook as an external login source down the road, but it's not a requirement as of right now. However, to get a decent feel for what it would take, I've been following the tutorial and guide found here - http://www.asp.net/mvc/tutorials/mvc-4/using-oauth-providers-with-mvc.
My question :
If a user has already been created using :
WebSecurity.CreateUserAndAccount(model.Email, model.Password);
WebSecurity.Login(model.Email, model.Password);
Can they be "upgraded" to an oAuthMemebership account in the future, if they choose to use their Facebook credentials instead of the email and password they created when first signing up?
I couldn't find a clear answer to this question in the guide, or elsewhere, so I'm hoping someone can clarify how that process may work.
The SimpleMembership setup allows for a local and multiple OAuth logins all sharing the same UserProfile - so a single user can login with either a local password, or FacebOogLiveWitter.
(I should state, that I'm assuming in this answer that the OAuth provider does not send back a matching piece of information for a local account. If they do then the principles of actually performing the merge are the same, but the complexity and steps are vastly reduced.)
The OAuth registration process will refuse the user if they use an existing user name, rather than try and merge two accounts. Therefore this isn't simple, you'll have to build the functionality yourself. The process is complex as there are many directions the user can approach this from (so you could simplify by only supporting one or two), and you need to enforce security as well in case someone tries to merge into an account they don't own.
I will assume you are comfortable with the link you've posted, and you've followed the Facebook help at (for example) Facebook Login and The Login Flow for Web (without JavaScript SDK) so you have a working test application.
Your general process has to have multiple user journey approaches to make sense to a user:
for a logged in user (with a local account)
let them login to facebook and associate the accounts
let them merge an existing account on your site which uses a facebook login
for a logged-in user (with a facebook account)
let them create a local account
let them merge an existing local account on your site
for a non logged in user who tries to register a local account
let them merge this new account with a facebook login that is already registered, and do that as part of the registration process
for a non logged in user who tries to register (or log in for the first time with) a facebook account
let them link this with an existing local account as part of the registration process
etc.
ASK PERMISSION
(You can skip this if the OAuth provider has sent back a matching identifying piece of information, such as an email address).
You should enforce confirmation security, usually through email confirmation sent to the target account of the merge. Otherwise:
someone can login to your site with facebook for the first time
during that process say they "own" the email address or username of a local account (remember, facebook won't necessarily confirm what their email is for you)
and therefore gain access to the existing local account
So, once the merge "request" is made, you need to ask for permission to proceed from the target account of the merge.
The MVC 4 AccountController
I will use Facebook as our OAuth example. To compare what happens when you register a user on your local authentication framework vs. OAuth:
Local: creates an entry in webpages_Membership and an entry with the same UserId in UserProfile (assuming you are using the default tables for the MVC 4 application template)
OAuth: creates an entry in webpages_OAuthMembership and an entry with the same UserId in UserProfile
Now let's look at what happens when a user signs in using Facebook for the first time:
They click on Login using Facebook (or whatever your button says)
they get taken to facebook to login
they succeed (let's assume that, and ignore the failure case)
they then get sent, invisibly to them, to /Account/ExternalLoginCallback
OAuthWebSecurity.SerializeProviderUserId is called, passing the OAuth details to that Action
They get redirected to /Account/ExternalLoginConfirmation and asked to provide a username for their new presence on your site
If that user name is available then UserProfile and webpages_OAuthMembership entries are created
This process is your chance to "join" the accounts by matching some unique piece of information. As long as you end up with the same UserId in UserProfile, webpages_Membership and webpages_OAuthMembership you should be ok. So we have to intercept the process at the point of /Account/ExternalLoginConfirmation.
If the OAuth provider has sent back a matching identifying piece of information, such as an email address, this becomes simple, test for this in the ExternalLoginConfirmation action, and auto-merge using a similar process to the one outlined below.
However, I think you can't/shouldn't assume that the user uses the same email address for your site and OAuth, (nor should you for many reasons). Also, probably in the T&Cs for something like FacebOogLiveWitter it stops you asking for the email of their account anyway, and if they don't currently they might in future.
So instead, you could link the accounts based on alternatives, like username or email address, or phone number. Either way you are going to need them to input some identifying piece of information that is unique against an account, and will pull back the target account.
Wrapping up
So to put this all together: In the first part of this answer I outlined how you will need to consider multiple user journeys to merge accounts. I will use the example 4.1.
Your process will need to:
(Assumption - when a user first registers with a local account, you ask them for an email address and validate it or assume it is valid)
Let the user login with facebook for the first time
at Account/ExternalLoginConfirmation ask them if they want to
Create a new account with you
Use their facebook login to access an existing account
Assuming the latter, then you log a request in a new table (maybe "MergeAccountRequests") with:
The facebook account UserId
The target merge local account UserId
An authorisation code to use in the email you need to send
(From this point on, if they login without confirming that merge, they will have to get sent to a page to ask them to confirm, rather than create objects in other db tables which you have to worry about later)
You then send an email to the address of the target merge (local) account asking for permission to complete the merge (a standard confirmation email, with a link)
When they click on that link, or enter the code you sent them (you could use SMS as well as email) then you need to merge the two accounts
Choose the "new" and "target accounts (in this case "new" is the facebook account as you don't have data associated with it yet)
Delete the UserProfile of the "new" account
Change the UserId of the "new" account webpages_OAuthMembership table to the same as the "target" account
Log the user out (so there are no complications depending on which account they are currently logged in with)
Display a message to the user telling them the merge is almost complete and that they can now log in with either account to confirm and complete the merge
Rather than send them to a login page, i would give them the login options alongside the confirmation message.