Skip to content

Commit a07faa9

Browse files
authored
Merge pull request #59 from Baroshem/chore/0.8.0
feat/nuxt3-stable-and-basic-auth
2 parents 1c93c65 + 04bca55 commit a07faa9

File tree

14 files changed

+734
-729
lines changed

14 files changed

+734
-729
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
- XSS Validator for both GET and POST requests
2424
- CORS Handler similar to popular Express.js middleware
2525
- Allowed HTTP Methods Restricter
26+
- Basic Auth support
2627
- TypeScript support
2728

2829
[📖  Read the documentation](https://nuxt-security.vercel.app)

docs/content/1.getting-started/2.configuration.md

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export interface ModuleOptions {
3030
corsHandler: MiddlewareConfiguration<CorsOptions> | boolean;
3131
allowedMethodsRestricter: MiddlewareConfiguration<AllowedHTTPMethods> | boolean;
3232
hidePoweredBy: boolean;
33+
basicAuth: MiddlewareConfiguration<BasicAuth> | boolean;
3334
}
3435
```
3536
@@ -131,6 +132,7 @@ security: {
131132
route: '',
132133
},
133134
hidePoweredBy: true,
135+
basicAuth: false
134136
}
135137
```
136138

docs/content/1.index.md

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Security Module for Nuxt based on OWASP Top 10 and Helmet
3232
- XSS Validator for both GET and POST requests
3333
- CORS Handler similar to popular Express.js middleware
3434
- Allowed HTTP Methods Restricter
35+
- Basic Auth support
3536
- TypeScript support
3637
::
3738
::
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
title: Basic Auth
3+
description: ''
4+
---
5+
6+
This middleware will implement Basic Auth in your application. Only users with correct credentials passed to the browser prompt will be able to see the application. Others will be automatically rejected.
7+
8+
You can learn more about HTTP Authentication [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#basic_authentication_scheme).
9+
10+
```ts
11+
export type BasicAuth = {
12+
name: string;
13+
pass: string;
14+
enabled: boolean;
15+
message: string;
16+
}
17+
```
18+
19+
To write a custom logic for this middleware follow this pattern:
20+
21+
```javascript
22+
// nuxt.config.js
23+
24+
{
25+
modules: [
26+
"nuxt-security",
27+
],
28+
security: {
29+
basicAuth: {
30+
route: '/secret-route',
31+
value: {
32+
name: 'test',
33+
pass: 'test',
34+
enabled: true,
35+
message: 'test'
36+
}
37+
}
38+
}
39+
}
40+
```

package.json

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nuxt-security",
3-
"version": "0.7.0",
3+
"version": "0.8.0",
44
"license": "MIT",
55
"type": "module",
66
"homepage": "https://nuxt-security.vercel.app",
@@ -35,16 +35,17 @@
3535
},
3636
"dependencies": {
3737
"@nozomuikuta/h3-cors": "^0.1.5",
38-
"@nuxt/kit": "^3.0.0-rc.13",
38+
"@nuxt/kit": "^3.0.0",
39+
"basic-auth": "^2.0.1",
3940
"limiter": "^2.1.0",
4041
"memory-cache": "^0.2.0",
4142
"xss": "^1.0.14"
4243
},
4344
"devDependencies": {
4445
"@nuxt/module-builder": "latest",
45-
"@nuxt/schema": "^3.0.0-rc.13",
46+
"@nuxt/schema": "^3.0.0",
4647
"@nuxtjs/eslint-config-typescript": "latest",
4748
"eslint": "latest",
48-
"nuxt": "^3.0.0-rc.13"
49+
"nuxt": "^3.0.0"
4950
}
5051
}

playground/nuxt.config.ts

+11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@ export default defineNuxtConfig({
66
MyModule
77
],
88
// security: {
9+
// basicAuth: {
10+
// route: '',
11+
// value: {
12+
// name: 'test',
13+
// pass: 'test',
14+
// enabled: true,
15+
// message: 'test'
16+
// }
17+
// }
18+
// }
19+
// security: {
920
// headers: {
1021
// crossOriginResourcePolicy: {
1122
// value: "test",

src/defaultConfig.ts

+1
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,5 @@ export const defaultSecurityConfig: ModuleOptions = {
9797
...defaultMiddlewareRoute,
9898
},
9999
hidePoweredBy: true,
100+
basicAuth: false,
100101
};

src/module.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { resolve, normalize } from 'pathe'
22
import { fileURLToPath } from 'node:url'
33
import { defineNuxtModule, addServerHandler } from '@nuxt/kit'
44
import defu from 'defu'
5-
import { AllowedHTTPMethods, MiddlewareConfiguration, ModuleOptions, RateLimiter, RequestSizeLimiter, SecurityHeader, SecurityHeaders, XssValidator } from './types'
5+
import { AllowedHTTPMethods, BasicAuth, MiddlewareConfiguration, ModuleOptions, RateLimiter, RequestSizeLimiter, SecurityHeader, SecurityHeaders, XssValidator } from './types'
66
import { defaultSecurityConfig } from './defaultConfig'
77
import { SECURITY_HEADER_NAMES } from './headers'
88
import { RuntimeConfig } from '@nuxt/schema'
@@ -83,5 +83,11 @@ export default defineNuxtModule<ModuleOptions>({
8383
if (allowedMethodsRestricterConfig && allowedMethodsRestricterConfig.value !== '*') {
8484
addServerHandler({ route: allowedMethodsRestricterConfig.route, handler: normalize(resolve(runtimeDir, 'server/middleware/allowedMethodsRestricter')) })
8585
}
86+
87+
// Register basicAuth middleware that is disabled by default
88+
const basicAuthConfig = nuxt.options.security.basicAuth as MiddlewareConfiguration<BasicAuth>
89+
if (basicAuthConfig && basicAuthConfig?.value?.enabled) {
90+
addServerHandler({ route: basicAuthConfig.route, handler: normalize(resolve(runtimeDir, 'server/middleware/basicAuth')) })
91+
}
8692
}
8793
})

src/runtime/server/middleware/allowedMethodsRestricter.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const securityConfig = useRuntimeConfig().security
55

66
export default defineEventHandler((event) => {
77
const allowedMethods: string[] = securityConfig.allowedMethodsRestricter.value
8-
if (!allowedMethods.includes(event.req.method!!)) {
8+
if (!allowedMethods.includes(event.node.req.method!!)) {
99
throw createError({ statusCode: 405, statusMessage: 'Method not allowed' })
1010
}
1111
})
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { defineEventHandler, setHeader, createError, sendError } from 'h3'
2+
import getCredentials from 'basic-auth'
3+
import { useRuntimeConfig } from '#imports'
4+
5+
type Credentials = {
6+
name: string;
7+
pass: string;
8+
};
9+
10+
export type BasicAuth = {
11+
name: string;
12+
pass: string;
13+
enabled: boolean;
14+
message: string;
15+
}
16+
17+
const securityConfig = useRuntimeConfig().security
18+
19+
export default defineEventHandler(async (event) => {
20+
const credentials = getCredentials(event.node.req)
21+
const basicAuthConfig: BasicAuth = securityConfig.basicAuth.value
22+
23+
if (!credentials && !validateCredentials(credentials, basicAuthConfig)) {
24+
setHeader(event, 'WWW-Authenticate', `Basic realm=${basicAuthConfig.message || "Please enter username and password"}`)
25+
sendError(event, createError({ statusCode: 401, statusMessage: 'Access denied' }))
26+
}
27+
})
28+
29+
const validateCredentials = (credentials: Credentials, config: BasicAuth): boolean => credentials?.name === config?.name && credentials?.pass === config?.pass

src/runtime/server/middleware/requestSizeLimiter.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { defineEventHandler, getRequestHeader, sendError, createError } from 'h3'
1+
import { defineEventHandler, getRequestHeader, createError } from 'h3'
22
import { useRuntimeConfig } from '#imports'
33

44
const securityConfig = useRuntimeConfig().security
55

66
const FILE_UPLOAD_HEADER = 'multipart/form-data'
77

88
export default defineEventHandler(async (event) => {
9-
if (['POST', 'PUT', 'DELETE'].includes(event.req.method!!)) {
9+
if (['POST', 'PUT', 'DELETE'].includes(event.node.req.method!!)) {
1010
const contentLengthValue = getRequestHeader(event, 'content-length')
1111
const contentTypeValue = getRequestHeader(event, 'content-type')
1212

src/runtime/server/middleware/xssValidator.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ const securityConfig = useRuntimeConfig().security
66
const xssValidator = new FilterXSS(securityConfig.xssValidator.value)
77

88
export default defineEventHandler(async (event) => {
9-
if (['POST', 'GET'].includes(event.req.method!!)) {
10-
const valueToFilter = event.req.method === 'GET' ? getQuery(event) : readBody(event)
9+
if (['POST', 'GET'].includes(event.node.req.method!!)) {
10+
const valueToFilter = event.node.req.method === 'GET' ? getQuery(event) : readBody(event)
1111
const stringifiedValue = JSON.stringify(valueToFilter)
1212
const processedValue = xssValidator.process(JSON.stringify(valueToFilter))
1313
if (processedValue !== stringifiedValue) {

src/types.ts

+8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ export type XssValidator = {
1818
css: Record<string, any> | boolean;
1919
} | {};
2020

21+
export type BasicAuth = {
22+
name: string;
23+
pass: string;
24+
enabled: boolean;
25+
message: string;
26+
}
27+
2128
export type HTTPMethod = 'GET' | 'POST' | 'DELETE' | 'PATCH' | 'POST' | string;
2229

2330
export type AllowedHTTPMethods = HTTPMethod[] | '*'
@@ -53,4 +60,5 @@ export interface ModuleOptions {
5360
corsHandler: MiddlewareConfiguration<CorsOptions> | boolean;
5461
allowedMethodsRestricter: MiddlewareConfiguration<AllowedHTTPMethods> | boolean;
5562
hidePoweredBy: boolean;
63+
basicAuth: MiddlewareConfiguration<BasicAuth> | boolean;
5664
}

0 commit comments

Comments
 (0)