Hi guys, question for y’all.
Any help would be much appreciated, thanks.
It looks like a job for Drab!
It just does what you want to archive, runs Elixir on the server from the event in a browser. For now, you can’t run elixir and JS in the same event, but it is in the todo and will be introduced in the future.
I’ve previously attempted using Drab for live reloading form elements, however it doesn’t seem to be compatible with form_for, and the only workaround is to create the form using html only, thus removing csrf protection, which isn’t appropriate for my purposes
I’m leaving the comment here but I just realised this doesn’t really apply since you’ll be short-circuiting the request with drab which obviously doesn’t go through the regular request response-cycle
just ignore it
CSRF is just an hidden input that phoenix automatically injects in form_for - if that’s the single reason why you don’t want to use it, you can always add it by yourself using
When using a router pipeline with the
CSRFProtection plug it will check the CSRF token that is sent with the request (e.g. the automatically injected hidden field in form_for, or a manually added header to an ajax request, or a manually added input to a form submission) against the token in session. This prevents cross-site request forgery because the token is created on your server and then placed on your page, so an external source won’t have access to it, unless they also have access to the content of your page, (through XSS - cross-site scripting - which can happen if you’re saving and displaying user input without sanitising it (phoenix sanitises all rendered content by default so unless you disable/render your own thing bypassing that, this won’t be a problem))
Yes, there was an issue with form_for and csfr, but only when you replace the whole form with Drab. See https://github.com/grych/drab/issues/67
The early version of Drab.Live was not very aware where the assign lives, and for code like:
<%= function1(@assign1) do %>
<%= function2(@assign2) %>
When you update @assign2, it refreshed the whole expression with function1.
Now this issue is resolved. So such construction:
<%= form_for(@conn, ...) do %>
<%= input_tag(@assign1) %>
works as expected: updating @assign1 only refreshes the small portion of the form (input_tag), leaving the rest, so hidden with csfr token as well, untouched.
But, if you refresh the whole form_for, phoenix will generate the new token, so submitting it shows the token error. Solution? Avoid update the whole form.
Also, this issue shows only when you are using
Drab.Live (living assigns part). For what you want to archive - running Elixir from the event from the browser - you don’t have to use living assigns. Actually you don’t have to use any Drab Module
Hmm I just realized I could very easily build csfr token check into Drab.
When the event is launched from some DOM node, client-side Drab is checking if the node is inside the
<form>. If yes, it is taking all inputs from the form and sends it to the Elixir event handler function inside the
sender argument. So the token is already there, we only need to check it.
If the token is not present (form was not generated by form_for), do nothing - do not attempt anything checks.
Do you think it is worth the effort? I think so. It will push Drab even closer to standard Phoenix behaviour.
That’s exactly the news I wanted to hear (funny enough I tried a suggestion from another of my threads the day before this update came out! - Refreshing dropdown list without data loss). I shall have a play with the newer version and see if I can achieve what I need, thanks for the help!
Thanks for the suggestion! I probably won’t be implementing my own CSRF (I’ll have another look at the updated Drab module), however the automatic XSS sanitization while be very helpful from a security aspect, as this is a final year project i’m working on.
Just did a quick test, and can confirm it’s working perfectly how I want it. Thank you very much, I’ve been stuck on this for 2 weeks!
But now I’m confused - I’m not familiar with drab’s inner workings - my comment was assuming this particular use case:
- regular form
- include the csrf token as an hidden field
- user clicks button, drab gets called
- does something on the backend
- passes that to the front-end
- then the form would be at some point submitted regularly (not through drab)
but since you can use form_for with drab (As per your comment) that’s not needed?
With Drab, form is actually not needed (but still useful for collecting data from it in one shot). Think about it as the regular (not web) program, where controller (which I call commander here) has just a free access to the UI. It does not need a form, while it can read the text from the text input directly.
But form might be useful, because when the event is raised from the element inside any form (regardless of action etc), it sends all form inputs to the event handler function, so you don’t need to keep asking the UI for individual values.
The issue with csrf token is when you update, via Drab.Live, the whole form on the page. Phoenix’s form_for function will generate a new token so when you submit (with regular submit!), you will get an error.