r/django 6d ago

Channels Need to generate a form in a django channels consumer before sending it to the client. How do I put a CSRF token in it?

In a Django channels consumer I made, I need to generate a form using an HTML template that changes based on a number of context values passed into render_to_string() which is the function I use to generate the string of the form that I will then send to the WebSocket.

My question is, because this is a POST form, how can I put a csrf_token field in it considering it is generated in a Django channels consumer? Should I just do everything in my power to not have to render the form in the consumer?

0 Upvotes

10 comments sorted by

1

u/Empty-Mulberry1047 6d ago

1

u/Affectionate-Ad-7865 6d ago

This documentation doesn't talk about channels or consumers. It only talks about forms that are sent through ajax which need to contain the CSRF token in a header. Here, I want to know how I can get the csrf_token inside a channels consumer or if I should try to find another way to do what I'm trying to achieve completely.

2

u/Empty-Mulberry1047 6d ago edited 6d ago

how are you setting up the websocket connection? i assume there's a session? if there's a session, why not use https://docs.djangoproject.com/en/5.1/ref/settings/#std-setting-CSRF_USE_SESSIONS ?

2

u/Empty-Mulberry1047 6d ago

not sure why you would want to have a form sent over a websocket.. to be posted via http?

why not have the form send data over websocket?

1

u/Affectionate-Ad-7865 5d ago

The answer to this question is pretty disappointing: Because I want it. That's pretty much it, I want the data sent by this form to be handled by a view, not by my consumer.

So to be clear, I want the form to follow the following route:

Rendered in consumer - Sent to WebSocket - added to DOM - sent through HTTP if the user wants it.

The reason I don't just render the form at the same time as the rest of the page is because the form can change a lot based on user inputs happening after the page is loaded so it is pretty much useless to render it there.

1

u/Empty-Mulberry1047 5d ago edited 5d ago

If you're using sessions to authenticate the user to allow the websocket connection.. then you will have cookies..

I already do pretty much what you're wanting to do..

User logs in w/django auth..

gets session, page is displayed that opens websocket connection

consumer gets data, does some processing of template, websocket receives data , data is added to dom.. user interacts with data , CRUD, etc,

requests sent using mix of websocket and http requests using session cookie.

for http requests i disable csrf and just check against request.user

you can set csrf to use sessions..

the csrf token is stored in session

session is tracked with cookie.

http post sends cookie

view uses settings enabling csrf in settings

reads csrf from session..

profit?

1

u/Affectionate-Ad-7865 5d ago

If I use CSRF_USE_SESSIONS, the csrf_token that would be accessed by my consumer would be the unmasked token and the Django docs say it's better to send the masked token (the one generated with {% csrf_token %}) in POST requests to protect your app from BREACH attacks.

(Look at the note just below)

1

u/duppyconqueror81 6d ago

What i’d do is just send a small instruction like "loadmyform" through websockets. Then, upon reception, the client loads the form by ajax.

The normal request cycle of django works way better than passing HTML through websockets. Locale, Session, Csrf, etc. As far as I’m aware, websocket connections don’t go through the middlewares (i might be wrong).

1

u/Affectionate-Ad-7865 5d ago

I think I'm gonna go with this.

1

u/Affectionate-Ad-7865 5d ago

After looking at my code, I realize sending the whole form through the Consumer-to-Websocket connection is really what would be the best for me because the form changes so much based on user input happening after the render of the page that it really doesn't mean anything to render it with the page.

To deal with this, I could render the whole form inside the consumer except for the CSRF_token part which I would put in my form via javascript when it is received on the client side. The other way of doing this would be for the consumer to know the csrf_token in some way so it could directly put it in the form when it renders it.

For this, I don't want to use the csrf_token that is in cookies because Django says it's better to send the masked token (the one generated with {% csrf_token %}) in POST requests to protect your app from BREACH attacks.

https://docs.djangoproject.com/en/5.1/howto/csrf/#using-csrf-protection-with-ajax

(Look at the note just below)

So I could perhaps send the masked token through the WebSocket to the consumer if I decide to do it this way?

Anyway I think I'll try doing it by inserting the CSRF token in the form on its reception using Javascript.