Skip to content

Commit

Permalink
Merge pull request #59 from Baroshem/chore/0.8.0
Browse files Browse the repository at this point in the history
feat/nuxt3-stable-and-basic-auth
  • Loading branch information
Baroshem authored Nov 18, 2022
2 parents 1c93c65 + 04bca55 commit a07faa9
Show file tree
Hide file tree
Showing 14 changed files with 734 additions and 729 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
- XSS Validator for both GET and POST requests
- CORS Handler similar to popular Express.js middleware
- Allowed HTTP Methods Restricter
- Basic Auth support
- TypeScript support

[📖  Read the documentation](https://nuxt-security.vercel.app)
Expand Down
2 changes: 2 additions & 0 deletions docs/content/1.getting-started/2.configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface ModuleOptions {
corsHandler: MiddlewareConfiguration<CorsOptions> | boolean;
allowedMethodsRestricter: MiddlewareConfiguration<AllowedHTTPMethods> | boolean;
hidePoweredBy: boolean;
basicAuth: MiddlewareConfiguration<BasicAuth> | boolean;
}
```
Expand Down Expand Up @@ -131,6 +132,7 @@ security: {
route: '',
},
hidePoweredBy: true,
basicAuth: false
}
```
Expand Down
1 change: 1 addition & 0 deletions docs/content/1.index.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Security Module for Nuxt based on OWASP Top 10 and Helmet
- XSS Validator for both GET and POST requests
- CORS Handler similar to popular Express.js middleware
- Allowed HTTP Methods Restricter
- Basic Auth support
- TypeScript support
::
::
40 changes: 40 additions & 0 deletions docs/content/2.middlewares/7.basic-auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
title: Basic Auth
description: ''
---

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.

You can learn more about HTTP Authentication [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#basic_authentication_scheme).

```ts
export type BasicAuth = {
name: string;
pass: string;
enabled: boolean;
message: string;
}
```
To write a custom logic for this middleware follow this pattern:
```javascript
// nuxt.config.js

{
modules: [
"nuxt-security",
],
security: {
basicAuth: {
route: '/secret-route',
value: {
name: 'test',
pass: 'test',
enabled: true,
message: 'test'
}
}
}
}
```
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nuxt-security",
"version": "0.7.0",
"version": "0.8.0",
"license": "MIT",
"type": "module",
"homepage": "https://nuxt-security.vercel.app",
Expand Down Expand Up @@ -35,16 +35,17 @@
},
"dependencies": {
"@nozomuikuta/h3-cors": "^0.1.5",
"@nuxt/kit": "^3.0.0-rc.13",
"@nuxt/kit": "^3.0.0",
"basic-auth": "^2.0.1",
"limiter": "^2.1.0",
"memory-cache": "^0.2.0",
"xss": "^1.0.14"
},
"devDependencies": {
"@nuxt/module-builder": "latest",
"@nuxt/schema": "^3.0.0-rc.13",
"@nuxt/schema": "^3.0.0",
"@nuxtjs/eslint-config-typescript": "latest",
"eslint": "latest",
"nuxt": "^3.0.0-rc.13"
"nuxt": "^3.0.0"
}
}
11 changes: 11 additions & 0 deletions playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ export default defineNuxtConfig({
MyModule
],
// security: {
// basicAuth: {
// route: '',
// value: {
// name: 'test',
// pass: 'test',
// enabled: true,
// message: 'test'
// }
// }
// }
// security: {
// headers: {
// crossOriginResourcePolicy: {
// value: "test",
Expand Down
1 change: 1 addition & 0 deletions src/defaultConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,5 @@ export const defaultSecurityConfig: ModuleOptions = {
...defaultMiddlewareRoute,
},
hidePoweredBy: true,
basicAuth: false,
};
8 changes: 7 additions & 1 deletion src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { resolve, normalize } from 'pathe'
import { fileURLToPath } from 'node:url'
import { defineNuxtModule, addServerHandler } from '@nuxt/kit'
import defu from 'defu'
import { AllowedHTTPMethods, MiddlewareConfiguration, ModuleOptions, RateLimiter, RequestSizeLimiter, SecurityHeader, SecurityHeaders, XssValidator } from './types'
import { AllowedHTTPMethods, BasicAuth, MiddlewareConfiguration, ModuleOptions, RateLimiter, RequestSizeLimiter, SecurityHeader, SecurityHeaders, XssValidator } from './types'
import { defaultSecurityConfig } from './defaultConfig'
import { SECURITY_HEADER_NAMES } from './headers'
import { RuntimeConfig } from '@nuxt/schema'
Expand Down Expand Up @@ -83,5 +83,11 @@ export default defineNuxtModule<ModuleOptions>({
if (allowedMethodsRestricterConfig && allowedMethodsRestricterConfig.value !== '*') {
addServerHandler({ route: allowedMethodsRestricterConfig.route, handler: normalize(resolve(runtimeDir, 'server/middleware/allowedMethodsRestricter')) })
}

// Register basicAuth middleware that is disabled by default
const basicAuthConfig = nuxt.options.security.basicAuth as MiddlewareConfiguration<BasicAuth>
if (basicAuthConfig && basicAuthConfig?.value?.enabled) {
addServerHandler({ route: basicAuthConfig.route, handler: normalize(resolve(runtimeDir, 'server/middleware/basicAuth')) })
}
}
})
2 changes: 1 addition & 1 deletion src/runtime/server/middleware/allowedMethodsRestricter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const securityConfig = useRuntimeConfig().security

export default defineEventHandler((event) => {
const allowedMethods: string[] = securityConfig.allowedMethodsRestricter.value
if (!allowedMethods.includes(event.req.method!!)) {
if (!allowedMethods.includes(event.node.req.method!!)) {
throw createError({ statusCode: 405, statusMessage: 'Method not allowed' })
}
})
29 changes: 29 additions & 0 deletions src/runtime/server/middleware/basicAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { defineEventHandler, setHeader, createError, sendError } from 'h3'
import getCredentials from 'basic-auth'
import { useRuntimeConfig } from '#imports'

type Credentials = {
name: string;
pass: string;
};

export type BasicAuth = {
name: string;
pass: string;
enabled: boolean;
message: string;
}

const securityConfig = useRuntimeConfig().security

export default defineEventHandler(async (event) => {
const credentials = getCredentials(event.node.req)
const basicAuthConfig: BasicAuth = securityConfig.basicAuth.value

if (!credentials && !validateCredentials(credentials, basicAuthConfig)) {
setHeader(event, 'WWW-Authenticate', `Basic realm=${basicAuthConfig.message || "Please enter username and password"}`)
sendError(event, createError({ statusCode: 401, statusMessage: 'Access denied' }))
}
})

const validateCredentials = (credentials: Credentials, config: BasicAuth): boolean => credentials?.name === config?.name && credentials?.pass === config?.pass
4 changes: 2 additions & 2 deletions src/runtime/server/middleware/requestSizeLimiter.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { defineEventHandler, getRequestHeader, sendError, createError } from 'h3'
import { defineEventHandler, getRequestHeader, createError } from 'h3'
import { useRuntimeConfig } from '#imports'

const securityConfig = useRuntimeConfig().security

const FILE_UPLOAD_HEADER = 'multipart/form-data'

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

Expand Down
4 changes: 2 additions & 2 deletions src/runtime/server/middleware/xssValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ const securityConfig = useRuntimeConfig().security
const xssValidator = new FilterXSS(securityConfig.xssValidator.value)

export default defineEventHandler(async (event) => {
if (['POST', 'GET'].includes(event.req.method!!)) {
const valueToFilter = event.req.method === 'GET' ? getQuery(event) : readBody(event)
if (['POST', 'GET'].includes(event.node.req.method!!)) {
const valueToFilter = event.node.req.method === 'GET' ? getQuery(event) : readBody(event)
const stringifiedValue = JSON.stringify(valueToFilter)
const processedValue = xssValidator.process(JSON.stringify(valueToFilter))
if (processedValue !== stringifiedValue) {
Expand Down
8 changes: 8 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ export type XssValidator = {
css: Record<string, any> | boolean;
} | {};

export type BasicAuth = {
name: string;
pass: string;
enabled: boolean;
message: string;
}

export type HTTPMethod = 'GET' | 'POST' | 'DELETE' | 'PATCH' | 'POST' | string;

export type AllowedHTTPMethods = HTTPMethod[] | '*'
Expand Down Expand Up @@ -53,4 +60,5 @@ export interface ModuleOptions {
corsHandler: MiddlewareConfiguration<CorsOptions> | boolean;
allowedMethodsRestricter: MiddlewareConfiguration<AllowedHTTPMethods> | boolean;
hidePoweredBy: boolean;
basicAuth: MiddlewareConfiguration<BasicAuth> | boolean;
}
Loading

1 comment on commit a07faa9

@vercel
Copy link

@vercel vercel bot commented on a07faa9 Nov 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nuxt-security – ./

nuxt-security-git-main-baroshem.vercel.app
nuxt-security.vercel.app
nuxt-security-baroshem.vercel.app

Please sign in to comment.