Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
npm-debug.log
17 changes: 17 additions & 0 deletions .github/workflows/CodeCov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: CodeCov

on: [push, pull_request]

jobs:
build:
runs-on: ubuntu-latest
name: RunTest
steps:
- uses: actions/checkout@v1
- name: Upload coverage reports to Codecov
run: |
# Replace `linux` below with the appropriate OS
# Options are `alpine`, `linux`, `macos`, `windows`
curl -Os https://uploader.codecov.io/latest/linux/codecov
chmod +x codecov
./codecov -t ${CODECOV_TOKEN}
18 changes: 18 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM node:16

# Create app directory
WORKDIR /usr/src/app

# Copy app and configs
COPY package*.json ./

RUN npm install
# If you are building your code for production (do NOT ignore git 'node_modules')
# RUN npm ci --only=production

# Bundle app source
COPY . .

EXPOSE 3000

CMD [ "node", "index.js" ]
47 changes: 42 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# How to Add Authentication with Google Authenticator in Node.js
# Add MFA/2FA Authentication with Google Authenticator
* Using Node.js & docker

Code for [How to Add Authentication with Google Authenticator in Node.js tutorial](https://blog.shahednasser.com/how-to-add-authentication-with-google-authenticator-in-node-js/)
* Idea was described on shahednasser´s blog, in the post [How to Add Authentication with Google Authenticator in Node.js tutorial](https://blog.shahednasser.com/how-to-add-authentication-with-google-authenticator-in-node-js/)

***So why this?**

*I wanted to extend the tutorial with docker*

## Installation

Expand All @@ -10,12 +15,44 @@ After cloning this repository, install the dependencies:
npm i
```

## Run the Server
## Run the APP

Run the server with the following command:
Run the 'APP' with the following command:

```bash
npm start
```

The server will run at `localhost:3000`.
The 'APP' will run at port 3000 if you do not change the config/default.json

### Test it
```bash
Open your browser http://localhost:3000
```


# Ready for docker

## Create docker container

### Build and tag you Docker container (eg: mfa)
```bash
docker build . -t mfa
```

### Start the MFA Docker image
```bash
docker run -p 3000:3000 -d mfa
```

The server will run at port 3000 if you do not change
* config/default.json
* Dockerfile (expose port)


### Test it
```bash
Open your browser http://localhost:3000
```


8 changes: 8 additions & 0 deletions config/default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"app": {
"name": "MFA/2FA NodeAPP",
"port": 3000,
"sesCrypter": "mysupersecret",
"codeCrypter": "mysupersupersecret"
}
}
73 changes: 40 additions & 33 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,22 @@ const QRCode = require('qrcode')
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
const bodyParser = require('body-parser')
const config = require('config');

const port = config.get('app.port');
const appname = config.get('app.name');
const sesCrypter = config.get('app.sesCrypter');
const codeCrypter = config.get('app.codeCrypter');

const app = express()
const port = 3000

app.set('view engine', 'ejs')

app.use(session({
secret: 'supersecret',
}))

secret: sesCrypter,
resave: true,
saveUninitialized: true
}));

app.use(bodyParser.urlencoded({ extended: false }))

Expand All @@ -36,7 +43,7 @@ app.post('/sign-up', (req, res) => {
}

//generate qr and put it in session
QRCode.toDataURL(authenticator.keyuri(email, '2FA Node App', secret), (err, url) => {
QRCode.toDataURL(authenticator.keyuri(email, appname, secret), (err, url) => {
if (err) {
throw err
}
Expand Down Expand Up @@ -69,7 +76,7 @@ app.post('/sign-up-2fa', (req, res) => {
})

const jwtMiddleware = expressJWT({
secret: 'supersecret',
secret: codeCrypter,
algorithms: ['HS256'],
getToken: (req) => {
return req.session.token
Expand Down Expand Up @@ -98,32 +105,32 @@ app.get('/logout', jwtMiddleware, (req, res) => {
})

function verifyLogin (email, code, req, res, failUrl) {
//load user by email
const db = new sqlite3.Database('db.sqlite')
db.serialize(() => {
db.get('SELECT secret FROM users WHERE email = ?', [email], (err, row) => {
if (err) {
throw err
}

if (!row) {
return res.redirect('/')
}

if (!authenticator.check(code, row.secret)) {
//redirect back
return res.redirect(failUrl)
}

//correct, add jwt to session
req.session.qr = null
req.session.email = null
req.session.token = jwt.sign(email, 'supersecret')

//redirect to "private" page
return res.redirect('/private')
//load user by email
const db = new sqlite3.Database('db.sqlite')
db.serialize(() => {
db.get('SELECT secret FROM users WHERE email = ?', [email], (err, row) => {
if (err) {
throw err
}

if (!row) {
return res.redirect('/')
}

if (!authenticator.check(code, row.secret)) {
//redirect back
return res.redirect(failUrl)
}

//correct, add jwt to session
req.session.qr = null
req.session.email = null
req.session.token = jwt.sign(email, codeCrypter)

//redirect to "private" page
return res.redirect('/private')
})
})
})
}

//create database with tables if it doesn't exist
Expand All @@ -134,5 +141,5 @@ db.serialize(() => {
db.close()

app.listen(port, () => {
console.log(`2FA Node app listening at http://localhost:${port}`)
})
console.log(`${appname} listening on ${port}`)
})
Loading