Skip to content

Commit 85e2407

Browse files
authored
Implement basic authentication (#3)
* Implement basic authentication Implementation of authentication middleware for API endpoints with basic authentication. Username, password and authentication token are configurable via environment variables. Moved HTML templates to root and added build, linter and start scripts. * Improved logging Improved logging by updating the format and adding request id to server. Storing logs in a file. * Configured eslint Added eslint configuration with several rules for disallowing console, debugger, checking unused vars, prefer imports and strict checks for boolean conditions.
1 parent c12cb53 commit 85e2407

21 files changed

+301
-29
lines changed

.cspell.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
"package-lock.json",
1010
"report",
1111
".env",
12-
".github"
12+
".github",
13+
"dist"
1314
],
1415
"language": "en",
1516
"noConfigSearch": true,
@@ -22,7 +23,9 @@
2223
"stefanzweifel",
2324
"SHELLCHECK",
2425
"DEVSKIM",
25-
"gitmessage"
26+
"gitmessage",
27+
"uuidv",
28+
"EDITMSG"
2629
],
2730
"version": "0.2"
2831
}

.env.dist

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
API_BASE_HOST=http://127.0.0.1:3000
2-
PORT=3000
2+
PORT=3000
3+
AUTH_TOKEN='CHANGE_ME' #base64 encoding of AUTH_USERNAME and AUTH_PASSWORD
4+
AUTH_USERNAME=CHANGE_ME
5+
AUTH_PASSWORD=CHANGE_ME

.eslintignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
dist
2+
node_modules
3+
.eslintrc.js
4+
.prettierrc.js

.eslintrc.json

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"root": true,
3+
"extends": [
4+
"eslint:recommended",
5+
"plugin:@typescript-eslint/recommended",
6+
"plugin:prettier/recommended"
7+
],
8+
"parser": "@typescript-eslint/parser",
9+
"parserOptions": {
10+
"project": "./tsconfig.json",
11+
"sourceType": "module",
12+
"ecmaVersion": 2020
13+
},
14+
"plugins": ["@typescript-eslint/eslint-plugin"],
15+
"rules": {
16+
"@typescript-eslint/strict-boolean-expressions": [
17+
2,
18+
{
19+
"allowString": false,
20+
"allowNumber": false
21+
}
22+
],
23+
"@typescript-eslint/interface-name-prefix": "off",
24+
"@typescript-eslint/ban-ts-comment": "off",
25+
"eqeqeq": ["error", "always"],
26+
"no-console": "warn",
27+
"no-debugger": "error",
28+
"padding-line-between-statements": [
29+
"error",
30+
{
31+
"blankLine": "always",
32+
"prev": "block-like",
33+
"next": "*"
34+
}
35+
],
36+
"@typescript-eslint/no-unused-vars": [
37+
"off",
38+
{
39+
"destructuredArrayIgnorePattern": "^_"
40+
}
41+
],
42+
"@typescript-eslint/consistent-type-imports": [
43+
"error",
44+
{
45+
"prefer": "type-imports"
46+
}
47+
]
48+
},
49+
"ignorePatterns": ["dist", "**/node_modules/**"]
50+
}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
node_modules
22
.env
33
megalinter-reports/
4+
dist
5+
access.log

.jscpd.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"**/.idea/**",
1212
"**/report/**",
1313
"**/*.svg",
14-
"megalinter-reports"
14+
"megalinter-reports",
15+
"dist"
1516
]
1617
}

.mega-linter.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
APPLY_FIXES: all # all, none, or list of linter keys
22
SHOW_ELAPSED_TIME: true
33
FILEIO_REPORTER: false
4+
EXCLUDED_DIRECTORIES:
5+
- dist
6+
- node_modules
7+
- megalinter-reports
48
DISABLE_LINTERS:
59
- BASH_SHELLCHECK
610
- REPOSITORY_CHECKOV

.prettierignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dist
2+
node_modules
3+
.github

package.json

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,26 @@
77
"license": "MIT",
88
"private": true,
99
"scripts": {
10-
"start:dev": "nodemon src/app.ts"
10+
"build": "tsc",
11+
"start:dev": "nodemon src/app.ts",
12+
"start": "node dist/app.js",
13+
"linter": "npx mega-linter-runner"
1114
},
1215
"dependencies": {
16+
"body-parser": "1.20.2",
17+
"cors": "2.8.5",
1318
"dotenv": "16.3.1",
1419
"express": "4.18.2",
1520
"helmet": "7.0.0",
16-
"nunjucks": "3.2.4"
21+
"morgan": "1.10.0",
22+
"nunjucks": "3.2.4",
23+
"uuid": "9.0.0"
1724
},
1825
"devDependencies": {
26+
"@types/uuid": "9.0.2",
27+
"@types/cors": "2.8.13",
1928
"@types/express": "4.17.17",
29+
"@types/morgan": "^1.9.4",
2030
"@types/node": "20.3.3",
2131
"@types/nunjucks": "3.2.3",
2232
"eslint": "8.44.0",

src/app.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,52 @@ import dotenv from 'dotenv'
22
import express from 'express'
33
import nunjucks from 'nunjucks'
44
import helmet from 'helmet'
5+
import bodyParser from 'body-parser'
6+
import cors from 'cors'
7+
import morgan from 'morgan'
8+
import { createWriteStream } from 'fs'
9+
import { join } from 'path'
10+
import type { NextFunction, Request, Response } from 'express'
11+
import { v4 as uuidv4 } from 'uuid'
512
import routes from './routes'
613

714
dotenv.config()
815

916
const port = process.env.PORT ?? 3000
1017
const app = express()
1118

19+
app.use((request: Request, response: Response, next: NextFunction) => {
20+
const requestID = uuidv4()
21+
request.headers['X-Request-Id'] = requestID
22+
response.set('X-Request-Id', requestID)
23+
next()
24+
})
25+
26+
morgan.token('id', (request: Request) => {
27+
return request.headers['X-Request-Id'] as string
28+
})
29+
30+
// adding Helmet to enhance your API's security
1231
app.use(helmet())
1332

14-
nunjucks.configure('src/views', {
33+
// using bodyParser to parse JSON bodies into JS objects
34+
app.use(bodyParser.json())
35+
36+
// enabling CORS for all requests
37+
app.use(cors())
38+
39+
// adding morgan to log HTTP requests
40+
const accessLogStream = createWriteStream(join(__dirname, '../access.log'), {
41+
flags: 'a'
42+
})
43+
app.use(
44+
morgan(
45+
':date[iso] - :id - :remote-addr - :method :url - :response-time ms',
46+
{ stream: accessLogStream }
47+
)
48+
)
49+
50+
nunjucks.configure('views', {
1551
autoescape: true,
1652
express: app
1753
})
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { Request, Response, NextFunction } from 'express'
2+
export default (request: Request, response: Response, next: NextFunction) => {
3+
if (request.headers.authorization === `Basic ${process.env.AUTH_TOKEN}`) {
4+
return next()
5+
}
6+
7+
return response
8+
.status(403)
9+
.send({ status: 'fail', message: 'Authorization failed' })
10+
}

src/routes/api/v1/hello-world.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,27 @@
11
import type { Request, Response } from 'express'
22
import express from 'express'
3+
import authentication from '../../../middleware/security/authentication'
34

45
const router = express.Router()
56

6-
router.get('/', (_request: Request, response: Response) => {
7+
router.get('/', authentication, (_request: Request, response: Response) => {
78
response.json({
89
status: 'success',
9-
message: 'Hello World API!'
10+
message: 'Hello World API!',
11+
products: [
12+
{
13+
id: '1',
14+
name: 'MacBook Pro'
15+
},
16+
{
17+
id: '2',
18+
name: 'HP'
19+
},
20+
{
21+
id: '3',
22+
name: 'Dell'
23+
}
24+
]
1025
})
1126
})
1227
export default router

src/routes/api/v1/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Express } from 'express'
1+
import type { Express } from 'express'
22
import helloWorldRoute from './hello-world'
33

44
export default (app: Express) => {

src/routes/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Express } from 'express'
1+
import type { Express } from 'express'
22
import v1 from './api/v1'
33
import render from './render'
44

src/routes/render/hello-world.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,21 @@ import express from 'express'
44
const router = express.Router()
55

66
router.get('/', async (_request: Request, response: Response) => {
7-
const result = await fetch(`${process.env.API_BASE_HOST}/api/v1/hello-world`)
7+
const token = Buffer.from(
8+
`${process.env.AUTH_USERNAME}:${process.env.AUTH_PASSWORD}`
9+
).toString('base64')
10+
const result = await fetch(
11+
`${process.env.API_BASE_HOST}/api/v1/hello-world`,
12+
{
13+
method: 'GET',
14+
headers: {
15+
'Content-Type': 'application/json',
16+
Authorization: `Basic ${token}`
17+
}
18+
}
19+
)
820
const data = await result.json()
921

10-
response.render('hello-world.html', {
11-
message: data.message
12-
})
22+
response.render('hello-world.html', { data })
1323
})
1424
export default router

src/routes/render/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Express } from 'express'
1+
import type { Express } from 'express'
22
import helloWorldRoute from './hello-world'
33

44
export default (app: Express) => {

src/views/hello-world.html

Lines changed: 0 additions & 8 deletions
This file was deleted.

tsconfig.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
{
22
"compilerOptions": {
3+
"outDir": "dist",
34
"module": "commonjs",
45
"esModuleInterop": true,
5-
"sourceMap": false
6+
"sourceMap": false,
7+
"rootDir": "src",
8+
"strictNullChecks": true,
9+
"skipLibCheck": true
610
},
7-
"exclude": ["node_modules"]
11+
"include": ["src"],
12+
"exclude": ["node_modules", "megalinter-reports"]
813
}

src/views/base.html renamed to views/base.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<meta name="description" content="">
88
<meta name="viewport" content="width=device-width, initial-scale=1">
99
<meta name="keywords" content="NodeJs, Terraform, DemoApp">
10+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
1011
</head>
1112
<body>
1213
{% block content %}{% endblock %}

views/hello-world.html

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!DOCTYPE html>
2+
{% extends 'base.html' %}
3+
4+
{% set title = 'Hello World' %}
5+
6+
{% block content %}
7+
<div class="container">
8+
<h1 class="text-center mt-5">{{ data.message }}</h1>
9+
<table class="table table-responsive table-striped table-hover table-sm">
10+
<thead class="table-info">
11+
<tr><th>ID</th><th>Name</th></tr>
12+
</thead>
13+
<tbody>
14+
{% for product in data.products %}
15+
<tr>
16+
<td>
17+
{{ product.id }}
18+
</td><td>{{ product.name }}</td></tr>
19+
{% endfor %}
20+
</tbody>
21+
</table>
22+
</div>
23+
{% endblock %}

0 commit comments

Comments
 (0)