Skip to content

Commit aab9585

Browse files
author
Jan Goebel
committed
cookie-based session with express
0 parents  commit aab9585

File tree

14 files changed

+1932
-0
lines changed

14 files changed

+1932
-0
lines changed

.gitignore

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
*.css
2+
# See https://help.github.com/ignore-files/ for more about ignoring files.
3+
4+
# dependencies
5+
/node_modules
6+
7+
# testing
8+
/coverage
9+
10+
# production
11+
/build
12+
13+
# misc
14+
.DS_Store
15+
.env.local
16+
.env.development.local
17+
.env.test.local
18+
.env.production.local
19+
20+
npm-debug.log*
21+
yarn-debug.log*
22+
yarn-error.log*

README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Express Session with Redis
2+
3+
This repository contains sample code on how to use [Redis](https://redis.io) for a cookie-based session implementation with [Redis](https://redis.io) and the Node.js [express](https://expressjs.com/) framework.
4+
5+
This repository is part of the *[Sessions in express.js tutorial series on YouTube](https://www.youtube.com/playlist?list=PL1Nml43UBm6fPP7cW9pAFTdZ_9QX2mBn2) provided by [productioncoder.com](https://productioncoder.com/)*.
6+
7+
For updates, please reach out to [@productioncoder](https://twitter.com/productioncoder) on Twitter or leave a comment in the [Youtube playlist](https://www.youtube.com/playlist?list=PL1Nml43UBm6fPP7cW9pAFTdZ_9QX2mBn2).
8+
9+
<h3 align="center">Please help this repo with a ⭐️ if you find it useful! 😁</h3>
10+
11+
## Session implementation with express.js and express-session
12+
13+
This repository illustrates how to create a session-based authentication system in [Express.js](https://expressjs.com/) and what configurations you need to perform for you server to also accept cookies from cross origins - a scenario that is typical for Single Page Applications (SPAs) written in modern frameworks such as [React](https://reactjs.org/) or [Vue](https://vuejs.org/).
14+
15+
## Running this project
16+
17+
### 1. Installing Redis
18+
19+
Make sure that you have _[Redis](https://redis.io) running locally_ on your machine on its _default_ port `6379`.
20+
21+
This project assumes that your [Redis](https://redis.io) instance does _not require a password_ (which is the default).
22+
23+
If your local [Redis](https://redis.io) requires a password, please update the `db/redis.js` file to include the password field:
24+
25+
```
26+
const redisClient = redis.createClient({
27+
port: 6379,
28+
host: 'localhost',
29+
password: 'your-password'
30+
});
31+
```
32+
33+
If you are on macOS, the easiest way to start up a [Redis](https://redis.io) instance is by using [Homebrew](https://brew.sh/)
34+
35+
```
36+
brew install redis
37+
brew services start redis
38+
```
39+
40+
To stop [Redis](https://redis.io), you can run
41+
42+
```
43+
brew services stop redis
44+
```
45+
46+
### 2. Install dependencies
47+
48+
Run
49+
50+
```
51+
npm install
52+
```
53+
54+
to install the project's dependencies.
55+
56+
### 3. Run project
57+
58+
Execute the `dev` script to start up your server.
59+
60+
```
61+
npm run dev
62+
```

controller/auth.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const authService = require('../service/auth');
2+
3+
async function login(req, res) {
4+
const {email, password} = req.body;
5+
6+
// perform payload validation
7+
// in prod, always use a validation library like joi
8+
// for this tutorial, we only do basic validation
9+
if (!email || !password) {
10+
return res.status(400).json('Bad request params - you need to provide an email and a password');
11+
}
12+
13+
try {
14+
const user = await authService.login(email, password);
15+
req.session.user = user;
16+
res.sendStatus(204);
17+
} catch(err) {
18+
// in prod, do not use console.log or console.error
19+
// use a proper logging library like winston
20+
console.error(err);
21+
res.status(401).json(err);
22+
}
23+
}
24+
25+
module.exports = {
26+
login
27+
};

controller/profile.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function profile(req, res) {
2+
res.json(req.session);
3+
}
4+
5+
module.exports = {profile};

dao/user.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const bcrypt = require('bcrypt');
2+
3+
const users = {
4+
5+
pwHash: bcrypt.hashSync('user1pw', 10),
6+
roles: ['ADMIN'],
7+
id: '41c514f4-7288-4199-80c0-e0be7e4353d7'
8+
},
9+
10+
pwHash: bcrypt.hashSync('user2pw', 10),
11+
roles: ['ACCOUNT_MANAGER'],
12+
id: 'fa54f8ac-6ed7-49d5-b242-64b793da816a'
13+
}
14+
}
15+
16+
// this call would be async and would return a promise
17+
// if we were to use a real database
18+
async function findUserByEmail(email) {
19+
const user = users[email];
20+
return user ? user : Promise.reject('user not found');
21+
}
22+
23+
module.exports = {findUserByEmail};

db/redis.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const redis = require('redis');
2+
// 1 configure our redis
3+
const redisClient = redis.createClient({
4+
port: 6379,
5+
host: 'localhost'
6+
});
7+
8+
module.exports = redisClient;

index.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const express = require("express");
2+
const router = require("./routes");
3+
const session = require("./middleware/session");
4+
const corsMw = require("./middleware/cors");
5+
6+
const app = express();
7+
app.use(express.json());
8+
// if you run behind a proxy (e.g. nginx)
9+
// app.set('trust proxy', 1);
10+
11+
// setup CORS logic
12+
app.options("*", corsMw);
13+
app.use(corsMw);
14+
15+
app.use(session);
16+
app.use(router);
17+
app.listen(8080, () => console.log("server is running on port 8080"));

middleware/authenticate.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
function authenticate(req, res, next) {
2+
if (!req.session || !req.session.user) {
3+
const err = new Error('You shall not pass');
4+
err.statusCode = 401;
5+
next(err);
6+
}
7+
next();
8+
}
9+
10+
module.exports = authenticate;

middleware/cors.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const cors = require("cors");
2+
3+
const whitelist = new Set(["https://example1.com", "https://example2.com"]);
4+
const corsOptions = {
5+
optionsSuccessStatus: 200,
6+
origin: function(origin, callback) {
7+
if (whitelist.has(origin)) {
8+
callback(null, true);
9+
} else {
10+
callback(new Error("Not allowed by CORS"));
11+
}
12+
},
13+
credentials: true
14+
};
15+
16+
module.exports = cors(corsOptions);

middleware/session.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const session = require('express-session');
2+
const connectRedis = require('connect-redis');
3+
const redisClient = require('../db/redis');
4+
5+
const RedisStore = connectRedis(session);
6+
7+
module.exports = session({
8+
store: new RedisStore({client: redisClient}),
9+
secret: 'mySecret',
10+
saveUninitialized: false,
11+
resave: false,
12+
name: 'sessionId',
13+
cookie: {
14+
secure: false, // if true: only transmit cookie over https, in prod, always activate this
15+
httpOnly: true, // if true: prevents client side JS from reading the cookie
16+
maxAge: 1000 * 60 * 30 // session max age in milliseconds
17+
}
18+
});

0 commit comments

Comments
 (0)