Repository navigation
Determine Best Auth Solution #828
Description
Current Situation
This stems from discussion in #768
As explained in https://github.com/phihos/idom-auth-example-sanic, the problem we need to solve is how to securely authenticate users inside an ReactPy single page application. Usually authentication is done via Cookie or Authorization header on each HTTP request. But after the websocket connection has been established, no future headers will be sent. However, there are some ways you could try to work around this:
-
The user could push some Javascript that sends a separate auth request to your auth API and then reloads the page to reestablish the websocket connection with new auth headers, but this is not an elegant solution. It defeats the purpose of ReactPy not having to write any Javascript and having a visible reload has a negative impact on the user-experience.
-
You could also render the login page traditionally and then redirect to a new page with embedded ReactPy . But then you already split your application into two parts: "pre-auth" with traditional server-side template rendering and "post-auth" with ReactPy. Keeping both parts consistent is probably not fun.
-
You can also do authentication inside the single page app and save the auth state via
use_state. But it will be gone as soon as a websocket disconnect happens. You can mitigate this by pushing some Javascript that sets a session cookie. But now there is a new problem: Session cookies should be set with theHttpOnlyflag to prevent XSS attacks from recovering the session cookie. This can not be done (or at least is difficult to do) with Javascript. So you might end up with a security flaw in your app. -
Since you have at least one full HTTP request-response cycle you can set a session cookie with a session ID on that response if the request does not already contain a cookie with a valid session ID. That ensures that the following request for the websocket connection always contains a session ID cookie. With
use_requestwe can extract the session ID and then the server can retrieve the session data. In that data we can look up the authentication state and let ReactPy display a login form or the actual content. We can later manipulate the session data to perform a login or logout. All without the need to set a further cookie or push Javascript - provided we implement a server-side session. A rough prototype for this has been implemented here based on work done in https://github.com/phihos/idom-auth-example-sanic.
Proposed Actions
Explore the viability of each option.
Activity
Archmonger commentedon Nov 1, 2022
As a note, Conreq currently uses approach 2.
Handling this will likely need us to dive deep into the authentication systems of each framework we support.
Archmonger commentedon Nov 3, 2022
Maybe the API will look something like this:
Archmonger commentedon Nov 4, 2022
Should we should beta test this as a standalone hook within
django-idom, and migrate it to core after IDOM hitsv1? Similar to how we are currently movinguse_location/use_websocket/etc hooks to core after first being developed indjango-idom.If so then I can draft a version of this fairly quickly.
[-]Make Auth Easier[/-][+]Determine Best Auth Solution[/+]rmorshea commentedon Nov 4, 2022
I updated the issue description here. I think we need to explore these options more deeply.
rmorshea commentedon Nov 4, 2022
I'm not really satisfied that option (4), the one @phihos implemented, is ideal. I'm worried that, by going with that approach, we'll basically have to reinvent the wheel for every auth mechanism under the sun( Oauth, JTW, etc). I'm also concerned that, not being a security expert myself, that we'll get something wrong.
On thinking about this further I actually think that a solution like option (1) is more viable than it might have seemed at the outset. The only real downside is that, if you try to do it with
html.script, you need to refresh the page. However, with a custom JS component, you wouldn't need a page refresh.I mocked up a very simple
<Request/>component that's compatible with IDOM's custom JS interface:You could then embed this invisible component into your view to tell the client to hit some URL and trigger the
onResponsecallback with the data.This would effectively allow you to use a standard auth flow.
rmorshea commentedon Nov 4, 2022
I've tried the above out and it seems to work.
3 remaining items
Archmonger commentedon Nov 5, 2022
I'm still not convinced this is a 1.0 milestone though, we're getting pretty deep into scope creep for the 1.0 release.
rmorshea commentedon Nov 5, 2022
The scope of this issue is mostly focused on finding the best option. Not necessarily on implementing it. I'd at least like to have a prepared answer if someone asks and ideally have something documented.
rmorshea commentedon Nov 5, 2022
I think you're right though. Most people who initially adopt IDOM are probably going to be making internal facing services that don't need app level auth.
ajvogel commentedon Jun 7, 2023
Would using one of the backend auth libraries (such as flask-login) not work?
Archmonger commentedon Jun 7, 2023
@ajvogel I'm not familiar with that specific library, but it depends on
use_scopehook)Feel free to test it out and let us know.
rmorshea commentedon Jun 7, 2023
@ajvogel, I suspect that
flask-loginmight not help here unfortunately.flask-loginwould basically require solution number 2. In the long-run I think we're probably going to run with solution number 4.rmorshea commentedon Jun 7, 2023
@ajvogel, if this is blocking for you, can you upvote this issue? It will help us prioritize what we should work on.
Archmonger commentedon Jul 28, 2023
I'm realizing we now have a new solution open to us given our "ReactPy as middleware" concept.
Our auth solution can be implemented similar to the Django Channels AuthMiddleware, which effectively just adds a
scope['user']database model to the scope.That user object should have log out and log in methods attached to it.
rmorshea commentedon Jul 28, 2023
That seems like more of a new interface than an implementation. Determining the identity of the user still requires one of the options list above.
ipcloudlive commentedon Dec 30, 2023
We Need some thing like Cookie or LocalStorage or SessionStorage to solve auth problem
Expecting the solution before New Year
Archmonger commentedon Feb 23, 2024
I will be exploring authentication within
ReactPy-Django, using that as our ground-zero for testing out authentication methods. This will require theuse_messengerhook to get merged in.My cursory exploration tells me this will get really messy for adding support to every possible backend. It's only barely possible due to how Django is extremely batteries-included.
The future of authentication for ReactPy Core likely involves giving some barebones instructions on how to use JavaScript (#894 and #1001) to accomplish auth.