Form Basics and CSRF Protection
In this lesson, we'll cover the basics of working with HTML forms in AdonisJS and how they incorporate Cross-Site Request Forgery (CSRF) protection via AdonisJS Shield.
- Author
- Tom Gobich
- Published
- Feb 17
- Duration
- 6m 13s
Developer, dog lover, and burrito eater. Currently teaching AdonisJS, a fully featured NodeJS framework, and running Adocasts where I post new lessons weekly. Professionally, I work with JavaScript, .Net C#, and SQL Server.
Adocasts
Burlington, KY
Transcript
Form Basics and CSRF Protection
-
[music]
-
Let's go ahead and introduce forms in a very basic state
-
by rigging one up for our Redis FlushDB button.
-
Up till now, that button doesn't really do much.
-
So we can click on it, and it's just not going to do anything.
-
We went to a hash, but that's about it.
-
So we can get rid of that.
-
And let's go ahead and hide our browser back away,
-
and let's dive back into our home page.
-
I'm going to go ahead and close out some of these files that we're not using anymore,
-
and let's scroll down to that button.
-
So currently we have it as a button.
-
It's an href, just going to a hash, as we saw within the browser when we clicked it.
-
In the most basic state for forms,
-
we're going to want to wrap the button in a form,
-
just like so, and we can indent this.
-
We rigged our button component up to where whenever we pass it as an href,
-
it will be applied as an anchor,
-
and any time href is not added, it will be rendered as a button.
-
So what we can do is just get rid of the href,
-
and now our button's a button,
-
and we can specify the type as "submit"
-
so that now whenever we click our button,
-
that will automatically submit our form.
-
If we give this a save, jump back into our browser,
-
we can verify that because the behavior is now going to change.
-
If we give this button a click, our page will do a refresh.
-
As you can see, our image is just re-rendered,
-
and now we're at a question mark page.
-
Let's get rid of that real quick.
-
Okay, cool. Let's hide this back once more.
-
What we need is for a URL for our form to act against
-
to perform some action for the form's posting.
-
So for that, we can define an action.
-
This action is going to need a URL that we have defined within a route.
-
If we dive into our routes, I'm going to hold Command or Ctrl+P
-
to open up our file explorer. We can dive into our routes file,
-
and right down here we have our router.delete,
-
and we particularly want to use our flush method,
-
so that's redis.flush.
-
So let's dive back into our home page,
-
and let's add in an action for route redis.flush, just like so.
-
One last thing that we need to do, though, is specify a method.
-
For HTML forms, this method is going to be "post."
-
By default, HTML forms aren't going to support
-
delete, put, patch, like we have the ability to define within our routes.
-
So we have this route defined as a delete,
-
but we need it to be a post in order for our HTML form
-
to actually be able to use this.
-
So we'll see in the next lesson how we can keep this as delete,
-
but for right now, let's switch this to post so that we can move onward.
-
Let's dive back into our home page, and let's give everything a save.
-
Let's dive back into our browser, let's open up our inspector,
-
and let's dive into the network tab.
-
Let's switch our network tab over to all,
-
and we're also going to want to go into the gear icon and persist our logs.
-
Whenever we click the flush redisdb button,
-
our page will do a refresh and our log will clear out by default,
-
but with that persist logs on,
-
we'll still be able to see and inspect our network requests
-
as we refresh our page.
-
Cool. So now we can go ahead and click our flush redisdb button,
-
and just like so, we see a bunch of requests go out,
-
but if we scroll up to the top, we should see the initial request that went out
-
that caused all of these subsequent requests from our page refreshing.
-
And that's a post request to our flush method.
-
If we dive into that, we can see that we got back a 302 found.
-
Within the response headers, we can scroll down a little bit further,
-
and we can see that it sent back a redirect location to slash,
-
so our home page, essentially redirecting us back to where we were.
-
If we take a look at the request, we didn't send any information.
-
If we take a look at the response, it's our page. Cool.
-
So we can hide that away.
-
Looking at our network request, everything appears to have worked properly,
-
but if we dive into our terminal,
-
we should see an invalid or expired CSRF token warning
-
for this particular request.
-
Furthermore, if we dive back into our text editor,
-
let's scroll up to our controllers, and let's go into our redis controller,
-
particularly within our flush method,
-
as this is the method that we're calling with our form.
-
If we add a console.log flushing redis database here,
-
we'll give that a save.
-
Let's jump back into our browser, and let's click our button once more.
-
We see the exact same behavior, everything refreshed.
-
If we scroll down, we'll see another flush request go out right about here,
-
and if we dive back into our terminal,
-
we're going to see that invalid or expired CSRF token warning again,
-
but we're also not going to see that console.log that we have.
-
So we're never actually reaching our flush route handler right here,
-
and our cache is never actually getting flushed
-
because of that CSRF warning.
-
CSRF stands for cross-site request forgery,
-
and it's a protection system provided by AdonisJS Shield
-
to prevent unauthorized submissions of forms
-
on behalf of authenticated users within our application.
-
Whenever you're working with session-based applications,
-
CSRF protection is a must-have to help protect your users
-
and your application from unauthorized access
-
to that authenticated user session
-
so that the forms cannot be submitted on that user's behalf by something else.
-
And if we dive into our config shield file,
-
we'll see a CSRF section within here,
-
and by default, whenever we create a new web starter kit application,
-
this is going to be enabled, and this will apply for methods
-
that can post information to our application,
-
like post, put, patch, and delete.
-
So everything is actually working exactly as we expect it.
-
There's just one thing that we need to do
-
to actually make this work within our application.
-
So if we dive back into our home page, back into our form,
-
all that we need to do is specify a CSRF token within our form,
-
and AdonisJS will both set and check that token for us
-
to verify that it is an authorized action being performed.
-
We can easily provide in a CSRF token within our form
-
by doing double curly braces and calling the CSRF field function,
-
just like so.
-
This will automatically apply in an input that's hidden within our form
-
with a CSRF token as its value.
-
So we can give this a save.
-
Let's jump back into our browser, and let's inspect our page.
-
So we can dive into our div, we can dive into our fixed element,
-
and dive into our form, and sure enough,
-
there is that hidden input called _CSRF
-
with our CSRF token value within it.
-
Let's dive back into our network tab,
-
and let's clear our current log out so that it's a little bit easier to work with,
-
and let's attempt to flush our Redis DB one more time.
-
Okay, we saw everything go out, and it behaved the exact same as it did before.
-
If we inspect this request and take a look at the request data,
-
we're now going to see a CSRF token being sent up with the form data
-
so that AdonisJS can check it and verify it's valid.
-
And furthermore, if we inspect our terminal,
-
we're going to see that that CSRF token warning is no longer there,
-
and we indeed did flush our Redis database.
-
Cool, so everything's now working exactly as we expected,
-
now that we're providing in that CSRF token with our form submission.
-
Introduction
-
Fundamentals
-
2.0Routes and How To Create Them5m 23s
-
2.1Rendering a View for a Route6m 29s
-
2.2Linking Between Routes7m 51s
-
2.3Loading A Movie Using Route Parameters9m 17s
-
2.4Validating Route Parameters6m 6s
-
2.5Vite and Our Assets6m 38s
-
2.6Setting Up Tailwind CSS9m 5s
-
2.7Reading and Supporting Markdown Content4m 32s
-
2.8Listing Movies from their Markdown Files8m 51s
-
2.9Extracting Reusable Code with Services7m 4s
-
2.10Cleaning Up Routes with Controllers4m 52s
-
2.11Defining A Structure for our Movie using Models9m 38s
-
2.12Singleton Services and the Idea of Caching6m 11s
-
2.13Environment Variables and their Validation4m 16s
-
2.14Improved Caching with Redis10m 44s
-
2.15Deleting Items and Flushing our Redis Cache6m 46s
-
2.16Quick Start Apps with Custom Starter Kits6m 28s
-
2.17Easy Imports with NodeJS Subpath Imports8m 40s
-
-
Building Views with EdgeJS
-
3.0EdgeJS Templating Basics8m 49s
-
3.1HTML Attribute and Class Utilities6m 9s
-
3.2Making A Reusable Movie Card Component10m 24s
-
3.3Component Tags, State, and Props4m 53s
-
3.4Use Slots To Make A Button Component6m 56s
-
3.5Extracting A Layout Component5m 13s
-
3.6State vs Share Data Flow2m 59s
-
3.7Share vs Global Data Flow6m 7s
-
3.8Form Basics and CSRF Protection6m 13s
-
3.9HTTP Method Spoofing HTML Forms3m 3s
-
3.10Easy SVG Icons with Edge Iconify7m 57s
-
-
Database and Lucid ORM Basics
-
4.0Configuring Lucid and our Database Connection4m 3s
-
4.1Understanding our Database Schema9m 35s
-
4.2Introducing and Defining Database Migrations18m 35s
-
4.3The Flow of Migrations8m 28s
-
4.4Introducing Lucid Models5m 43s
-
4.5Defining Our Models6m 49s
-
4.6The Basics of CRUD11m 56s
-
4.7Defining Required Data with Seeders11m 11s
-
4.8Stubbing Fake Data with Model Factories13m 48s
-
4.9Querying Our Movies with the Query Builder15m 30s
-
4.10Unmapped and Computed Model Properties3m 24s
-
4.11Altering Tables with Migrations7m 6s
-
4.12Adding A Profile Model, Migration, Factory, and Controller2m 57s
-
4.13SQL Parameters and Injection Protection9m 19s
-
4.14Reusable Query Statements with Model Query Scopes8m 11s
-
4.15Tapping into Model Factory States9m 15s
-
4.16Querying Recently Released and Coming Soon Movies4m 59s
-
4.17Generating A Unique Movie Slug With Model Hooks7m 59s
-
-
Lucid ORM Relationships
-
5.0Defining One to One Relationships Within Lucid Models5m 49s
-
5.1Model Factory Relationships2m 54s
-
5.2Querying Relationships and Eager Vs Lazy Loading5m 17s
-
5.3Cascading and Deleting Model Relationships5m 16s
-
5.4Defining One to Many Relationships with Lucid Models6m 56s
-
5.5Seeding Movies with One to Many Model Factory Relationships5m 24s
-
5.6Listing A Director's Movies with Relationship Existence Queries8m 41s
-
5.7Listing and Counting a Writer's Movies8m 41s
-
5.8Using Eager and Lazy Loading to Load A Movie's Writer and Director5m 18s
-
5.9Defining Many-To-Many Relationships and Pivot Columns9m 48s
-
5.10Many-To-Many Model Factory Relationships4m 50s
-
5.11A Deep Dive Into Relationship CRUD with Models18m 5s
-
5.12How To Create Factory Relationships from a Pool of Data13m 55s
-
5.13How To Query, Sort, and Filter by Pivot Table Data9m 47s
-
-
Working With Forms
-
6.0Accepting Form Data12m 15s
-
6.1Validating Form Data with VineJS9m 29s
-
6.2Displaying Validation Errors and Validating from our Request7m 16s
-
6.3Reusing Old Form Values After A Validation Error2m 3s
-
6.4Creating An EdgeJS Form Input Component5m 28s
-
6.5Creating A Login Form and Validator5m 1s
-
6.6How To Create A Custom VineJS Validation Rule9m 7s
-
-
Authentication & Middleware
-
7.0The Flow of Middleware7m 49s
-
7.1Authenticating A Newly Registered User4m 14s
-
7.2Checking For and Populating an Authenticated User2m 10s
-
7.3Logging Out An Authenticated User2m 24s
-
7.4Logging In An Existing User6m 54s
-
7.5Remembering A User's Authenticated Session6m 55s
-
Protecting Routes with Auth, Guest, and Admin Middleware5m 36s
-
-
Filtering and Paginating Queries
-
Creating A Movie List Page3m 43s
-
Filtering A Query By Pattern Likeness7m 9s
-
Filtering Our List by Movie Status5m 47s
-
How To Apply A Dynamic Sort Filter To Your Query7m 12s
-
Joining SQL Tables To Order By A Related Column4m 49s
-
Validating Query String Filter Values7m 23s
-
How To Paginate Filtered Query Results9m 15s
-
Pagination First, Last, Next, and Previous Buttons4m 2s
-
Join The Discussion! (4 Comments)
Please sign in or sign up for free to join in on the dicussion.
frp
Are you going to do anything with form validation? I'm trying to write a custom rule for Vine and the docs don't seem to say what the function should return. In Laravel a validation function returned true or false but I cannot figure it out from the example they use in the Vine docs.
Please sign in or sign up for free to reply
tomgobich
Yeah, validation will come with module 7, which focuses on form flow. The database modules are the last large modules, so things should start flowing quickly thereafter :)
Anything that doesn't report an error is considered valid. So, to walk through the example within Vine's documentation:
Please sign in or sign up for free to reply
frp
Ok, so the return earlier is just passing on to the next validator, and unless you have that field.report() method, you are good. Thanks, that makes sense.
Please sign in or sign up for free to reply
tomgobich
Yep, exactly!! :) Anytime!!
Please sign in or sign up for free to reply