I'm trying to use Windows Azure Access Control to avoid the security risks in using usernames/passwords in my app and to simplify the authentication. However, this is a site that can be used by medium or large companies that may wish to "pre-authenticate" users. In other words, they may want to bulk create users by putting in the users' Windows Live IDs and create their accounts automatically, before that user has ever signed in through Azure ACS. I could accomplish this by sending that user an email with a link to a one-time-use page to create their account, but I am hoping to do something a little more seamless.
What I'm trying to do is equivalent to how Team Foundation Service (*.visualstudio.com) lets you add users to a team project just by putting in their Windows Live IDs and once you do, they can log in and access the project, even if that user had never signed in to TFS previously.
What I don't understand is how to do that using ACS and System.IdentityModel. I can use the nameidentifier claim to uniquely identify a user, but how do I get a nameidentifier for another user through a given provider?
I'm sorry if I'm not explaining this well, so feel free to ask questions.
Not really an answer but just wanted to share my thoughts on the issue.
The problem with ACS and Windows Live authentication is that ACS never returns the user's email address. All we get back from ACS is a token telling me that the user is authenticated. This token is created based on your ACS realm (i.e. if you change the ACS realm, a new token will be created by ACS for the same user). Again, the admins of the company who are using your application can enter the email addresses of the user but there's no way to get that email address back from ACS.
Just thinking out loud :), there're a few things you could do:
You use ACS for authenticating the user and then use Windows Live REST API to get more details about the user using the token sent by ACS. By using Windows Live REST API, you can get more details like name, email address etc. about a user. Or you could use just Windows Live API for authenticating the user. I'm not 100% sure but I think this is what Team Foundation Service does. Do take a look at http://zud.io as it does the same i.e. uses Windows Live API.
Another idea would be to create some sort of invitation tokens. In this approach, admins would "invite" folks. They would create invitation record by providing the name, email address of the users and the application would create unique invitation tokens. The application could then create an invitation link using which users would come to your application and authenticate themselves. Once authentication is done, you could look up the invitation record and retrieve user information from the database and create user record and associate the authentication token with the user record. The issue with this approach is that a user may not use the same Live Id as provided by the administrator. For example, I have at least 3 live ids and if I have that invitation link, I could sign with any of those live ids and the application won't be able to stop me from registering.
Yet another idea would be to use Windows Azure Active Directory (WAAD) instead of ACS. You could consume Graph API in your application to create new users for your clients. You're still not managing user names and passwords as that is done by WAAD. The two issues I could think of there are - a) As an end user, I have to remember one more username/password combination and b) At the time of login, I have to provide my credentials in myusername#yourtenantname.onmicrosoft.com which to me personally is a big hassle.
We too have been going through the same pain and for now we have decided to go with approach #2.
Hope this helps.
Related
Let's say we have a backend in ExpressJS. We are using simple BcryptJS to hash and store passwords and emails in the database.
Now I want to add the link social media accounts feature like this:
Now what I was wondering is what should I store in the database? Like lets say I registered using email and password, now I go in settings and add Google login, what should I store with the user's record in the database to use the Google identity in the future when needed? Like should I store the access token? the refresh token? Should I keep refreshing the token? Should I not even store the token?
Sorry if it might sound silly, but I googled around and didn't find the answer I wanted, and I have spent the last hour thinking about this. What do you guys think? And this answer might help a lost developer in the future too.
SOCIAL LOGINS
If this is your starting point, and assuming that the email used from Google or Facebook matches that used when logging in with passwords:
Field
Example Value
User ID
203
Email
john#company.com
Then when you receive the Google or Facebook response you would need to look for an email in it, either by inspecting the ID token or calling their user info endpoint. You can then match to the User ID that makes sense to your business data.
If you store anything from those providers it should be a linked record, something like this. You should only need to store access tokens from the third party provider if your app needs to access the user's Google or Facebook resources with it:
Field
Example Value
User Link ID
1039
User ID
203
Provider
Google
Subject
d2ee68ee-7853-11ec-90d6-0242ac120003
PROBLEM AREAS
The above mechanism is inherently unreliable and can easily result in duplicate users in your business data, eg if the social provider does not give you an email and the user exists already in your business data. A technique to solve this problem can be to involve the user - ask them if they exist already in your app and if so then ask them to authenticate with an existing method (password in your case) as part of onboarding to social logins.
Foreign access tokens, from Google and Facebook, are not designed to be used to secure your own APIs - you may not even be able to validate them in some cases, and you will not be able to control claims and scopes. This leads some people to write custom code to issue their own tokens.
AUTHORIZATION SERVER
For future reference, the preferred architecture is for your UIs and APIs to only talk to your own Authorization Server, which is hosted alongside APIs. This component will then manage the following aspects for you, all of which will keep the security plumbing out of your apps:
Login connections to social providers
Dealing with provider specific differences
Providing account linking capabilities
Storing linked records
Allowing you to return your own customized tokens to your own apps
In more advanced use cases the AS can also hold onto the third party access token for you via the embedded token approach.
Main question
Should we be making a new OAuth client ID & secret for each customer or just using one for all customers of our app?
Some of my colleagues seem to think that if we use one, and it somehow leaks it will impact all customers. So if we make one for each customer, we minimize damage. But my (limited) understanding is that the main risk to customers is only if the access token / refresh token leak.
If the OAuth client ID & secret leak, someone could try phishing someone to believe they were giving permission to our app to do actions, when they would really be giving access to a malicious actor. But this would still require customers to fall for the phishing attempt. Even then, it would only impact the customers that fall for it and not the other customers.
The part I am less sure of is the impact of our actions AFTER we found out about a leaked client ID/Secret. We would likely need to delete the OAuth client and make a new one to prevent phishing attempts with the leaked one(s) being possible.
I believe on a single client ID model, this would break all API calls for all customers since the access tokens we stored internally would be the ones generated from the previous client ID (the one we just deleted). The user would need to open our app, log in to their google account again, and click "allow" again to get us a new access token from the new client ID we made to allow us to call APIs again. Where if we had a different client ID for each customer, we could do this process for just a sub set of customers or a single customer.
But I also feel that under the multi-client ID model, if a client ID did leak for one customer, how can we be sure it didn't leak for the others? Wouldn't you want to get new client IDs for all customers anyways?
I see the following options:
The leaker got access to our google cloud project and has access to all active client IDs. They can even make themselves new ones, so having multiple client IDs doesn't help.
The leaker got access to our internal DB and was able to decrypt it. So they can get all the client IDs stored in the DB anyways, so having multiple client IDs doesn't help. (They might also be able to get access tokens/refresh tokens which is a much a bigger concern!)
Someone mistakenly included client ID & secret in some log file or email. This may provide access to less than the full list of client IDs, but again how do we know how many log files leaked or how many emails were sent? Wouldn't it be a bad practice to not change all of them?
Please help me if I'm overlooking anything.
Here's a diagram for more visual thinkers (values in DB would be encrypted/salted/etc.)
Update
Since there seems to be confusion, here is the terminology from auth0.com
Resource Owner(s): School1, School2, etc.
Client: Our "app" server
Resource Server: Google - specifically "Google Workspace" or "Google Workspace Admin SDK APIs" (I believe)
Authorization Server: Google - specifically "Google Identity" (I believe)
User Agent: Browser
Is the Client the Resource Owner?: No. School owns the chromebooks and manages the student/teacher accounts using them through Google Workspace.
Is the Client a web app executing on the server?: Yes. We are following "Authorization Code Flow" to get access tokens from Google for each customer. We store them on our private DB.
Customer's experience - They download and run an installer and enter the license activation code. A website with the UI will be hosted. (let's say www.school1.com/App) It can be in their network, on cloud, wherever, they just need to make sure the hosting server's network can access to our internal server (let's say at 1.2.3.4). When opening www.school1.com/App they need to setup a new admin account and login. Then they click a setup button and get google pop up asking to login to their Google account (This is how we get the access token). Then they can interact with the website in their browser clicking buttons to make actions happen.
API flow - Clicks in browser become API calls to our server (1.2.3.4) with info for us to authenticate/authorize them as a valid App customer making the call. If authorized, our server then uses the access token we have stored internally to call the Google APIs.
Optional Background
My company is looking to make a product for schools using Google Workspace to manage their Chromebooks.
Google already has some APIs that we plan to use. We hope to leverage these with our own business logic. As a dummy example let's imagine we know schools want to reboot their Chromebooks every day at a specific time. The issueCommand REST API can be used to do the reboot. Our app would handle the scheduling of calling the API.
To be able to call these APIs requires permission from the Google Workspace administrator using OAuth 2.0 to authorize us to make the requests (No other authorization protocols are supported). There seems to be two ways to do this.
Service account apparently for server to server applications
OAuth Client ID apparently for server-side web apps
The service account requires the administrator to log in to their Admin Console and take a bunch of manual steps to grant our app permission, where the OAuth Client ID seems more user friendly. The admin just signs into the google pop up and is shown all the scopes we request, and can just click "Allow"
(There are other differences such as the service account will keep working even if the admin changes, but let's just assume we're committed to OAuth Client ID)
There are 2 parts to the authorization code flow:
A front channel request in the browser that uses the client ID and gets an authorization code
A back channel request that uses both a client ID and secret, to swap the code for tokens
If these leak you can replace the client secret without impacting end users, since it is private between the web back end and the Authorization Server. You may need to redeploy the web app but that should be something you already have a plan for.
I'd recommend keeping the client ID the same though. This is not a secret anyway and any end user can see what it is when the app redirects them to sign in - eg if they use browser tools to view the HTTP request.
You have a single app so use a single client ID and secret. Trying to do otherwise would not work since when a user starts authentication you don't know who they are yet since they have not identified themselves.
The above is standard OAuth and I would stick to that since it results in simple code and a solution that is easy to manage.
Ive been researching the MS Graph API lately, and I'm running into a problem. My use case is that I want to read a certain mailbox's mail, and send mail for that mailbox. The mailbox is a non user related box, and is used as a service account for emails.
Ive found 2 possible flows for getting into that mailbox. The user authenticated flow, and the application (and admin consented) flow. For the application, I want to read the emails in the background in a NodeJS app, without user interaction.
When I look into the user consented flow, I find that to make it work the user HAS to log in atleast once, by hand, and consent to some stuff I want to do. I found this page, on how this works, and made it work in my NodeJS app. The problem is, when using this flow, I have to login BY HAND atleast once after starting my NodeJS app. Afterwards I can use the refresh token to refresh the access token, but I dont want to do the login by hand.
The second option, and most suitable option for my NodeJS app, is the application flow. I found this tutorial on how to do that, and I made it all work. I fetch a token, and that token grants me access to the box via the Graph API.
But using the application flow, I found that I have to have the roles (i.e.): "User.ReadAll, Mail.Read, Mail.Send". This gives my application rights to read and send mail FOR ALL users in the account.. Which is WAY too much overkill for my situation.
Now my question; Is there a way for me to use MS Graph, with the application authentication flow, but without having access to all users' mailboxes, only to my specific account I want to read?
Thanks in advance,
Caspar
While linking the documentation reference, I saw that I read over the most important footnote:
Important Administrators can configure application access policy to limit app access to specific mailboxes and not to all the mailboxes in the organization, even if the app has been granted the application permissions of Mail.Read, Mail.ReadWrite, Mail.Send, MailboxSettings.Read, or MailboxSettings.ReadWrite.
So it is possible using the application access policies.
For example if i have build a mobile application and using the nodejs REST api for accessing the backend.
I want to restrict the access of the application with same login credentials on a maximum of two devices.
For example me and my friend can have have access to the application with same login credentials but a third friend must not be allowed to have access to the account with same login credentials.
Can it be implemented with some kind of token. Can anyone please help me in understanding the concept to implement this.
Posting as an answer, since it does appear to be a solution.
It can be implemented with a token, but I think it's important here to maintain sessions. Also, you need to keep track of who is connected to what account, and from what device. You'll definitely need unique identifiers, and to know how many logins the account is already utilizing. If a user logs out, remove that device from the list until they login again. Read up on session management. I have had good success using PassportJS for stuff like this :)
I'm learning Node, doing authentication stuff at the moment with passport.
Say my server has 2 pages, a public home page with various login options, then a super-secret page(and perhaps more) that is only accessible after authenticating.
If I'm only going to be using 3rd party strategies, is there any reason to have a database?
I know that you'd obviously need one for local user's id and pass, but if the server exclusively relies on 3rd party authentication, would session persistence be enough things to work? Or are there still various things that you would need to save for some reason (apart from logging) ?
Could you do without a database, sure... but in this case what is the point in authenticating at all? All you would be proving is that the user has a Google account which anyone can set up for free in a matter of minutes.
If your content is super secret then chances are you want to have a database of users (email addresses and the like) that have permission to see the content. By authenticating through OAuth you will be given an access token that will allow you to fetch the authenticated users email address. This can then be looked up against your user table to see if the user is registered and if your app enforces it, check whether the user has access to the page requested.
OAuth is proving that this person is the owner of the Google/Facebook/Twitter/Github Account. You can use this knowledge to sign someone in against a database of "local accounts" based on email used at sign up, assuming you validate the email on sign up locally.