Access extended user information using GSuite Open ID Connect - google-oauth

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

Related

NextAuth - OAuthAccountNotLinked - Imported data from another website - Autolinking

I have OneLogin setup in my application and is working fine. Am using MongoDB database for storing the sessions, accounts and users.
And now, I imported user data from my old WordPress website(which doesn't uses OneLogin, but the native WordPress login).
So basically I imported the user data from WordPress and populated the users collection using the email_id, name, etc. When I login with the OneLogin into my application, it throws the error saying OAuthAccountNotLinked. When researched I can see that you are not recommending the auto-linking of user accounts for safety reasons. But in my case, it's a OneLogin provider that my client's organization that has now started using. And new OneLogin user registrations are manually approved by the admin. So security wise it won't be a problem. We are only using OneLogin as auth provider!
How can I setup auto-linking in this scenario? Because I have 10,000s of Users in my MongoDB collection(imported from old WordPress website). And each User is being requested to manually register at OneLogin using the same email id they were using before in the old WordPress website and is manually approved within the OneLogin.
Thanks
Quote right from the original site
Automatic account linking on sign in is not secure between arbitrary providers - with the exception of allowing users to sign in via an email addresses as a fallback (as they must verify their email address as part of the flow).
When an email address is associated with an OAuth account it does not necessarily mean that it has been verified as belonging to account holder — how email address verification is handled is not part of the OAuth specification and varies between providers (e.g. some do not verify first, some do verify first, others return metadata indicating the verification status).
With automatic account linking on sign in, this can be exploited by bad actors to hijack accounts by creating an OAuth account associated with the email address of another user.
For this reason it is not secure to automatically link accounts between arbitrary providers on sign in, which is why this feature is generally not provided by authentication service and is not provided by NextAuth.js.
Automatic account linking is seen on some sites, sometimes insecurely. It can be technically possible to do automatic account linking securely if you trust all the providers involved to ensure they have securely verified the email address associated with the account, but requires placing trust (and transferring the risk) to those providers to handle the process securely.
Examples of scenarios where this is secure include with an OAuth provider you control (e.g. that only authorizes users internal to your organization) or with a provider you explicitly trust to have verified the users email address.
Automatic account linking is not a planned feature of NextAuth.js, however there is scope to improve the user experience of account linking and of handling this flow, in a secure way. Typically this involves providing a fallback option to sign in via email, which is already possible (and recommended), but the current implementation of this flow could be improved on.
Providing support for secure account linking and unlinking of additional providers - which can only be done if a user is already signed in already - was originally a feature in v1.x but has not been present since v2.0, is planned to return in a future release.
You probably need to write your own implementation to handle such a situation. And call that implementation on each provider call back. Like a check, the email already exists in DB, through an extra level of verification like OTP etc. Finally, when everything passes, let the user in and store some extra info in the DB for future reference.
Since I wasn't able to find a straight forward solution, I come up with a workaround. It's not a perfect solution, but does the job. Maybe someone else will post a better solution here.
So what I did is, I have all the old user data imported to the users collection in my MongoDB database. And NextAuth uses this users, accounts and session collections to store the User and related data. Btw am storing the session data in database.
In my case, we have sent unique OneLogin registration URLs(created from the OneLogin Admin Dashboard) to our existing users for them to create a new account in OneLogin. Then when the User registers an account at OneLogin, the admin approves/rejects the User. If they are approved, they will be eligible to login to our app using their OneLogin credentials.
What I noticed is that, when a User tries to login, NextAuth checks the email field in users collection to find a matching record. Since we imported the Users from WordPress database, we only have records in users collection and nothing on the accounts collection. In accounts collection, NextAuth is storing the access_token, user_id (mapping to users collection), provider details, etc.
So during login, the NextAuth does the internal checks and finds the existing email in the users collection, but it fails to identify the user as there's no info about the provider(OneLogin) details.
Record from accounts collection:
So what I did is, updated all records in users collection by appending a _TEMP to the email field's values. For example, if there's a record with the value abc#abc.com, it will become abc#abc.com_TEMP
Then what I did is, I wrote an API route(/api/check_old_account) in my NextJS application, where it does the following:
gets the currently logged in User's email id (from NextAuth session)
searches the users collection for the same email id with a "_TEMP" at the end
if there exists a _TEMP version of the currently logged in User, then return a response saying an old account exists
Then in the HOME page, I wrote the code to call the above mentioned api (/api/check_old_account) if the User is logged in. This call is made only once in the HOME page, when everything is loaded. Here the login using OneLogin works without errors because we renamed the existing email id in users collection by appending _TEMP. So when User logins, the users collection inserts a new record with their email and related data to the accounts collection also. Basically there would be two records in users collection for that User now. One is their original email id record that got inserted now when the logged in (eg: abc#abc.com) and their old imported account (eg: abc#abc.com_TEMP). The reason why I wrote the code to call the API in the HOME page only is because, after User logs in, they will be redirected to the HOME page. So thought it won't be necessary to write the code globally or somewhere else.
So, if the above API response says that there's an old account that exists, I display a warning popup to the User saying that there's an old account existing in the database and asks whether they want to link that old account. If the User presses YES button, I make an API call to /api/link_old_account. In this API route, the code is written to perform this:
if there's a _TEMP version email of the currently logged in user in users collection, find the respective record _id that has mappings in the accounts collection.
then change the value of the userId field of that respective record(currently logged in user id) in accounts collection, with the user id of the record with the _TEMP version email.
then delete the record with the _id of the currently logged in user
then update the email field by removing the _TEMP from it
then deletes the records that matches the currently logged in user in the userId field of sessions collection. So that the currently logged in sessions of this User would be invalidated.
then redirects the user back to the HOME page using res.redirect(307, '/')
The solution seems to be working fine so far.

How to authorize different roles on a .Net 5 OAuth API

I have an IdentityServer4 application, a client application and a .Net 5 API. I want the client application to be able to talk to my API on the users behalf with an access token. Some users should be able to do admin requests while others should only be able to do normal user tasks.
I believe I need to add two scopes for these, api.admin and api.normal.
First question is where would I add these scopes in identityserver? Does the client request both scopes and just gets back whatever IS decides is right for that user?
Secondly, how do I validate what scopes are in the access token on my API. Method 1 should only be used if the access token contains the api.admin scope for eg.
Thanks!
First, scopes are something you typically hard-code in your client and it does not "vary" between users. It main purpose is to indicate what the "client" application want to have access to, not the user.
So you only need only one scope like "api".
Then you have different roles or claims in the access-token that describe what the authenticated user have access to.
You then use the authorization middleware in the API to determine what the user actually have access to.
Like what the picture below shows:
There is no reason that different scopes could not be requested by the client based on the user interacting with the client or even environmental based criteria.
As some quick example, a USER that has Authenticated to a Client Application that determines the user is a "Preferred Customer" vs a visitor might be granted scope to allow reading "Preferred Content".
The client then requests a "preferredcontent" scope for the "Preferred Customer" and not for the visitor.
And of course the Authorization Server may reject the scopes requested for any reason.

Does / can Alexa replace its generated UserId for a linked user?

We have an application utilizing Alexa skill with account linking for user details.
According to Alexa's account linking documentation:
Our skill is set up for Account Linking which in turn refers to a 3rd party (or it could be internal) Identity Management System (IMS) for user authentication. Our application (as well as our organization) does NOT internally maintain the app's user identities.
Let's say the user enabled our skill on her device, at which point (according to the above docs) she was issued an Alexa generated userId, something like:
"userId": "amzn1.ask.account.AFMWOL5WIGQZLYYEMM2IJL2BJ26ICPQHBPR2DSLRX46CHW36R6CPVH7RZTUZMBCHHJLQQNU3YO2BWZWNIRTM3PCAGTNZKILK33PM2XVI36H4RSINFH2A26OLURAIB2RVPV6GMK56BR5BLBIFNJKT64EANASM7IDTQJBQLGDIEUZHUIM6VSHTTOUPVPITIPOIOPESUK53TSALU5I"
Consequently, upon further interaction with the skill, she wanted to access some more personalized information which triggered account linking so her identity was confirmed and re-established via provided account linking details (OAuth 2 Authorization Code Profile), which, while doing so, as mentioned before contacted the 3rd party Identity Management System (not LWA) to verify the user identity:
the user is presented with a login screen,
she enters her credentials, say: "alice"/"password123"
she answers the relevant questions to permit certain actions that our app can do on her behalf and
is allowed to further interact with the system on a more personalized basis
Let's say that inside our app, we want to be able to identify the user by her real userId: alice and NOT the one auto-assigned by Amazon, as described above.
Does Alexa allow for this?
Upon successful account linking, does Alexa overwrite in its JSON request (or can it somehow pass it through, along with now embedded accessToken field) the userId it receives from the 3rd party Identity Provider) so that that "real" userId (is it called subjectId in OAuth parlance?)can then be used to identify the user actions during her further interactions with the app?
If yes, how can we obtain the user details from JSON via Alexa Java API?
If no, what would be possible ways to facilitate such ability?
Can the real userId be obtained from the accessToken that Alexa puts in its request after authenticating a user? If so, how?
I'm not sure if it's related to this, seems to be a similar request, has it been put on Alexa's roadmap or became a feature already?
This seems to be a detailed version of How to obtain userId specified by Alexa user during account linking
Generally speaking, after the user complete the OAuth authentication, the application (i.e. Alexa in this case) would receive a access_token and refresh_token. If you want any additional data, e.g. username alice, email, phone, address...etc., you would need to check with your OAuth provider and see if they have an API to retrieve those based on the access_token with the appropriate scope.

Anonymous login using OpenID or OAuth

In short: Can I use OpenID oder OAuth for anonymous logins on my web service?
Described in more detail:
Scenario/Background: I'm going to provide online-tools for mental health related exercises. The users should be able to see their completed exercises after each login, but I don't want them to register at my service—because I don't want to store their e-mail adresses, passwords, not even nicknames or anything like that (not even hashed!), because it could reveal the users' identities. I want to keep their privacy perfectly safe by not storing anything that relates to the offline identity.
Problem: How to perform the login (or how to recognize recurring users) without credentials?
Approach(?): When I use my Google account to log in at some third-party web services, I'm asked wether I want to share my profile data, e-mail adress—and what else. Would it here be possible to reveal nothing? The only thing, the service would know, is that somebody has a google account and knows the password. To me, it's completely unimportant who the owner is, it's just important to recognize the same owner after each login—let's say by some kind of anonymous token id.
Is this approach working?
Are there other approaches for anonymous user logins—without storing data?
Use OpenID Connect. OpenID Connect is a specification built on top of OAuth 2.0 (RFC 6749). You can delegate user authentication to an Identity Provider (such as Google) that supports OpenID Connect.
An IdP will issue an ID token to you after successful user authentication. You can find the user's attributes such as name, email address, etc. in the ID token. So, you don't have to manage users' attributes any more if you use an external Identity Provider that supports OpenID Connect.
(Addition for the comment)
OpenID Connect Core 1.0 has defined 6 standard scopes as listed below. These values can be included in scope parameter of an authorization request.
openid
profile
email
address
phone
offline_access
Among the above, profile, email, address and phone are defined in "5.4. Requesting Claims using Scope Values". They can be used to request some attributes of a user to be included in an ID token which will be issued by an authorization server. For example, when email is included in scope parameter, the ID token issued based on the request will contain values of email and email_verified (if the authorization server supports the attributes).
So, if you want to minimize the number of attributes contained in an ID token, avoid including profile, email, address and phone in scope parameter. In other words, scope parameter should contain only openid.

Identifying an Authenticated Google User's "Work Account"

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.