I am curious how others are solving the following problem...
I have an angular application that requires user authentication. The user must log in to the system to make requests. The user must also be authorized to create a socket.io connection.
When the application first starts up I must determine if the user is already authorized. I was planning on looking in sessionStorage for a user object/token. Does that make sense? And if the user is not authorized, the application will make a request to get the current user. If/When that fails, a 401 error is returned and angular intercepts/prompts for a username/password.
Given that there are two different code paths for authentication (check sesssionStorage vs make a request and wait for the response) how do you trigger the requests for all the other information that is required for the application? Do you emit/listen to a LOGIN event that gets broadcast in both scenarios?
My plan was to wrap the socket.io connection attempt and the ".on(...)" calls inside of the LOGIN event, does that make sense?
And if you want to listen to socket.io events or grab information from the server in a controller that is loaded after the LOGIN event has fired, how do you trigger the data from being retrieved from the server?
Sorry for the long winded questions, but I've hit a road block and I'm wondering how others are managing all the authentication and different pieces of information that is required to get from the server.
Ok, I guess let's go in order of question asked.
Does it make sense to use sessionStorage to hold user auth info?
Yes. You will need to decide between local/session Storage depending on how you want it to work. We decided we wanted the session to still be active for the time period that the backend recognizes, so even if the user closes the browser and reopens it, they will be logged in, and so we opted for localStorage instead of sessionStorage.
How do you trigger the requests for all the other information that is required for the application? Do you emit/listen to a LOGIN event that gets broadcast in both scenarios?
After login, we redirect the user to the "landing" page (route). All of our route/state changes wait on a resolve function which is doing the session auth. We only store the session id in localStorage. After the successful state change, the controllers that go with the newly loaded views start requesting data from our services. All of our controllers are designed to load data on init. So no, we do not use events. It feels like events should be used only as a last resort - and that is also the impression I get from core Angular devs since I get chastised for event usage in pull requests :)
My plan was to wrap the socket.io connection attempt and the ".on(...)" calls inside of the LOGIN event, does that make sense?
Maybe. I am not using socket.io outright, but instead have been experimenting with atmosphere (mainly because we had a java backend requirement). I do initiate the connection in the login success handler. But the general atmosphere event handling I have put into an application-level controller that is on <body>.
And if you want to listen to socket.io events or grab information from the server in a controller that is loaded after the LOGIN event has fired, how do you trigger the data from being retrieved from the server?
I could imagine (again using an app-level controller, or a service, or in my case, likely both) a function that returns the socket.io connection. A controller that has come into being could grab the socket.io stuff on init, and setup the listeners for the events that it is interested in.
My advice is to get all of your session and auth stuff worked out first, especially with regard to how you are going to do routing. Once all of it is working to your satisfaction, then add the socket.io stuff in. I realize that this might not be possible in all cases because perhaps you need something from the socket.io connection that is critical to your app even at an early stage.
Related
I'm building an admin page for an application and have a state value 'authenticated' that flips from 'false' to 'true' after a successful login (which is authenticated on the server) which then shows the actual admin panel.
Are component state values safe from tampering by the client? Basically, if the client can modify my 'authenticated' state value to 'true', they can skip the login and go straight to the admin panel (which I obviously don't want).
I read that React Dev Tools allows the client to modify values yet everyone says "validate on the server" but I am validating on the server and updating my state accordingly, if the user is approved. If it is not wise to have a state value manage this, what is the right way to conditionally show the admin page after a successful, server-side authenticated login?
I think this is an important question since tampering with state values in a React app can have huge negative consequences on data integrity within an app/database.
TL;DR: Either require an authentication token with every request or require authentication through a session.
Never trust users always. One potentially big issue is if you "hide" admin actions behind the admins page without requiring authentication.
For example, assume the backend server uses a REST API to accept commands. In the admin panel you get links to administrative actions like a button 'Delete Everything' that sends a DELETE request to server.net:8080/api/admin/everything without requiring any authentication. If you're a user, you can find that in the code potentially and then send a DELETE request to that address from anywhere without any repercussions.
We'd never give administrative privileges to anyone who would want to delete everything... Because we'll never untrust someone. Right?
Worse, someone might find the server and fuzz some inputs to it, and oops! They manage to delete everything (or even worse, GET everything stored in the database). This wouldn't be hard to do, especially if the server you use to authenticate is the same server you use to issue commands. History has proven "security through obscurity" to be a very bad paradigm. Every action should be authenticated, even if it seems like the actions will be hard to find.
Generally, providing a JSON web token or some other form of authentication token and having the user send that with every request is a good start at least, especially if it has an expiration date. The token would be provided through a separate request with valid credentials.
Sending a token with every single request obviously isn't ideal. There are a couple of other things to try. For servers using PHP, you can probably trust sessions (though very many people who know more than me would probably disagree). In more modern cases, you could try to use Web Sockets, requiring the token after connection. Then only after authentication with the token do you allow the user to make administrative requests.
That way, even if a user knows the exact command they can send to perform any action, the server won't let them without a current session or token. Unfortunately, unless you're already using Web Sockets or depending on a session, it will likely require a lot of changes. I'd consider this to be critical though.
It is always possible to tamper values in the front-end, there is no way you can rely solely on the front end to ensure security.
Your best approach is to implement some form of authentication and authorization on your backend. In this way, even is some users pretend to be admin, they will be blocked when you do the next request to the server.
Perhaps if you can send more information regarding your problem, we can think of a more specific solution.
So I have read few methods how to do that, the first one is using onEnter mehods on route and the second one is having an higher component (like this example https://medium.com/the-many/adding-login-and-authentication-sections-to-your-react-or-react-native-app-7767fd251bd1)
So my question here is not about state management but about state update, let me explain.
I login successfully , my state is updated, and know I can access the '/settings' route.
(1'. If I logout state is update and I can't access ...)
My question is how should I handle properly session end ?
The login state is saying I'm still connected but meanwhile the session has expired. So it means I can access the '/settings' page because has not been updated. What should I do ? I see two options:
I send a request to my server each time I want to access a protected route so it will update the state accordingly.
I let the user go to '/settings' but when he tries to perform an action I check on server side if the session is still valid and send him to the login page in case the session has expired.
I hope it's clear, what do you think the better option is ? I would go for the option 2. but I'm not 100% sure
thanks for your help
Short answer for your use case: go with option 1.
Explanation: Imagine you click on "Settings" and and maybe all the settings are loaded from the store. Now, displaying all these settings is fine, but what if you want to update something? You probably make changes or even fill out some text fields, only to get informed that your session has timed out and you need to re-authenticate in order for your settings to be updated/saved. Also, what if the user is using a shared/public client and forgets to log out? All settings will still be displayed, even when the session has long timed out. And to make it even worse, just think of the additional effort required for effectively caching actions made / information added by the user that have to be replayed once re-authentication took place.
So in order to not discourage your users and for security reasons, go with option 1 and make sure the session is still valid BEFORE you render a protected resource.
This can be done in many ways, for example sending a timeout value with your token and check the value on client side (not very secure, since everything stored on the client can be altered on the client), send a verification request to your API using middleware or HoCs before rendering, a combination of both et cetera.
I am developing a REST API based on Node / Express and a frontend for it based on React / Redux. Users can login (which gives them access to additional functionality) but they can use basic functionality also without logging in.
When a user logs in, the client makes an HTTP call with the credentials, the server creates a session and returns a user object (user_id and some other data) as well as a session cookie. The React app saves the user object in its Redux state. In subsequent HTTP calls, the user is authenticated through the cookie.
When rendering the user interface, the React app determines whether it is logged in or not by checking for a user object in its state. This is used to grey out some buttons which are only available to logged in users, or to hide the login link when the user is already logged in.
The problem
It could occur that the session expires, or that the user logs out in a different browser tab. The React app has no way of knowing this and thinks it is still logged in (i.e. app state mismatches reality), leading to wrong UI display.
What pattern to solve this?
Put a hook on all Ajax calls to check for 401 and update the
state?
Return session state in HTTP headers (and then?)
A Comet pattern for the server to notify the client that it has been logged out? (not a REST API anymore then)
Additional calls before actual API calls to make sure user is still logged in? (seems wasteful)
And how to deal with this once the client detects it is no longer logged in during an ongoing operation? I'd prefer to handle this in one place rather than all functions making API calls...
I'd be thankful for some best practice!
There are two straightforward ways to deal with this issue in a React application that I can think of. Both inspired by a colleague of mine few days ago.
Use SSE (server-side-events) technology to PUSH notifications. As you correctly pointed out, this makes your API less pure. This approach should be quite an acceptable sacrifice where flawless UX is required AND/OR your server might need to push other notifications to the app.
Establish a short term timer somewhere in your client app (e.g.: setTimeout(...)) that makes periodic calls to a "ping" API endpoint that will return current user/session information. This approach will impact UX for the duration of timeout, often negligible, and is commonly known as polling.
Hope this helps!
As an alternative to the naive polling, you can make it a little smarter by adding an endpoint that lets you know in how many seconds timeout is set to occur for the session at that point in time.
Then ping just before that time (instead of at a certain poll-rate) and update accordingly.
Logging out in another tab would return with an invalid token so would be picked up, too, but not as quickly if this is your main concern.
For this you could use broadcasting to let the other tabs know immediately (or use sessionStorage's change event to simulate a broadcast on unsupported browsers).
Otherwise the best way would be to implement a ServiceWorker; these can handle all requests for your app to the server. It's a centralised piece of code separate from your app that can broadcast to all tabs that a session is lost the moment it sees that one of its requests was rejected, and you can efficiently naively poll from this one place (instead of in each individual tab's runtime).
Since I am using token from the API Server that is valid for a specific period of time. So in addition to setting token in session storage I was thinking of setting up another session storage variable that stores the timestamp at which the token was generated. Then, in my js code I plan to add the validity period (say, 3600 seconds) and check if the token is still valid or not. If it is valid then the user session is valid else it is invalid.
I'm building an app using MEAN Stack (something like Facebook). So a user can login to my app using different browsers and I want for example, if there user will add a new message to the MongoDB, I want to update his messages in the other sessions. The same of he will remove a massage. At the same time, there maybe logged in different users from different browsers and I want to notify the user with his update in the other sessions.
Does Socket.io supports such an option? And what is the best way to do it?
Thanks.
Yes socket.io do support that. Here is an example made by socket.io themselves: https://github.com/socketio/socket.io/tree/master/examples/chat
You should be looking for socket.on() which are the listeners for an event on the server side and look into socket.emit() which are the senders of the events. the .emit()could be added into an function which are triggered on a button click for example.
Depending on you needs, if you're going to send the message to every user using your app then you could use this above code. But if you only wants to send to a specific list of persons you should look into something called Rooms (http://socket.io/docs/rooms-and-namespaces/#rooms).
This does exactly what it sounds like, it emits the messages to the specific room where users have been added to when they connect to your application.
Parts of my app require user validation which is done either automatically (using an existing refresh token stored as a cookie) or manually using a login form.
While I can implement this using a Service it feels rather hackish (the various services are designed to return data). But I can't think of a better way to share capabilities between different controllers.
P.S
I did check out https://github.com/witoldsz/angular-http-auth but catching the 401 error and initiating a login means that I will make an extra call even though I can tell it will fail.
I think you can definitely break up the login process into a service, as it can be incredibly important to store and pass info to various controllers in your app, and this is precisely what services are for.
I have created a bug-reporting app using something similar to that link you pointed out, but I customized it using a service and a controller as well. These are the steps I followed as I set it up:
First, I set up the interceptor to catch the 401 errors and
broadcasts a message that login is required.
I then set up an authService to log all those bad 401 calls. If
there is a bad call, it gets stored.
I also have a login controller that also uses the authService than
handles the form, registration, login, logout, etc etc. The
controller is used in the menu on every page, so there is no chance
that a broadcast event could be missed. My controller listens for
the broadcast event, and when received, displays the login form.
After a successful login, I tell my authService to repeat all those
stored calls and delete them.
Now this works great, but what if someone refreshes the page, the authService is deleted, and the interceptor will have to do all the work again, events will need to be broadcast, and ultimately it is a pain. To overcome this I did a simple check in my login controller.
First just check to see if the authService has the user object stored.
If not, do a check with the server, if the result is that the user is logged in, populate the authService again.
If the user is not logged in, do nothing, but let the authService know that you have checked against the server and seen that the user wasn't logged in.
Again, in my case I didn't want to force users to log in unless they were trying to perform specific actions that required a login. As a bug-reporting app, I wanted to allow anonymous users to read content, but as soon as they were going to post they had to register or login.
If your case involves being logged in 100% of the time, you can completely ignore the interceptor. Just set up a service and a controller. If the login controller sees that the authService isn't populated, redirect to the login screen. Upon refresh, do a simple check against the server to ensure they are still logged in, otherwise redirect to the login screen.
A service is very appropriate in this case. As described in this video about best practices, Services aren't as much about "Getting data", as it is separation of logic from the controller. A controller says what to do, a service says how to do it.
So, in your case, a Controller says "I need to check if the user is authenticated" but to know how to do that, it depends on a service.
This fits perfectly with the data-gathering concept as well. A controller says "I need to get all of the Employee Information." The service defines how.
He specifically says in this meeting that whenever there is information that needs to be shared between controllers, a service is pretty much always the best way to do it.