Using React with KeyCloak - Do you actually need to store the JWT? - reactjs

I've been learning about how to use Keycloak from React (with the Keycloak javascript adapter) with the scenario that Keycloak will provide SSO, allowing the React application to authenticate users with Keycloak, and then use the access JWT provided by Keycloak for bearer authorization with some secured REST api.
I have read lots of discussion on Stack Overflow about where is the best place within React to store the JWTs that Keycloak provides when the user is first authenticated (i.e. context / redux / local storage / cookies). However whatever choice you make about storing a JWT there is always a risk of a XSS attack and the token therefore being obtained by someone else. Because you'll be storing a refresh token as well as the access token, if these are both at risk of being compromised then that would be less than ideal!
Thinking about this I'm wondering if it's actually possible to not ever need to store these tokens in memory at all. I am wondering if the following scenario woud work, and if so (1) is it a better security approach or have I missed something, and (2) are there any disadvantages to it?
The plan is this... When the user first runs our React application we authenticate the user via Keycloak, which might require a login / authentication process. React gets the response back and only extracts and stores from the response the parts that the React application needs to operate, such as some user information and resource roles. But we won't store the JWTs, instead we will instantly discard them.
When the React application subsequently needs to make a rest call it first will re-authenticate the user via keycloak, which will be a seamless / non-visible process from the user's point of view, as they are still authenticated as far as Keycloak is concerned. (On that last point I'm guessing that this is done by a session cookie from Keycloak?). Keycloak will respond to the react application with the tokens, we extract the access token and use it to make the required rest call, and then the React application can discard the token immediately after it has been sent to the server.
Have I come up with a genius idea, or is this seriously flawed???

Related

Is it safe to store access token in next-auth session?

What I want is to access the customer details in the client side in next.js and I need to pass the customer access token to the backend API. Using next-auth, I was able to store the access token to the session but is it safe or is it better to use the next.js api route and use getToken function? I'm still a beginner in frontend security, I don't know if I'm just being a paranoid but I can't find a topic/post that states it is completely safe.
So by default the session strategy is set to jwt, which means your session is encoded / signed and safe to store sensitive info in.
However, NextAuth.js can be used to automatically put that accessToken you get from your OAuth provider in the JWT token via the jwt callback. Therefore you can pull it out anywhere with the getToken() method you mentioned and authenticate against Google APIs for further use (get drive contents, contacts, etc, etc, etc).
Check out this example for how to do that in the jwt callback: https://github.com/nextauthjs/next-auth-refresh-token-example/blob/57f84dbc50f30233d4ee389c7239212858ecae14/pages/api/auth/%5B...nextauth%5D.js#L67

React & Express JWT Auth: Is it safe enough to store access tokens in Cookies?

I've spent a few days trying to figure out a secure authentication method for SPA/React (client-side).
Most of the tutorials I've read in the wild contradict each other.
One says have to store in Cookies another in Local Storage, one says don't need to use refresh token, one says have to use a refresh token.
I'm building a React SPA app for the frontend and Express for the API (backend). Both are stored in the same domain:
React: example.com
Express: api.example.com or example.com/api
Is it enough to secure my application by using Cookie (access token JWT):
httpOnly:✅
secure: ✅
sameSite: strict
without refresh token
This matches the answer here: https://stackoverflow.com/a/57779076/11340631
The question is:
Is this safe enough?
How long does it take to set the expiration of the access token?
Is this as per Oauth recommendation?: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps
What if my access token is stolen? For example, my friend is using my PC and he stole my cookies and use it in his PC browser.
I really hope to get the answer here, any answer is appreciated.
It's safe against extracting the token with Cross Site-Scripting, but, without other security controls it might be prone to Cross Site Request Forgery (cookies are automatically attached to a request). Is API accepting key in the cookie or must it be sent in the Authorization Bearer header?
My concern is, that if you're not using refresh token, the access token must have a relative long expiration. OAuth2 was not intended to be used to authentication alone, but together with some session-like solution, for example OpenID Connect and SSO.
The shorter the better, unless it can be revoked any time server-side. If there's no way to revoke the key, the 5 minutes expiration date is, in my opinion maximum. That's why refresh token and session-like endpoint is the must.
OAuth is not designed for web application client's authentication at all. That's the common anti-pattern in many projects I've pentested. https://oauth.net/articles/authentication/
I'm glad for your awareness of such a threat. Access tokens must either live very shortly, or they must be revoked server-side in a some way, for example by utilizing some kind of revoke-list. Or the refresh token with server-side-like session endpoint should be utilized.

How do authentication tokens and authorization work in reactjs?

I'm new to reactjs and I've ben trying to understand how the authentication token works to protect routes. In various tutorials people get that token from an api when logging in a user, and then store it along with a "isAuthenticated" variable set to true in localStorage. Then when routing they check if isAuthenticated is true, without any api call to verify the token. Is that safe? I was trying to implement authorization in the same way, by just adding some isAuthorized variable, but can't both of these be tampered with since react works client side?
Of course they can. Whatever is on the client is fully controlled by the user. There is no "client-side authorization". Such features (access control to certain functionality on the client) is usually a user experience feature, like why show something to the user that won't work anyway.
All authorization must be done server-side.
This inherently means that it's usually ok to have page structures (views) without data in the client for anybody to see, the point is that data from the backend will be authorized and will only be available to appropriate users.

What is the correct way to implement a stateless authentication?

I am new to the Single Page Application. One big question for me is how to make my application secured. I am using React in the front-end and express + mongodb in the back-end.
I old web site, we use session to do the authorization. If session is timeout, we can let the user redirect to the login page. And if a user is keep do some actions on our website, his session will never expired.
But now, I am using JWT to do the authorization. A token may expired in 1 minuet, after that, the user have to login again.
For my understanding, one way is 're-send a token on every request/response, then each request/response will have a new token'. But I think this is not the correct way of how to use JWT.
So my questions are:
What is the correct way to avoid the user login again if he still work on our web app?
Do we need to store the token in the database (mongodb)?
If I store the token in localStorage, everyone can borrow it from the browser and copy the token into their client. How to avoid it?

Firebase auth state persists on client, but not on hard refresh

I'm building an isomorphic React app that uses Express to handle server requests.
When running the bundled React app on the client side, my Firebase login flow works nicely:
I login using Firebase's email/password option
After authentication, ref.getAuth() successfully returns the user's auth object
Subsequent calls to ref.getAuth() while navigating through my app client-side (via react-router) also return a successful auth object.
However, hard refreshes (which would come from the server) don't persist, even after a successful login on the client. Using the same React components in a server context, ref.getAuth() returns null.
Am I missing a step to make this work on the server in the same manner it works on the client (with the use case being a hard-refresh of the site)?
If you're connecting to Firebase on the server as part of your isomorphic/universal rendering (which I assume you are), Firebase has no way of knowing which user initiated the request to your server that then subsequently issued the request to Firebase—on the client, the user's cookies can be sent along to Firebase, but it's your server, not the client, that's initiating the request on the server, and so is not associated with any given user.
My first thought was, in order to send authentication from the server, you'll need to have some sort of login on your own server; once you verify (with Firebase or otherwise) that the user is who they say they are, you can generate a token that you can save (securely) in the user's session and also send back to the client. Then, on the client, and on each server request, just before rendering your React application with React.render*, you would call authWithCustomToken() with that user's token.
The one caveat, however, is that authentication to a Firebase database is global—when you authenticate a Firebase ref (even in Node.js), every single other ref pointing to the same database gets authenticated with those credentials; you can't log in as different users by using separate refs. So, if your React rendering pipeline on the server does any asynchronous operations between when the auth callback is called and the app is rendered (e.g. if you use something like react-async or do other fancy async data loading before rendering), the user that is authenticated against your Firebase might have changed by the time you go to render your application. If, however, your rendering pipeline is purely synchronous, you should be able to get away with this strategy (getAuth() can help ensure that you have the right auth before you render).
Aside from that, I think the most straightforward solution is the following:
Authenticate your users through your own server, creating a secure token and passing it back to the client for authentication purposes. Store this token in the user's session so the client can request it and auth with it on the client as necessary. You'll also need to generate your own auth data (the stuff that is normally passed to the callback for authWithPassword) and store this in the session as well.
For server requests to your Firebase, use one of the recommended server authentication schemes:
Using a Firebase app secret: All authentication methods can accept a Firebase app secret instead of a JWT token. This will grant the server complete read and write access to the entire Firebase database. This access will never expire unless it is revoked via the App Dashboard.
Using a secure JWT with the optional admin claim set to true: This method will grant a server complete read and write access to the entire Firebase database. This token will expire normally, so it is important to set the expiration times accordingly.
Using a secure JWT designed to give access to only the pieces of data a server needs to touch: This method is more complicated, but it is the safest way to authenticate a server as it lets the Security and Firebase Rules prevent the server from doing anything it's not supposed to, even if it becomes compromised in some way.
Include server logic to ensure that the current logged in user can only access appropriate data. Since the above methods of authentication will grant access to data the user may or may not have access to, you'll need to take your own steps to ensure that users don't get accidental access to things they shouldn't.
Pass the auth data that you stored in the session in step one to the React application as a property, instead of relying on things like ref.getAuth() to get this data inside your React app (since it won't work on the server), to identify the user in your UI.

Resources