├── .editorconfig ├── .gitignore ├── .npmrc ├── .nuxtrc ├── .prettierrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── eslint.config.mjs ├── package-lock.json ├── package.json ├── playground ├── app.vue ├── nuxt.config.ts ├── package.json ├── server │ └── tsconfig.json └── tsconfig.json ├── src ├── module.ts └── runtime │ ├── plugin.ts │ └── server │ └── middleware │ ├── basic-auth.spec.ts │ └── basic-auth.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_size = 2 5 | indent_style = space 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules 3 | 4 | # Logs 5 | *.log* 6 | 7 | # Temp directories 8 | .temp 9 | .tmp 10 | .cache 11 | 12 | # Yarn 13 | **/.yarn/cache 14 | **/.yarn/*state* 15 | 16 | # Generated dirs 17 | dist 18 | 19 | # Nuxt 20 | .nuxt 21 | .output 22 | .vercel_build_output 23 | .build-* 24 | .env 25 | .netlify 26 | 27 | # Env 28 | .env 29 | 30 | # Testing 31 | reports 32 | coverage 33 | *.lcov 34 | .nyc_output 35 | 36 | # VSCode 37 | .vscode/* 38 | !.vscode/settings.json 39 | !.vscode/tasks.json 40 | !.vscode/launch.json 41 | !.vscode/extensions.json 42 | !.vscode/*.code-snippets 43 | 44 | # Intellij idea 45 | *.iml 46 | .idea 47 | 48 | # OSX 49 | .DS_Store 50 | .AppleDouble 51 | .LSOverride 52 | .AppleDB 53 | .AppleDesktop 54 | Network Trash Folder 55 | Temporary Items 56 | .apdisk 57 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false 3 | -------------------------------------------------------------------------------- /.nuxtrc: -------------------------------------------------------------------------------- 1 | imports.autoImport=false 2 | typescript.includeWorkspace=true 3 | # enable TypeScript bundler module resolution - https://www.typescriptlang.org/docs/handbook/modules/reference.html#bundler 4 | experimental.typescriptBundlerResolution=true 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v1.7.0 4 | 5 | [compare changes](https://undefined/undefined/compare/v1.6.0...v1.7.0) 6 | 7 | ### 🚀 Enhancements 8 | 9 | - Allow disabled state at build time (3adc6ac) 10 | 11 | ### 📖 Documentation 12 | 13 | - Use new `nuxi module add` command in installation (b04bd31) 14 | 15 | ### 🏡 Chore 16 | 17 | - Update dependencies (9669535) 18 | - Update eslint and add prettier (5c00622) 19 | - Update readme (fc0a6f2) 20 | - Update dependencies (f28af8e) 21 | 22 | ### ❤️ Contributors 23 | 24 | - Kurt Gierke ([@kgierke](http://github.com/kgierke)) 25 | - Andrew Nyzhnyk ([@dasph](http://github.com/dasph)) 26 | - Daniel Roe ([@danielroe](http://github.com/danielroe)) 27 | 28 | ## v1.6.0 29 | 30 | [compare changes](https://undefined/undefined/compare/v1.5.0...v1.6.0) 31 | 32 | ### 🚀 Enhancements 33 | 34 | - Add customizable delimiter for users string (02d4b4f) 35 | 36 | ### 🩹 Fixes 37 | 38 | - Use utf-8 encoding to allow special characters in credentials (7d778cf) 39 | 40 | ### ❤️ Contributors 41 | 42 | - Kurt Gierke ([@kgierke](http://github.com/kgierke)) 43 | 44 | ## v1.5.0 45 | 46 | [compare changes](https://undefined/undefined/compare/v1.4.0...v1.5.0) 47 | 48 | ### 🚀 Enhancements 49 | 50 | - Accept a string "true" for the enabled option (3428eb2) 51 | 52 | ### 🏡 Chore 53 | 54 | - Update dependencies (e5a4311) 55 | 56 | ### ❤️ Contributors 57 | 58 | - Kurt Gierke ([@kgierke](http://github.com/kgierke)) 59 | 60 | ## v1.4.0 61 | 62 | [compare changes](https://undefined/undefined/compare/v1.3.1...v1.4.0) 63 | 64 | ### 🚀 Enhancements 65 | 66 | - Disable basic auth in prerender context (aeb0a8c) 67 | 68 | ### 🏡 Chore 69 | 70 | - Test bundler module resolution (24b5e6c) 71 | - Update dependencies (f507093) 72 | - Update prepack command (e0ad8aa) 73 | 74 | ### ❤️ Contributors 75 | 76 | - Kurt Gierke ([@kgierke](http://github.com/kgierke)) 77 | - Daniel Roe 78 | 79 | ## v1.3.1 80 | 81 | [compare changes](https://undefined/undefined/compare/v1.3.0...v1.3.1) 82 | 83 | ### 🩹 Fixes 84 | 85 | - Remove default admin credentials (e253930) 86 | 87 | ### 🏡 Chore 88 | 89 | - Add license (1097524) 90 | 91 | ### ❤️ Contributors 92 | 93 | - Kurt Gierke ([@kgierke](http://github.com/kgierke)) 94 | 95 | ## v1.3.0 96 | 97 | [compare changes](https://undefined/undefined/compare/v1.2.0...v1.3.0) 98 | 99 | ### 🚀 Enhancements 100 | 101 | - Allow users to be a string (4b8c34a) 102 | 103 | ### 🩹 Fixes 104 | 105 | - Remove console log statement (9f796dc) 106 | 107 | ### ❤️ Contributors 108 | 109 | - Kurt Gierke ([@kgierke](http://github.com/kgierke)) 110 | 111 | ## v1.2.0 112 | 113 | [compare changes](https://undefined/undefined/compare/v1.1.0...v1.2.0) 114 | 115 | ### 🚀 Enhancements 116 | 117 | - Replace glob pattern with regex (2f94a1c) 118 | 119 | ### ❤️ Contributors 120 | 121 | - Kurt Gierke ([@kgierke](http://github.com/kgierke)) 122 | 123 | ## v1.1.0 124 | 125 | ### 🚀 Enhancements 126 | 127 | - Add basic functionality (7413269) 128 | - Update readme (83f8077) 129 | 130 | ### 🏡 Chore 131 | 132 | - Remove unused tests (0bfc578) 133 | - Add defu as dependency (1f0c1a6) 134 | - Add empty test (cdccc80) 135 | 136 | ### ❤️ Contributors 137 | 138 | - Kurt Gierke ([@kgierke](http://github.com/kgierke)) 139 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Kurt Gierke 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Basic Auth 2 | 3 | [![npm version][npm-version-src]][npm-version-href] 4 | [![npm downloads][npm-downloads-src]][npm-downloads-href] 5 | [![License][license-src]][license-href] 6 | [![Nuxt][nuxt-src]][nuxt-href] 7 | 8 | Nuxt 3 Module for Basic Authentication. 9 | 10 | - [✨  Release Notes](/CHANGELOG.md) 11 | 12 | 13 | 14 | ## Features 15 | 16 | 17 | 18 | - ✅ Simple to use 19 | - ✅ Supports multiple users 20 | - ✅ Whitelist routes 21 | 22 | ## Quick Setup 23 | 24 | Install the module to your Nuxt application with one command: 25 | 26 | ```bash 27 | npx nuxi module add @kgierke/nuxt-basic-auth 28 | ``` 29 | 30 | Configure the module in `nuxt.config.ts` 31 | 32 | ```js 33 | export default defineNuxtConfig({ 34 | modules: ["@kgierke/nuxt-basic-auth"], 35 | 36 | basicAuth: { 37 | enabled: true, 38 | users: [ 39 | { 40 | username: "admin", 41 | password: "admin", 42 | }, 43 | ], 44 | // Optional: Delimiter for users string 45 | // usersDelimiter: ",", 46 | // Optional: Whitelist routes 47 | // allowedRoutes: ["/api/.*"], 48 | }, 49 | }); 50 | ``` 51 | 52 | That's it! You can now use Basic Auth in your Nuxt app ✨ 53 | 54 | ## Options 55 | 56 | | Option | Type | Default | Description | 57 | | ---------------- | ---------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | 58 | | `enabled` | `boolean` | `true` | Enables or disables Basic Auth. | 59 | | `users` | `array` | `[]` | Array of users. Each user must have a `username` and `password` property. Can also be formatted as string `:,:` | 60 | | `usersDelimiter` | `string` | `,` | Delimiter for users string. | 61 | | `allowedRoutes` | `string[]` | `[]` | Array of routes that are not protected by Basic Auth. Supports regex patterns. | 62 | 63 | All options can also be set through environment variables, using the `NUXT_BASIC_AUTH_` prefix. For example, `NUXT_BASIC_AUTH_ENABLED=true`. 64 | 65 | ## Development 66 | 67 | ```bash 68 | # Install dependencies 69 | npm install 70 | 71 | # Generate type stubs 72 | npm run dev:prepare 73 | 74 | # Develop with the playground 75 | npm run dev 76 | 77 | # Build the playground 78 | npm run dev:build 79 | 80 | # Run ESLint 81 | npm run lint 82 | 83 | # Run Vitest 84 | npm run test 85 | npm run test:watch 86 | 87 | # Release new version 88 | npm run release 89 | ``` 90 | 91 | 92 | 93 | [npm-version-src]: https://img.shields.io/npm/v/@kgierke/nuxt-basic-auth/latest.svg?style=flat&colorA=18181B&colorB=28CF8D 94 | [npm-version-href]: https://npmjs.com/package/@kgierke/nuxt-basic-auth 95 | [npm-downloads-src]: https://img.shields.io/npm/dm/@kgierke/nuxt-basic-auth.svg?style=flat&colorA=18181B&colorB=28CF8D 96 | [npm-downloads-href]: https://npmjs.com/package/@kgierke/nuxt-basic-auth 97 | [license-src]: https://img.shields.io/npm/l/@kgierke/nuxt-basic-auth.svg?style=flat&colorA=18181B&colorB=28CF8D 98 | [license-href]: https://npmjs.com/package/@kgierke/nuxt-basic-auth 99 | [nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js 100 | [nuxt-href]: https://nuxt.com 101 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import { createConfigForNuxt } from "@nuxt/eslint-config/flat"; 3 | 4 | // Run `npx @eslint/config-inspector` to inspect the resolved config interactively 5 | export default createConfigForNuxt({ 6 | features: { 7 | // Rules for module authors 8 | tooling: true, 9 | }, 10 | dirs: { 11 | src: ["./playground"], 12 | }, 13 | }).overrideRules({ 14 | "@typescript-eslint/no-explicit-any": "off", 15 | "vue/html-self-closing": "off", 16 | }); 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@kgierke/nuxt-basic-auth", 3 | "version": "1.7.0", 4 | "description": "Nuxt 3 Module for Basic Authentication", 5 | "repository": "your-org/@kgierke/nuxt-basic-auth", 6 | "license": "MIT", 7 | "type": "module", 8 | "exports": { 9 | ".": { 10 | "types": "./dist/types.d.ts", 11 | "import": "./dist/module.mjs", 12 | "require": "./dist/module.cjs" 13 | } 14 | }, 15 | "main": "./dist/module.cjs", 16 | "types": "./dist/types.d.ts", 17 | "files": [ 18 | "dist" 19 | ], 20 | "scripts": { 21 | "prepack": "nuxt-module-build build", 22 | "dev": "nuxi dev playground", 23 | "dev:build": "nuxi build playground", 24 | "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground", 25 | "release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags", 26 | "format": "prettier --write . --ignore-path .gitignore", 27 | "lint": "eslint .", 28 | "test": "vitest run", 29 | "test:watch": "vitest watch" 30 | }, 31 | "dependencies": { 32 | "@nuxt/kit": "^3.13.2", 33 | "defu": "^6.1.4" 34 | }, 35 | "devDependencies": { 36 | "@nuxt/devtools": "latest", 37 | "@nuxt/eslint-config": "^0.6.0", 38 | "@nuxt/module-builder": "^0.8.4", 39 | "@nuxt/schema": "^3.13.2", 40 | "@nuxt/test-utils": "^3.14.4", 41 | "@types/node": "^22.7.8", 42 | "changelogen": "^0.5.7", 43 | "eslint": "^9.13.0", 44 | "nuxt": "^3.13.2", 45 | "prettier": "^3.3.3", 46 | "vitest": "^2.1.3" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /playground/app.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /playground/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | export default defineNuxtConfig({ 2 | modules: ["../src/module"], 3 | basicAuth: { 4 | enabled: true, 5 | users: "test:admin,test2:admin2,test3ö:admin3", 6 | }, 7 | devtools: { enabled: true }, 8 | }); 9 | -------------------------------------------------------------------------------- /playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@kgierke/nuxt-basic-auth-playground", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "nuxi dev", 7 | "build": "nuxi build", 8 | "generate": "nuxi generate" 9 | }, 10 | "devDependencies": { 11 | "nuxt": "latest" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /playground/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nuxt/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /src/module.ts: -------------------------------------------------------------------------------- 1 | import { defineNuxtModule, createResolver, addServerHandler } from "@nuxt/kit"; 2 | import { defu } from "defu"; 3 | 4 | // Module options TypeScript interface definition 5 | export interface ModuleOptions { 6 | enabled?: boolean | string; 7 | users?: 8 | | { 9 | username: string; 10 | password: string; 11 | }[] 12 | | string; 13 | usersDelimiter?: string; 14 | allowedRoutes?: string[]; 15 | } 16 | 17 | // Runtime config TypeScript interface definition 18 | export type ModuleRuntimeConfig = ModuleOptions; 19 | 20 | export default defineNuxtModule({ 21 | meta: { 22 | name: "@kgierke/nuxt-basic-auth", 23 | configKey: "basicAuth", 24 | }, 25 | // Default configuration options of the Nuxt module 26 | defaults: { 27 | enabled: true, 28 | users: [], 29 | usersDelimiter: ",", 30 | allowedRoutes: [], 31 | }, 32 | setup(options, nuxt) { 33 | const { resolve } = createResolver(import.meta.url); 34 | 35 | /** 36 | * Add runtime config to the Nuxt instance 37 | */ 38 | nuxt.options.runtimeConfig.basicAuth = defu( 39 | nuxt.options.runtimeConfig.basicAuth || {}, 40 | options, 41 | ); 42 | 43 | /** 44 | * Add the server middleware to the Nuxt instance 45 | */ 46 | addServerHandler({ 47 | middleware: true, 48 | handler: resolve("./runtime/server/middleware/basic-auth"), 49 | }); 50 | }, 51 | }); 52 | -------------------------------------------------------------------------------- /src/runtime/plugin.ts: -------------------------------------------------------------------------------- 1 | import { defineNuxtPlugin } from "#app"; 2 | 3 | export default defineNuxtPlugin(() => { 4 | console.log("Plugin injected by @kgierke/nuxt-basic-auth!"); 5 | }); 6 | -------------------------------------------------------------------------------- /src/runtime/server/middleware/basic-auth.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @TODO: Add tests 3 | */ 4 | 5 | import { describe, it, expect } from "vitest"; 6 | 7 | describe("Basic Auth Middleware", () => { 8 | it("should be defined", () => { 9 | expect(true).toBeDefined(); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /src/runtime/server/middleware/basic-auth.ts: -------------------------------------------------------------------------------- 1 | import { useRuntimeConfig } from "#imports"; 2 | import { defineEventHandler } from "h3"; 3 | import type { ModuleRuntimeConfig } from "../../../module"; 4 | 5 | export default defineEventHandler((event) => { 6 | const config = useRuntimeConfig().basicAuth as ModuleRuntimeConfig; 7 | 8 | /** 9 | * If the request is a prerender request, do nothing. 10 | */ 11 | if ( 12 | event.node.req.headers?.["x-nitro-prerender"] && 13 | import.meta.env.NODE_ENV === "prerender" 14 | ) { 15 | return; 16 | } 17 | 18 | /** 19 | * If the module is not enabled, or no users are defined, or the current route is allowed, do nothing. 20 | */ 21 | if ( 22 | !config.enabled || 23 | (typeof config.enabled === "string" && config.enabled !== "true") || 24 | !config.users?.length || 25 | config.allowedRoutes?.some((route) => { 26 | const regex = new RegExp(route); 27 | 28 | return regex.test(event.node.req?.url || ""); 29 | }) 30 | ) { 31 | return; 32 | } 33 | 34 | let authenticated = false; 35 | 36 | /** 37 | * Get the credentials from the Authorization header. 38 | */ 39 | const credentials = event.node.req.headers?.authorization?.split(" ")[1]; 40 | 41 | /** 42 | * If the credentials are defined, check if they match any of the users. 43 | */ 44 | if (credentials) { 45 | const [username, password] = Buffer.from(credentials, "base64") 46 | .toString("utf8") 47 | .split(":"); 48 | 49 | const users = Array.isArray(config.users) 50 | ? config.users 51 | : config.users.split(config.usersDelimiter ?? ",").map((user) => { 52 | const [username, password] = user.split(":"); 53 | return { username, password }; 54 | }); 55 | 56 | authenticated = users.some( 57 | (user) => user.username === username && user.password === password, 58 | ); 59 | } 60 | 61 | /** 62 | * If the user is not authenticated or the credentials are not defined, send a 401 response. 63 | */ 64 | if (!authenticated) { 65 | event.node.res.setHeader( 66 | "WWW-Authenticate", 67 | 'Basic realm="Secure Area", charset="UTF-8"', 68 | ); 69 | event.node.res.statusCode = 401; 70 | event.node.res.end("Access denied"); 71 | } 72 | }); 73 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./playground/.nuxt/tsconfig.json" 3 | } 4 | --------------------------------------------------------------------------------