-
-
Notifications
You must be signed in to change notification settings - Fork 323
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Determine Best Auth Solution #828
Comments
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. |
Maybe the API will look something like this: @component
def my_component():
auth_manager = hooks.use_auth()
# Returns a IDOM session dataclass that contains any auth data, such the active user
print(auth_manager.session)
# Direct pass-through to the backend framework's authentication system
# but also sets cookies within the client. This will likely require us to allow kwargs.
auth_manager.login(username="username", password="password")
# Direct pass-through to the backend framework's authentication system.
# Will also need to invalidate cookies within the client
auth_manager.logout()
# Attempts to re-validate whether the user is currently logged in (cookies still valid)
# and returns a boolean. This is needed due to the difference between HTTP response cycles
# versus websocket persistent connections
auth_manager.validate_session() |
Should we should beta test this as a standalone hook within If so then I can draft a version of this fairly quickly. |
I updated the issue description here. I think we need to explore these options more deeply. |
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 I mocked up a very simple // request.js
export function bind(node, config) {
return {
create: (component, props) => () => component(props),
render: () => undefined,
unmount: () => undefined,
};
}
export function Request(props) {
fetch(props.request.url, props.request.info)
.then((response) => response.text())
.then(props.onResponse)
.catch(props.onError);
} # request.py
from idom import component
from idom.web.module import module_from_file, export
@component
def request(url, body, method, on_response):
return _request_component(
{
"onResponse": on_response,
"request": {
"url": url,
"info": {"body": body, "method": method},
},
}
)
_request_module = module_from_file(name="request", file="request.js")
_request_component = export(_request_module, "Request") You could then embed this invisible component into your view to tell the client to hit some URL and trigger the from idom import component
from idom.backend.fastapi import configure
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from hacky_request_component import request
app = FastAPI()
@app.post("/example")
def example():
return JSONResponse("hello", headers={"AuthToken": "fake"})
@component
def temp():
return request(url="/example", body=None, method="POST", on_response=print)
configure(app, temp) This would effectively allow you to use a standard auth flow. |
I've tried the above out and it seems to work. |
However, that doesn't really provide components the user's authentication information, nor does it give a way to login/logout/validate a user session within IDOM components. Feels a bit disjointed to me. |
Ok, so I did a bit more reading and I found that option (4) is an established pattern. There are examples of server-side session managers for frameworks like Flask via databases like Redis. This makes me more confident that we could go with that approach without completely reinventing things. |
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. |
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. |
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. |
Would using one of the backend auth libraries (such as flask-login) not work? |
@ajvogel I'm not familiar with that specific library, but it depends on
Feel free to test it out and let us know. |
@ajvogel, if this is blocking for you, can you upvote this issue? It will help us prioritize what we should work on. |
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 That user object should have log out and log in methods attached to it. |
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. |
We Need some thing like Cookie or LocalStorage or SessionStorage to solve auth problem |
I will be exploring authentication within 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 so 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. |
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 further HTTP requests and therefore no further headers will be sent. However, there are some ways you could try to work around this:
You could push some Javascript via
html.script
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 kinda ugly. 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 theHttpOnly
flag 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_request
we 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.
The text was updated successfully, but these errors were encountered: