Skip to content
Permalink
40af2df39f
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
105 lines (68 sloc) 9.44 KB
# Forms and Authentication
In this worksheet you will be learning how to build valid html5 forms and how these can be used to build secure dynamic websites. By the end of this you will have a good understanding of how to develop a secure website.
Locate the code in the `exercises/02_forms_auth/` directory, install the dependencies and start the server. If you attempt to navigate to the base url. You should see a message requesting you to log in.
If you examine the URL you will see that this has changed to:
```
/login?msg=you%20need%20to%20log%20in
```
Because you are not logged in, the server is redirecting the browser to the login page.
There are two forms (login and register) and each has a POST route to process the data. At the end of each of the POST scripts the browser will be redirected to the appropriate page.
Now try accessing the `/register` route, this should display a registration form where a site visitor can create an account. Try creating an account then returning to the home screen and logging in.
Before attempting the exercises below, make sure you read through the JavaScript code in the `index.js` file and that you understand the html in the file in the `views/` directory.
### 2.1 Test Your Understanding
1. Create a link on the log in screen to take the user to the registration screen and a link there to take the user back to the log in screen.
2. Create a **Log Out** link on the homepage. If a user clicks on this it should take them to the `/logout` route which will log them out. HINT: you will need to delete the session key!
3. Currently all uploaded files are saved as `avatar.png` in the `public/avatars/` directory which means when a new user creates their account the avatar will overwrite the previous one. Modify the code so that the name of the avatar matches the username. How will you decide what file extension to use?
4. The registration form should only allow each username to be assigned once. Modify the script so that if a new user chooses a username that is already in use they should be prompted to choose another one. The form should display the message **Username already in use**.
5. The registration code allows a user to create an account with a blank username and/or password. Modify the code to prevent this. The form should be displayed with a message informing the user which field needs correcting. Make sure you pass the form data back to the form and display this.
6. The server throws an error if the user fails to choose a profile picture. This should be optional so fix the code so that the error is not thrown.
7. When logging in, if the password field is left blank the user is automatically allowed to log in! Improve the security by checking the password field and, if it is blank, redirecting the user back to the login screen displaying a the message **Invalid Password**.
## 3 HTML5 Forms
The website currently has two forms, one handles user registration and the other logging in. Both send their data using the POST method which means the server must provide POST routes to process the data. If you examine the registration form you will see that there is an `enctype` attribute. This stands for _encoding type_. The enctype attribute specifies how the form-data should be encoded when submitting it to the server. In this form it is set to `multipart/form-data`. This value is required when you are using forms that have a file upload control.
### 3.1 Test Your Understanding
1. The forms are not using valid html5 labels. Fix this and use the HTML5 validator to check the job has been done correctly.
2. It would be useful to ask for the user's actual name. Modify the form to request this and change the back-end database and code to ensure this is stored.
3. We want to know which country the user is from and store this as a two digit country code such as `GB`. Create a dropdown list using the [`select`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select) element which will allow the user to choose their country name, use the data in the `countries.json` file and import this using the `fs.readfile()` function which returns a promise. Make sure you update the database schema to include this additional field.
4. A user should be able to view and change their profile information. Create a new page based on a new template called `profile.handlebars`. This should populate a form with the user information and, if the user changes this and clicks on an **Update** button should update their record in the database. There should be a link to this on the homepage.
1. The page should display the user's profile picture but also give them the option of uploading a replacement image.
## 4 Authentication and Authorisation
When you build a dynamic website there is a requirement for users to be able to access online resources in a controlled manner. We need to consider:
1. Authentication: can a user get access to the site (log in).
2. Authorisation: when they are logged in, what access rights will a user have.
Because http is stateless, it has no way to remember whether someone is logged in or not and what permissions they have. To solve this problem, a web server can send some data to be stored in the web browser, these are known as cookies.
All cookies should be encrypted and only be accessible by the website that created them. Every time a user makes a request to the site they are sent in the HTTP headers. The server can also delete any cookies is has created.
In this website we are using cookies to flag whether a user is logged in. The home page is secure, it checks for the presence of a suitable cookie and, if it does not find one it redirects the user to the log in page. You can see this on line 37. The login page checks the username and password and, if they match creates a cookie called `authorised` and setits value to `true` (line 94).
If the cookie is found and set to true we know they have already logged in and can provide access to the secured section of the site. When the user clicks on the log out button we simply delete the cookie (line 102) and send them to the home page.
Start the web server and open the homepage in the Chrome browser. Open the developer tools, select the **Network** tab and refresh the page. Select the html page as shown and study the request headers, notice that there is no cookie.
![headers without cookies](exercises/.images/cookies_01.png)
If you now log in you will be directed back to the homepage. If you study the request headers you will see it now includes a cookie. This contains a hash that contains all the data we have stored. It is created by the following steps:
session data is encoded into JSON, then into base64, then attached to a cookie. Locate this string which looks something like this, the second line shows the data part:
```
koa:sess=eyJhdXRob3Jpc2VkIjp0cnVlLCJfZXhwaXJlIjoxNTUxNzMyODA5NjU1LCJfbWF4QWdlIjo4NjQwMDAwMH0=
eyJhdXRob3Jpc2VkIjp0cnVlLCJfZXhwaXJlIjoxNTUxNzMyODA5NjU1LCJfbWF4QWdlIjo4NjQwMDAwMH0=
```
Now copy the base64 encoded string and paste it into a website that can decode base64 data. You will end up with something like this:
```
{"authorised":true,"_expire":1551732809655,"_maxAge":86400000}
```
As you can see, the value we set is directly stored in the cookie. As you are now probably aware, cookies are not the most secure way to store session data!
So how can we improve this security? The simplest way is to use AES256-GCM encryption on the data. Luckily there is a module called [koa-encrypted-session](https://www.npmjs.com/package/koa-encrypted-session) that makes it easy to implement. It is recommended that you use the unencrypted sessions to help debug your code but switch to an encrypted cookie on the production server.
### 4.1 Test Your Understanding
In this section we will be implementing more granular authorisation by creating an admin-only area on the website:
1. Create a secure admin page:
1. Add an extra boolean field to the database called `admin` with a default value of `false`.
2. Manually modify one of your registered accounts and change the stored value to `true`, this will be your admin user.
3. When a user logs in, check this field and, if it is set to true, create a new session variable called `admin` and assign it a value of `true`.
4. Create a new admin page, `admin.handlebars` and make sure it is protected in the same way as the home page.
5. Create a link to this new page from the homepage.
6. Modify the page so that, if the user does not have admin authorisation they get redirected back to the home page which should display a message **You do not have admin privileges!**.
2. Modify the secure pages (home and admin) so that they displays the username of the currently logged-in user:
1. Start by modifying the `post('login')` callback, adding a second key called `username` to the `ctx.session` object to store the username.
2. Modify the code that logs the user out to remove this key.
3. Now change the callback for the secure home page so that it sends this data to the handlebars template.
4. Finally create a placeholder in the template to display this information.
## Advanced Topics
There are a number of advanced topics on the subject of authentication and authorisation. You may wish to consider:
1. [Sending emails using NodeJS](https://codeburst.io/implementing-nodemailer-5-min-de2d2c781d6b)
2. [Account verification](https://stackoverflow.com/questions/39092822/how-to-do-confirm-email-address-with-express-node)
3. [Password reset](http://sahatyalkabov.com/how-to-implement-password-reset-in-nodejs/)