parent
430b78f8ae
commit
9727a3daae
4 changed files with 218 additions and 3 deletions
@ -0,0 +1,199 @@ |
||||
HTTP is the internet protocol that standardizes how clients and servers interact with each other. When you open a website, among other things, HTTP is the protocol that helps load the website in the browser. |
||||
|
||||
## HTTP is Stateless |
||||
HTTP is a stateless protocol which means that each request made from the client to the server is treated as a standalone request; neither the client nor the server keeps track of the subsequent requests. Sessions allow you to change that; with sessions, the server has a way to associate some information with the client so that when the same client requests the server, it can retrieve that information. |
||||
|
||||
In this guide, we will learn what is Session-Based Authentication and how to implement it in Node.js. We also have a separate [visual guide on Session-Based Authentication](/guides/session-authentication) as well that explains the topic visually. |
||||
|
||||
## What is Session-Based Authentication? |
||||
Session-based authentication is a stateful authentication technique where we use sessions to keep track of the authenticated user. Here is how Session Based Authentication works: |
||||
|
||||
* User submits the login request for authentication. |
||||
* Server validates the credentials. If the credentials are valid, the server initiates a session and stores some information about the client. This information can be stored in memory, file system, or database. The server also generates a unique identifier that it can later use to retrieve this session information from the storage. Server sends this unique session identifier to the client. |
||||
* Client saves the session id in a cookie and this cookie is sent to the server in each request made after the authentication. |
||||
* Server, upon receiving a request, checks if the session id is present in the request and uses this session id to get information about the client. |
||||
|
||||
And that is how session-based authentication works. |
||||
|
||||
## Session-Based Authentication in Node.js |
||||
Now that we know what session-based authentication is, let's see how we can implement session-based authentication in Node.js. |
||||
|
||||
Please note that, for the sake of simplicity, I have intentionally kept the project strictly relevant to the Session Based Authentication and have left out a lot of details that a production-ready application may require. Also, if you don't want to follow along, project [codebase can be found on GitHub](https://github.com/kamranahmedse/node-session-auth-example). |
||||
|
||||
First things first, create an empty directory that will be holding our application. |
||||
|
||||
```shell |
||||
mkdir session-auth-example |
||||
``` |
||||
Now run the following command to setup a sample `package.json` file: |
||||
```shell |
||||
npm init -y |
||||
``` |
||||
Next, we need to install the dependencies: |
||||
```shell |
||||
npm install express express-session |
||||
``` |
||||
`Express` is the application framework, and `express-session` is the package that helps work with sessions easily. |
||||
|
||||
### Setting up the server |
||||
Now create an `index.js` file at the root of the project with the following content: |
||||
|
||||
```javascript |
||||
const express = require('express'); |
||||
const sessions = require('express-session'); |
||||
|
||||
const app = express(); |
||||
|
||||
app.use(sessions({ |
||||
secret: "some secret", |
||||
cookie: { |
||||
maxAge: 1000 * 60 * 60 * 24 // 24 hours |
||||
}, |
||||
resave: true, |
||||
saveUninitialized: false, |
||||
})); |
||||
|
||||
app.use(express.json()); |
||||
app.use(express.urlencoded({extended: true})); |
||||
|
||||
// @todo register routes |
||||
|
||||
app.listen(3000, () => { |
||||
console.log(`Server Running at port 3000`); |
||||
}); |
||||
``` |
||||
The important piece to note here is the `express-session` middleware registration which automatically handles the session initialization, cooking parsing and session data retrieval, and so on. In our example here, we are passing the following configuration options: |
||||
* `secret`: This is used to sign the session ID cookie. Using a secret that cannot be guessed will reduce the ability to hijack a session. |
||||
* `cookie`: Object containing the configuration for session id cookie. |
||||
* `resave`: Forces the session to be saved back to the session store, even if the session data was never modified during the request. |
||||
* `saveUninitialized`: Forces an "uninitialized" session to be saved to the store, i.e., saves a session to the store even if the session was not initiated. |
||||
|
||||
Another important option is `store` which we can configure to change how/where the session data is stored on the server. By default, this data is stored in the memory, i.e., `MemoryStore`. |
||||
|
||||
Look at the [express-session documentation](https://github.com/expressjs/session) to learn more about the available options. |
||||
|
||||
### Creating Handlers |
||||
Create a directory called the `handlers` at the project's root. This is the directory where we will be placing all the route-handling functions. |
||||
|
||||
Now let's create the homepage route, which will show the welcome message and a link to log out for the logged-in users and redirect to the login screen for the logged-out users. Create a file at `handlers/home.js` with the following content. |
||||
|
||||
```javascript |
||||
module.exports = function HomeHandler(req, res) { |
||||
if (!req.session.userid) { |
||||
return res.redirect('/login'); |
||||
} |
||||
|
||||
res.setHeader('Content-Type', 'text/HTML) |
||||
res.write(` |
||||
<h1>Welcome back ${req.session.userid}</h1> |
||||
<a href="/logout">Logout</a> |
||||
`); |
||||
|
||||
res.end() |
||||
} |
||||
``` |
||||
|
||||
At the top of this function, you will notice the check `req.session.userid`. `req.session` is automatically populated using the session cookie by the `express-session` middleware that we registered earlier. `req.session.userid` is one of the data fields that we will set to store the `userid` of the logged in user. |
||||
|
||||
Next, we need to register this handler with a route. Open the `index.js` file at the root of the project and register the following route: |
||||
|
||||
```javascript |
||||
const HomeHandler = require('./handlers/home.js'); |
||||
|
||||
app.get('/', HomeHandler); |
||||
``` |
||||
|
||||
Next, we have the login page, redirecting the user to the home screen if the user is logged in or showing the login form. Create a file at `handlers/login.js` with the following content: |
||||
|
||||
```javascript |
||||
module.exports = function LoginHandler(req, res) { |
||||
if (req.session.userid) { |
||||
return res.redirect('/'); |
||||
} |
||||
|
||||
res.setHeader('Content-Type', 'text/HTML) |
||||
res.write(` |
||||
<h1>Login</h1> |
||||
<form method="post" action="/process-login"> |
||||
<input type="text" name="username" placeholder="Username" /> <br> |
||||
<input type="password" name="password" placeholder="Password" /> <br> |
||||
<button type="submit">Login</button> |
||||
</form> |
||||
`); |
||||
|
||||
res.end(); |
||||
} |
||||
``` |
||||
Again, at the top of the function, we are simply checking if we have `userid` in the session (which means the user is logged in). If the user is logged in, we redirect them to the homepage; if not, we show the login screen. In the login form, we have the method of `post`, and we submit the form to `/process-login`. Please note that, for the sake of simplicity, we have a simple HTML string returned in the response, but in a real-world application, you will probably have a separate view file. |
||||
|
||||
Let's first register this page and then implement `/process-login` endpoint. Open the `index.js` file from the root of the project and register the following route: |
||||
|
||||
```javascript |
||||
const LoginHandler = require('./handlers/login.js'); |
||||
|
||||
app.get('/login', LoginHandler); |
||||
``` |
||||
|
||||
Next, we have to implement the functionality to process the login form submissions. Create a file at `handlers/process-login.js` with the following content: |
||||
|
||||
```javascript |
||||
module.exports = function processLogin(req, res) { |
||||
if (req.body.username !== 'admin' || req.body.password !== 'admin') { |
||||
return res.send('Invalid username or password); |
||||
} |
||||
|
||||
req.session.userid = req.body.username; |
||||
|
||||
res.redirect('/'); |
||||
} |
||||
``` |
||||
|
||||
As you can see, we are simply checking that the username and password should both be `admin` and `admin` for a user to authenticate successfully. Upon finding valid credentials, we set the `userid` in the session by updating `req.session.userid`. Similarly, you can set any data in the session. For example, if we wanted to store the user role, we would do the following: |
||||
|
||||
```javascript |
||||
req.session.role = 'admin' |
||||
``` |
||||
|
||||
And later access this value out of the session anywhere in the subsequent requests. |
||||
|
||||
Register this route in the `index.js` file at the root of the project: |
||||
|
||||
```javascript |
||||
const ProcessLoginHandler = require('./handlers/process-login.js'); |
||||
|
||||
app.post('/process-login', ProcessLoginHandler); |
||||
``` |
||||
|
||||
Finally, we have the logout functionality. Create a file at `handlers/logout.js` with the following content: |
||||
|
||||
```javascript |
||||
module.exports = function Logout(req, res) { |
||||
req.session.destroy(); |
||||
res.redirect('/'); |
||||
} |
||||
``` |
||||
|
||||
We reset the session by calling `req.session.destroy()` and then redirecting the user to the homepage. Register the logout handler in the `index.js` file using the following: |
||||
|
||||
```javascript |
||||
const LogoutHandler = require('./handlers/logout.js'); |
||||
|
||||
app.get('/logout', LogoutHandler); |
||||
``` |
||||
|
||||
## Running the Application |
||||
Open the `package.json` file and register the `start` script as follows: |
||||
|
||||
```javascript |
||||
"scripts": { |
||||
"start": "node index.js" |
||||
}, |
||||
``` |
||||
|
||||
Now you can start the application by running the following command: |
||||
|
||||
```shell |
||||
npm run start |
||||
``` |
||||
|
||||
Now, if you open up your browser and visit the project at `http://localhost:3000` you will be able to see the Session-Based Authentication in action. |
Loading…
Reference in new issue