Storing User-Id in Client Side App for API Request - javascript

I have a Node-Express REST API where I can make a GET request to a user controller - /users/:id - where :id is the user-id number stored in the database. I also have a React-Redux client side app that makes a call to the API. In order to make the request, the client app needs access to the user-id, but I’m currently not sure of the best way to store the user-id on the client side.
For additional context, my API sends to the client a JWT token on login that holds the user-id; the client app saves the token in localStorage. When the client makes a request, the API verifies that the user-id in the decoded token matches the Id contained in the URL before sending a response back to the client.
I see two potential solutions:
Decode the JWT token on the client and use the user-id stored in the token to make the API call. I think this is a potential security risk, since I believe I would need to store the secret on the client app. Also, anyone who has the token can gain access to the user’s information.
API sends the user-id on authentication, and the client stores it in localStorage. (I don’t think storing it in the Redux store would work since the user could refresh, clearing state of the user-id). My sense is that this is not best practice, since I don’t see many other client side apps taking this approach.
Which one of the two is a better solution, or is there another approach that I'm not considering?

You're right about option #1. Never decode the token client-side. That would require the client-side code to know the "secret", which would expose it to anyone looking through your Javascript.
Option #2 is good, assuming that you still send the token with every request for security purposes. For storage, yes you have to store it in a cookie or in localStorage, or as you say it will be lost on refresh.
To get the ID in the client-side code, have your client-side code read it from the cookie / localstorage. There are libraries for that; react-cookie reads cookies, for example. Either you can do that every time you need to access it, or you can read it once during the initial page load, and then dispatch it into the Redux store.

In your /users/:id endpoint you can check to see if an :id was provided and then if not, extract the id from your JWT token otherwise use the id that was passed into the API call.
If that isn't acceptable, then you could use option #2 but use sessionStorage instead of localStorage

Related

Authentication, jwt, refresh token - where to store, why and how to not break request when access token expires

I am trying to understand authentication architecture with use of json web token. Despite my research there are still few areas where I am unsure about possible implementation.
I have seen that good practice is to use access token along with refresh token. Let's suppose I am going to keep access token in a cookie (http only etc.) to be able to give secure access to content requested not only from js but by web browser itself.
Now, where to store refresh token on client. My intent is to avoid forcing user to re-login each time he visits site so in-memory solution is not possible. Thus cookie or some sort of browser local storage/db.
If it is cookie it would be sent with every request, which I could imagine is not best from security point of view. If it is in local storage (from what I've heard) is not secure either.
Apart that I do not know how to solve the flow problem. Let me explain on an example:
Let's say that server gets request for some data and with access token and it turns out token expired. Now server must take (if it is there already in cookie) refresh token or ask for it and send new pair of tokens. The problem is, that original request is lost and it would have to be renewed from client side. So operation is unsuccessful and the smooth flow is broken from user point of view or it must be automated and handled silently but I find it not so easy to provide such functionality. How such problems are solved?
For instance with basic auth there is no such problem at all because browser automatically display (or send) auth data and goes back to original request.
And one more question about refresh token. If it is used just to be able to store it in DB in backend and by this have possibility to log-out user, why not store just access token in DB and read it from there only after it expired (I could even track additional expiration time by storing that data in DB). Then we could treat such access token as refresh token i.e. on that basis generate new one.

OAuth2 flow in web frontend and REST API

I'm working on a project based on Phalcon which consists in two different stand-alone subprojects: a php + angular frontend and a php REST API.
I protected the API with OAuth2, using PhpLeague OAuth2 Server. The API server is the OAuth2's authorization server AND resource server.
This is the actual flow:
The user can browse the public endpoints of the frontend, and when hits a private page, gets redirected to the login page;
The login page has username and password, POSTs them to the frontend server;
The frontend server calls a public method on the API server, which is expecting a Password Credential Grant: it validates the credentials and sends back an access token and a refresh token;
The frontend server caches both the access and refresh token in session and uses it for some API calls: the first of those is the '/users/me', which gets info about the current user and its ACL on the frontend sections;
The frontend server sends the page to the browser, which loads its javascript files.
Now, OAuth2 states that access tokens should be short-lived and refresh-token should be long-lived: in the frontend server logic, the API calls which receives a 401 (caused by the expired access token) are retried by sending first the refresh token to obtain a new access token via a Refresh Token Grant. If this second call is rejected, I assume the user is no more logged in (refresh token expired / revoked).
The pages are using Angular to perform data and ux/ui management. My question is:
should the Angular code call directly the API server?
Actually the first thing my javascript code does is to get a config object from the frontend server, which contains the access token too, and uses it to make the calls to the API server. The problem with this is that i should rewrite again the "refresh token logic" in javascript (after it expires, i get 401s), and by what I have read on the subject i understood that it is better to not make the refresh token visible to the client (as it can generate new access tokens).
So i was thinking about a "two step approach", where every javascript API call goes to an endpoint on the frontend server which relays it to the API server, but this is obviously slower (JS -> FRONTEND -> API and API -> FRONTEND -> JS).
What is the correct approach? It's not very clear to me if the frontend should be considered as two clients (php + js) which should work separately or not, as I imagine that an hypothetical iOS app would be making calls 100% against the API server.
I have used the same approach in my own projects. The problem that we have is that the client is not secure. In order to generate / refresh a token, you need to pass secure information to the authorization server.
I have done the same as you basically, let the back-end handle the tokens and their temporary storage. You cannot and should not trust the client with important information which lets you generate tokens. In terms of delays, I wouldn't worry about it too much since you're not going to be doing that much extra work, you won't even notice the delays. I have a system like this built and used by hundreds of thousands of users with absolutely no issues.
Now, you have said a few things in here which make me wonder what you are doing.
OAuth2 is not a user authentication system, it's an application authentication system. You don't pass a user and their password and generate a token for them, you pass a ClientID and ClientSecret and they generate a token for you. Then you have an endpoint which gives you the user details for this user, you pass your userid or username and get the details of that user.
A token expired does not mean your user is logged out. Those are two completely different things. How are you going to expire a token for example, when your user wants to log out? You can't, your token will still be valid until it expires after the set amount of time has passed.
A token can be used for let's say half an hour, but your user may use the website for 1 hour. So before you hit any API endpoint, you could check ... has this token expired yet? if yes then you can go and refresh it and keep working without having to bother your user with a new login screen.
The whole point of an OAuth2 system is to make sure that only authorised clients can access it. A client is not a user, it's an application. You can have a website for example and you only want users of that website to access your API.
You can have endpoints like ValidateUser for example, where you take a username and a password and return a yes or no and then you log your user in based on that.
Irrespective of language/framework, second approach is secure and better than first one because to get access token by providing refresh token to Authorization server, it still requires Client ID and Secret which should never be passed to Browser for security reasons.
In first approach, to make a direct call it will not work if your Authz Server is hosted on different domain than your frontend server because of Same Origin policy of browsers. Even if they are on same domain, still you are exposing Client ID and Secret which will compromise your frontend server

How to keep the shared secret secret when using JWT, for example?

There is something very basic I do not understand. In order for JWT to be secure both the client and the server must share a secret.
However, the client is typically a JavaScript application running in a browser on some remote completely unknown client machine.
Suppose I am the author of both the server and the client code, how am I supposed to ensure the safety of the shared secret on the client side?
You assume the secret is shared. It doesn't have to be. (And it only ever should be shared between systems trusting each other. You usually cannot trust the client that executes your JavaScript.)
A typical use for JWT is for the Server to produce signed data using the secret and sending the signed data (without the secret) somewhere (e.g. a client) without persisting it. When it gets the data back, it can verify (using the secret rather than a persisted copy of the data) that the data hasn't been tampered with since is has been signed.
What application does that use pattern have? You can e.g. implement token-based permissions that way and thus have authentication without identification:
Let's assume you provide a cloud storage service. A user can upload a file, to which you assign some identifier, let's say 5. You generate a shareable URL that has the JWT-signed data "may access file #5" as one of its parameters and display that URL to the user. The user and everyone they share this link with can then access that file through that URL. You just have to verify that the signature is a valid signature created by you and that the signed data indicates the correct file. Of course, if someone with whom the user has shared the URL distributes it further, other people may get access that way, too. But without knowledge of the URL, the file isn't accessible.
It is the same as sending cookie to client on auth and then relying to it for other actions.
Yep, you can't ensure safety of client's cookies, they can be stolen. Same as jwt token can be stolen.
Good part about jwt is that token itself is not being the part of comminication as cookie, so you can use it even in http communications - even if somebody gets the payload or header of user's request he wont be able to create new request with other data, which is possible in case of cookies usage.

AngularJS security on authentication

We're developing a system using AngularJS and PHP. I have some concerns about the security on the matter of authentication. I wrote a basic authentication based upon multiple different examples around the web (I only started learning Angular), which uses a database via REST API calls. On some routes it checks if the user information exists before it creates a promise, but I have a few questions:
Can session information be stored in$window.sessionStorage or $cookieStorage without the client being able to modify these values or should I keep them server-side with PHP $_SESSION and fetch them from there, never storing them anywhere in JS? Session information can contain uid, role, email and name
Can I store a value, like let's say $rootScope.role or $scope.role without the client being able to modify this value? Let's say for example we have multiple levels of user accounts where super-admin is the highest. If I create a route with a resolve which would check the $rootScope.rolelevel, can a novice go change the $rootScope.role value to super-admin gaining access to restricted backend sections?
Will I have to implement a GET /session check on every route to which gets $_SESSION data to actually make sure this data stays untouched?
Or am I just paranoid?
You're not paranoid, any client-side authentication should be questioned. When it comes to security, you can't assume that the client is forbidden or unable to do anything on their own device.
Security related functions must stay on the backend, an environment you set up and control.
can a novice go change the $rootScope.role value to super-admin gaining access to restricted backend sections?
Asking questions like "can a novice..." are futile in my opinion. Do you only want security against novice malicious users? If your "restricted backend sections" can be accessed by modifying the frontend, you're doing something wrong.
Great question! Front end security requires the cooperation of the browser and your server.
Javascript is an untrusted environment, so you can’t reliably enforce any authorization there (i.e. you can’t use properties on $scope to prevent a user from doing something). Your server needs to enforce these rules by ensuring that every API request is properly authenticated and authorized.
API requests are typically authenticated with a cookie. The cookie typically contains a session identifier, which points to a row in your database which contains the authorization information (i.e. what the user is allowed to access). The user can get this cookie by logging in (presenting hard credentials like a username and password).
The cookie may also contain a signed access token, such as a JWT. Depending on your architecture you can remove the session database and rely purely on the signed token for authentication.
In either case you want to set the HttpOnly flag on the cookie when your server is sending the cookie to the browser. This will prevent the JavaScript environment from reading the cookie, this is a good security measure to yourself against XSS attacks.
You also need to protect yourself against CSRF attacks. This is a situation where another website can trigger a GET or POST request to your API, and this will send along the authentication cookies. You can guard against this by creating another cookie that does NOT have the HttpOnly flag, and storing a random value in it. The JS environment must attach this value to any request, typically as a custom HTTP header. Your server then asserts that the value is associated with the session or token.
I’ve tried to cover all the bases in this answer. If you’d like to read more, you can check out these blog posts that I’ve written, they discuss token authentication, but each has sections that cover front-end security issues:
Token Based Authentication for Single Page Apps (SPAs)
https://stormpath.com/blog/build-secure-user-interfaces-using-jwts/
Disclaimer: I work at Stormpath and we provide a secure, hosted user management solution for any application, including Angular! See https://docs.stormpath.com to learn more and find the SDK for your server.

Is it possible to restrict an API to only one web interface/app?

I have a question regarding cross-origin policies.
I have a web app that gets data, usually in JSON format, via ajax.
When the web app initialize, a unique 'key' or 'token' is created from the server via ajax and is sent to the client, as a mean to identify it. The token is sent back on every ajax call for validation purposes. If it is not validated within two hours, a PHP script deletes it, and the user is required to authenticate him/herself again.
If the user sends another ajax call (i.e. if there is activity with the associated token), the token sets its expiration for another 2 hours.
On every call, I validate the token and then process the request. Everything works well but my issue is security-oriented.
Since the token is stored client-side (very crudely, like window.token = 'YTM0NZomIzI2OTsmIzM0NTueYQ==';), won't it be possible for malicious users to inspect the code, copy the JavaScript including the token, and create another app that will access the same data?
Since the token is stored client-side (very crudely, like window.token = 'YTM0NZomIzI2OTsmIzM0NTueYQ==';), won't it be possible for malicious users to inspect the code, copy the JavaScript including the token, and create another app that will access the same data?
Yes.
And possibly even more disturbing to you may be this: it doesn't even matter how your token is stored client-side - they'd even be able to login using the same API you expose to your users for logging in. (And if you think you don't have a login API because it's a form-post or something similar, you're fooling yourself - a form post is just as much an "API" as anything else... and can easily be replicated elsewhere).
The cross-domain stuff has very little to do with anything - as that's a client-side restriction of a browser - intended for the user's protection - not yours. I can make any HTTP request I want from a desktop or a server. I can even setup a service which allows me to proxy all requests made to my service over to your service... so the cross-domain security in browsers is of no help to you.

Categories