I am creating a frontend with React and a backend with Node. I would like to manage user information using Auth0 Lock - sending a JWT with each request to the API.
What if I need to do one of the following things?
Store a blog post with an author ID
The Auth0 unique identifier is user_id, which is not an integer and therefore cannot be used as an ID/key. How would I handle this on a server-side user store?
Have a users table to store a "profile about" or other similar information
Do I read the JWT on each API request, determine if that user exists, and then create a new user if it doesn't, or relate it to a pre-existing user if it does. Is it performant to check the user database on every single API request?
I am unsure how to handle general flow with JWT-based API and Auth0.
Edit:
My thoughts after some research and reflection: Auth0's unique user identifier is provided by them as user_id. The problem here is that it is not an integer. Therefore it should not be used as the key for the users table in a database.
It seems as if you shouldn't check the user database on each request, but this may be incorrect. One idea would be to callback to the backend on initial login, if the account doesn't exist, create it, if the account does exist, move on. Then just trust the Auth0 JWT (if it verifies on the backend) on each following request after the user has logged in on the frontend.
From the very few descriptions of this process I have seen online, it seems like the way I described was the normal way. But there are some situations where it doesn't make sense. What if a user was to be banned? They could still access the server functionality with their active JWT until it expires due to time.
So, if it is normal/performant to check the user store on each API request, how do I relate Auth0's string id, user_id, to an integer ID in the datastore to do queries? I am using a SQL variant.
How to identify users
You're not explicit about which database technology you use, but in general you should be able to use regular strings as identifiers/keys. You do mention that you're using SQL variant so that may be the source of the issue; you should probably use a more specific text-based data type with a fixed length enough.
The user_id is the result of concatenating the Auth0 identity provider identifier with the user identifier within that provider so we could argue that reaching a definitive max length is a little trickier. However, you can decide on arbitrary value, for example, something like 640 character ought to be enough for anyone.
You can also identify your users by email; this works if every authentication provider being used by your application requires users to provide their email and you also don't intend to support different accounts with the same email address.
A final alternative is for you to assign each user your own unique identifier that is better suited for how you intend to use it. You can achieve this by having an Auth0 rule update your user metadata with this new attribute and then request this attribute to be included in the generated token upon user authentication by the means of scopes.
Depending on the approach you would neither need a simple lookup table mapping one form of identifier to your internal one or in the case you update the user metadata with your internal identifier you could skip that lookup table entirely and just the value coming from the JWT.
How to handle first-time users
Like you mentioned, you could at each API request make sure that if this is the first request issued by a new user then you create your notion of application profile before processing the request.
The alternative to this would be triggering this application profile creation from within Auth0 when you detect that the user signup for the first time and then on the API always assume the profile exists.
Both approaches are valid; I would go with the one that would leave you with a simpler implementation and still meets your requirements.
How to handle users being banned
If you do need to support the ability to immediately ban a user and don't allow any other request to the API then you'll always have to have some kind of query at each API request to see if the user was banned or not. This increases the complexity significantly so do consider that you can tolerate a solution where the lifetime of a token is shorter and banned users may still call your API within that short time frame.
Related
I am creating a web server for a site with authentication using JWTs. Since this is going to be a publicly available system once ready, I would like to know if the following authentication flow using JWT access and refresh tokens could be considered secure.
In this case it is assumed that one server serves both the auth API and resources
When the server starts, it will generate a "run ID" which is the current UNIX timestamp and random characters/UUID. This is stored in memory and any restart will generate a new one.
The user obtains an initial HttpOnly access and refresh tokens when logging in or registering. Access token has a short lifespan (~5 mins.) and refresh token has a longer lifespan (~2 hours).
The access token functions like normal and contains the userID.
The refresh token has the userID, the server's run ID and its own unique "token ID" (similar to run ID, unique for each refresh token)
Instead of the user calling a token refresh endpoint, new access and refresh tokens are made when calling an endpoint that requires authorisation if the following criteria are met:
The access token has expired
The refresh token has not expired
The run ID in the refresh token matches up with the current run ID
The refresh token's token ID isn't blacklisted
In the event that there is a run ID mismatch or the token ID has been put on a blackist, the refresh token is instantly considered invalid.
The token ID blacklist is stored in memory (like the run ID) as an array with the ID and expiration time.
This allows a user to logout and blacklist that specific refresh token from being used.
This also allows any system administrators to emergency invalidate all refresh tokens by restarting the server since the runID will change.
I would like to know if this can be considered a secure authentication flow
It took me a while to understand what is going on with the tokens and your motivation around it. I believe what you did here is more complicated than reality requires and there should be little reason to introduce this level of complexity. Please also note, that security in its definition is a double edged sword. It's an eternal conflict between integrity and confidentiality VS availability. If for some reason we screw the systems availability then it will not be considered secure in the same sense, that turning a computer off is not considered a solution of security issues.
Having said that, let's look into your solution.
You intend to use access and refresh JWT tokens. How should a logout in such system look like? Well, you simply forget the token information on the client side or in case you want to hold tokens in cookies - which I believe you want to have based on "HttpOnly" flag - you can clean up the cookies. This is it. Once the information is gone from the client, the tokens can not be easily recreated. There is no need to blacklist the tokens on the server. It is in clients best interest to 'forget' the tokens he is not using and once the token is gone, there is no second copy to replace it.
We use token blacklisting in case we want to deal with a scenario like real-time user account locking. An expensive thing to implement if you ask me.
You can still try to blacklist refresh tokens, but assuming your authentication mechanism has access to the user pool, it can simply check in the database if the user is locked, no need to cache anything here.
Next thing I notice is that what you did here reassembles strongly simple Http Session handling including loosing the session once the server restarts. Why not forget the tokens and use a simple session instead?
I also wonder, why you intend to have only one instance of your system. In the High Availability age and playing with JWT tokens it would totally make sense to separate authentication module from logic and have more than one instance of both.
Please also note, that no one will be able to tell you, whether what you do is secure, until they can look into your code, configuration and ideally - working system. There are many small pieces that can break the solution. Ideally have a look into the OWASP ASVS (Application Security Verification Standard) available here to see, how deep the rabbit hole goes.
How do companies handle authentication in their multi-tenant web apps?
Essentially I have a single PostgreSQL database instance with many tables. Each table has a workspace_id column which I will use to use to grant/deny access. You can think of a workspace as a client and a single user can be associated with multiple workspaces.
My initial thought was to:
Use the frontend app and let the user send the email and password to
the backend.
Backend validates details and returns all the workspaces the user belongs to.
The frontend app displays the workspaces.
User selects the workspace they want to login into. The id of the workspace and the user details that were passed in step 1 is again to the backend.
The backend validates again all the details and issues a jwt token containing the user details and the workspace id.
Later when the user tries to access any resource I will extract the workspace id from the token to check if the user has access to a resource or not.
I am halfway through implementing what I've described above but I am not sure if that's the best approach. What do you think?
I'm not sure I would call this multi-tenancy - really it is just a case of different users with different claims:
Users should log in once, then have access to the data that their user identity is entitled to
When your UI calls APIs, the back end should receive a JWT access token with either of these payloads. The second of these is preferred, but not all systems support that:
The user ID only
The user ID plus an array of workspace IDs
SIMPLEST OPTION
This might just be to look up the user's workspace IDs whenever an API request is received, based on the user ID in the JWT access token, as in Joe's comment above.
CLAIMS PRINCIPAL
If workspace IDs are used frequently for authorization across many API requests, then a better option is to design a Claims Principal object, containing data commonly used by the API for authorization, and containing the important IDs. It might look like this for a particular user:
{
sub: "wdvohjkerwt8",
userID: 234,
workspaceIDs: [2, 7, 19]
}
This object typically needs to be comprised of both Identity Data (stored by the Authorization Server) and also domain specific data. The above userID might be a database key, whereas the subject claim is often a generated value.
When an API request is received, you can either read all claims from the JWT access token, or combine domain specific data with the JWT data.
The Claims Principal is then injected where needed, so that your API authorization logic can be coded in a simple way. In your case this will involve filtering workspaces when working with collections, or denying access if the user specifically tries to access a workspace they are not entitled to.
Here is some sample Node.js code of mine that does this, using a region array claim:
Claims Principal Code
Authorizing Code
Doc
I'm trying to make a user log in just once, and have his information on all the servers. Any changes made to the user's information will instantly be available for all servers. Is this possible to do without having each user "log in" separately for each server?
Sort of like the $_SESSION for php, but for Node.js
Design 1 -
What I think would be best to do, but don't know how to share socket data between servers, perhaps using something like PHP's $_SESSION?
Design 2 -
What I'm currently doing:
User uses socket.emit to main.js
main.js adds user information onto the emit
main.js emits to the appropriate server
Appropriate server emits back to main.js
main.js finally emits back to user
This seems awfully inefficient and feels wrong
If your information is primarily static, you can try something similar to JWT. These are cryptographically signed tokens that your authenticating server can provide and the user can carry around. This token may contain information about the user that you want each server to have available without having the user accessing it.
If it's not, you may be looking into sharing a database across all servers, and have that be the point of synchronization between them.
Updates based on our comments (so they can be removed later):
If you decide to use auto-contained JWT tokens, you don't need to be making trips to the database at all. These tokens will contain all the information required, but it will be transparent to the end user that won't have insight into their contents.
Also, once you understand the JWT standard, you don't necessarily have to work with JSON objects, since it is just the serialization approach that you can switch by another one.
You'd provide one of these tokens to your user on authentication (or whenever required), and then you'd require that user to provide that token to the other servers when requesting information or behavior from them. The token will become your synchronization approach.
I have an application built in Express that has two different user roles: admin and regular.
Whenever a person signs up, there are two users created internally, one with the admin role and another with the regular role. Both of them are created with the same email address, but each one of them has a different password.
Right now, when a regular user logs in, she can switch (log in) to the admin counterpart by just entering the admin password.
I would like to add a feature to let them switch back to the regular user by just pressing a button, but this would imply to store somehow the regular user session cookie on the client.
How can I achieve this without compromising the security of the app?
These are the possible ways I have tried without any luck:
Create two different keys for the session cookie, one for each type of user, so collisions are avoided and both cookies can coexist on the client at the same time.
Change the session cookie permissions so I can access it from the JavaScript client and deal with it there.
Question: if the user logs in as an admin in the first place, would you also want them to be able to shift to a regular user with no login, or is that not a supported use case?
Question 2: are you just rolling your own authentication and user management, or do you have another library in the mix which may be relevant?
Thoughts: Without knowing how you lay out your sessions, as well as how you store your users, but thinking about the way I do (which is that I have a user attribute of req.session): you could have a server method - let's just call it downgradeUser().
When invoked, it could simply change the session.user object to the regular user which shares the email address with the then-current admin user. Assuming you have a function getRegularUserByEmail which looks up your user based on an email address, bypassing any password check.
app.get('/downgradeuser', function downgradeUser(req,res) {
req.session.user = getRegularUserByEmail(req.session.user.email);
// render, redirect, send a 200-OK, etc, based on how you're calling it.
});
While you could secure access to this method or add a check inside it to verify that the original session.user has the admin role, my quick thought is that you might not really need to, since if called by a regular user, the result would be resetting the session.user to the same thing it was, although you'd probably want to do the check to avoid the needless overhead of reloading the user as well as because you might consider an unexpected case for which you'd want a warning.
Let me know if I am missing something or if this isn't suitable for some reason.
Hope this helps.
02/20/2011:
It was confirmed by Facebook today that indeed there is one call in which the access_token is broadcast in the open . . . it just happens to be one call I use to make sure that the USER is still logged in before saving to my application database. Their recommendation was to use the SSL option provided as of last month for canvase and facebook as a whole. For the most part the Auth and Auth are secure.
Findings:
Subsequent to my posting there was a remark made that this was not really a question but I thought I did indeed postulate one. So that there is no ambiquity here is the question with a lead in:
Since there is no data sent from Facebook during the Canvas Load process that is not at some point divulged, including the access_token, session and other data that could uniquely identify a user, does any one see any other way other than adding one more layer, i.e., a password, sent over the wire via HTTPS along with the access_toekn, that will insure unique untampered with security by the user?
Using Wireshark I captured the local broadcast while loading my Canvas Application page. I was hugely surprised to see the access_token broadcast in the open, viewable for any one to see. This access_token is appended to any https call to the Facebook OpenGraph API.
Using facebook as a single click log on has now raised huge concerns for me. It is stored in a session object in memory and the cookie is cleared upon app termination and after reviewing the FB.Init calls I saw a lot of HTTPS calls so I assumed the access_token was always encrypted.
But last night I saw in the status bar a call from what was simply an http call that included the App ID so I felt I should sniff the Application Canvas load sequence.
Today I did sniff the broadcast and in the attached image you can see that there are http calls with the access_token being broadcast in the open and clear for anyone to gain access to.
Am I missing something, is what I am seeing and my interpretation really correct. If any one can sniff and get the access_token they can theorically make calls to the Graph API via https, even though the call back would still need to be the site established in Facebook's application set up.
But what is truly a security threat is anyone using the access_token for access to their own site. I do not see the value of a single sign on via Facebook if the only thing that was established as secure was the access_token - becuase for what I can see it clearly is not secure. Access tokens that never have an expire date do not change. Access_tokens are different for every user, to access to another site could be held tight to just a single user, but compromising even a single user's data is unacceptable.
http://www.creatingstory.com/images/InTheOpen.png
Went back and did more research on this:
FINDINGS:
Went back an re ran the canvas application to verify that it was not any of my code that was not broadcasting.
In this call: HTTP GET /connect.php/en_US/js/CacheData HTTP/1.1
The USER ID is clearly visible in the cookie. So USER_ID's are fully visible, but they are already. Anyone can go to pretty much any ones page and hover over the image and see the USER ID. So no big threat. APP_ID are also easily obtainable - but . . .
http://www.creatingstory.com/images/InTheOpen2.png
The above file clearly shows the FULL ACCESS TOKEN clearly in the OPEN via a Facebook initiated call.
Am I wrong. TELL ME I AM WRONG because I want to be wrong about this.
I have since reset my app secret so I am showing the real sniff of the Canvas Page being loaded.
Additional data 02/20/2011:
#ifaour - I appreciate the time you took to compile your response.
I am pretty familiar with the OAuth process and have a pretty solid understanding of the signed_request unpacking and utilization of the access_token. I perform a substantial amount of my processing on the server and my Facebook server side flows are all complete and function without any flaw that I know of. The application secret is secure and never passed to the front end application and is also changed regularly. I am being as fanatical about security as I can be, knowing there is so much I don’t know that could come back and bite me.
Two huge access_token issues:
The issues concern the possible utilization of the access_token from the USER AGENT (browser). During the FB.INIT() process of the Facebook JavaScript SDK, a cookie is created as well as an object in memory called a session object. This object, along with the cookie contain the access_token, session, a secret, and uid and status of the connection. The session object is structured such that is supports both the new OAuth and the legacy flows. With OAuth, the access_token and status are pretty much al that is used in the session object.
The first issue is that the access_token is used to make HTTPS calls to the GRAPH API. If you had the access_token, you could do this from any browser:
https://graph.facebook.com/220439?access_token=...
and it will return a ton of information about the user. So any one with the access token can gain access to a Facebook account. You can also make additional calls to any info the user has granted access to the application tied to the access_token. At first I thought that a call into the GRAPH had to have a Callback to the URL established in the App Setup, but I tested it as mentioned below and it will return info back right into the browser. Adding that callback feature would be a good idea I think, tightens things up a bit.
The second issue is utilization of some unique private secured data that identifies the user to the third party data base, i.e., like in my case, I would use a single sign on to populate user information into my database using this unique secured data item (i.e., access_token which contains the APP ID, the USER ID, and a hashed with secret sequence). None of this is a problem on the server side. You get a signed_request, you unpack it with secret, make HTTPS calls, get HTTPS responses back. When a user has information entered via the USER AGENT(browser) that must be stored via a POST, this unique secured data element would be sent via HTTPS such that they are validated prior to data base insertion.
However, If there is NO secured piece of unique data that is supplied via the single sign on process, then there is no way to guarantee unauthorized access. The access_token is the one piece of data that is utilized by Facebook to make the HTTPS calls into the GRAPH API. it is considered unique in regards to BOTH the USER and the APPLICATION and is initially secure via the signed_request packaging. If however, it is subsequently transmitted in the clear and if I can sniff the wire and obtain the access_token, then I can pretend to be the application and gain the information they have authorized the application to see. I tried the above example from a Safari and IE browser and it returned all of my information to me in the browser.
In conclusion, the access_token is part of the signed_request and that is how the application initially obtains it. After OAuth authentication and authorization, i.e., the USER has logged into Facebook and then runs your app, the access_token is stored as mentioned above and I have sniffed it such that I see it stored in a Cookie that is transmitted over the wire, resulting in there being NO UNIQUE SECURED IDENTIFIABLE piece of information that can be used to support interaction with the database, or in other words, unless there were one more piece of secure data sent along with the access_token to my database, i.e., a password, I would not be able to discern if it is a legitimate call. Luckily I utilized secure AJAX via POST and the call has to come from the same domain, but I am sure there is a way to hijack that.
I am totally open to any ideas on this topic on how to uniquely identify my USERS other than adding another layer (password) via this single sign on process or if someone would just share with me that I read and analyzed my data incorrectly and that the access_token is always secure over the wire.
Mahalo nui loa in advance.
I am not terribly familiar with Facebook's authentication/authorization methods, but I do believe that they implement oauth (or something close to it) for delegation, distributed authorization, and "single sign-on".
OAuth is described by RFC-5849
EDIT: Facebook Uses OAuth 2.0 which is still in working draft.
In OAuth, and similar systems, the "access_token" is only part of the picture. There is also typically a secret key, which is known only by the service provider (facebook) and the client application (your app). The secret key is the only part that is expected to stay secret - and that part is never sent over the wire (after it's initial issuance).
In the case of Facebook, I think the secret key is assigned to you when you register your application to use their API, and the 'access_token' is returned to you for a given user, whenever the user agrees to allow your app to access their info.
Messages are sent in the clear, including the user's username, and the relevant "access_token"; However, each message must also include a valid signature in order to be accepted by the server. The signature is a cryptographically computed string, that is created using a technique called HMAC.
Computing the HMAC signature requires both the token and the secret, and includes other key parts of the message as well. Each signature is unique for the given message contents; and each message uses a nonce to ensure that no two messages can ever be exactly identical.
When the server receives a signed message, it starts by extracting the access_token (clear-text), and determining which app the token was issued for. It then retrieves the matching secret from its own local database (the secret is not contained in the message). Finally, the server uses the clear-text message, the clear-text access_token, and the secret to compute the expected HMAC signature for the message. If the computed signature matches the signature on the received message, then the message must have been sent by someone who knows the same secret (i.e. your application).
Have a look at Section 3.1 of RFC-5849 for an OAuth specific example, and further elaboration on the details.
Incidentally, the same approach is used by Amazon to control access to S3 and EC2, as well as most other service providers that offer API access with long-term authorization. Suffice it to say - this approach is secure. It might be a little counter-intuitive at first, but it makes sense once you think it through.
Adding a few links and quotes from Facebook Documentation:
Facebook is indeed using the HMAC-SHA256 algorithm. Registration document (PHP Example reading signed_request section).
Always verify the signed_request:
If you are unable to validate the
signed_request because you can't embed
your application secret (e.g. in
javascript or a desktop application)
then you MUST only use one piece of
information from the payload, the
oauth_token.
The Authentication Document contains a lot of useful info about the different flows you may use to authenticate a user. Also read the Security Considerations section at the bottom of the page:
Cross site request forgery is an
attack in which an trusted
(authenticated and authorized) user
unknowingly performs an action on
website. To prevent this attack, you
should pass an identifier in the state
parameter, and then validate the state
parameter matches on the response. We
strongly recommend that any app
implementing Facebook user login
implement CSRF protection using this
mechanism.
It was confirmed by Facebook that indeed there is one call in which the access_token is broadcast in the open - it just happens to be one call I use to make sure that the user is still logged in before saving to my application database. Their recommendation was to use the SSL option provided as of last month for canvas and Facebook as a whole. For the most part the Auth and Auth are secure.
To ensure a secure interface between a third party application and a Facebook application or even any website that uses Facebook Single Sign on, an identity question would provide the extra layer when used in conjunction with the access_token.
Either that or require your users to use Facebook with the new SSL feature of Facebook and Facebook Canvas Applications. If the access_token is broadcast in the open it cannot be used to uniquely identify anyone in your third party database when needing to have a confirmed identity before database interactions.