{"_id":"mern-access","_rev":"37-4c09999096bb7637ea714628b2f2758c","name":"mern-access","dist-tags":{"latest":"2.1.3"},"versions":{"2.1.1":{"name":"mern-access","version":"2.1.1","keywords":["mern","auth","authentication","jwt","otp","refresh-tokens","express","mongoose","node"],"author":{"name":"Sbonelo Dube","email":"sbonelosthole97@gmail.com"},"license":"MIT","_id":"mern-access@2.1.1","maintainers":[{"name":"sbonelosth","email":"sbonelosthole97@gmail.com"}],"homepage":"https://github.com/sbonelosth/mern-access#readme","bugs":{"url":"https://github.com/sbonelosth/mern-access/issues"},"bin":{"mern-access":"cli.js"},"dist":{"shasum":"63ec763e4d38aa47f48c35f499e12170bec4903e","tarball":"https://registry.npmjs.org/mern-access/-/mern-access-2.1.1.tgz","fileCount":10,"integrity":"sha512-ncmnqk3IBIQB5gLbuyKV2jn5BrQOmhWiwDb63aRPBYDgfapHVfYYcwx8ByLqBP3+Y4XOQW84Y7VzPq2/B2d6Hg==","signatures":[{"sig":"MEUCIQC+G8ibUPP8Xg5hhwjyE7gzqJ+lgSJbYku5avto9ytc9QIgHbagS696cDbV5yVPRLL03gAgb3orck84igOOANJRZvQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":30261},"main":"index.js","gitHead":"44190a725ff9d4a27daf43e6754837c3949bf82b","scripts":{"test":"echo \"No tests specified\" && exit 0"},"_npmUser":{"name":"sbonelosth","email":"sbonelosthole97@gmail.com"},"repository":{"url":"git+https://github.com/sbonelosth/mern-access.git","type":"git"},"_npmVersion":"10.9.2","description":"Plug-and-play authentication module for MERN apps (OTP + JWT, refresh rotation, revocation).","directories":{},"_nodeVersion":"22.16.0","dependencies":{"ms":"^2.1.3","bcrypt":"^5.1.1","jsonwebtoken":"^9.0.2"},"_hasShrinkwrap":false,"peerDependencies":{"express":">=4.18.0","mongoose":">=7.0.0"},"optionalDependencies":{"nodemailer":"^6.9.13"},"_npmOperationalInternal":{"tmp":"tmp/mern-access_2.1.1_1757216968090_0.44318761506473625","host":"s3://npm-registry-packages-npm-production"}},"2.1.2":{"name":"mern-access","version":"2.1.2","keywords":["mern","auth","authentication","jwt","otp","refresh-tokens","express","mongoose","node"],"author":{"name":"Sbonelo Dube","email":"sbonelosthole97@gmail.com"},"license":"MIT","_id":"mern-access@2.1.2","maintainers":[{"name":"sithembile","email":"sthembilevezi04@gmail.com"},{"name":"sbonelo","email":"sbonelosthole97@gmail.com"}],"homepage":"https://github.com/sbonelosth/mern-access#readme","bugs":{"url":"https://github.com/sbonelosth/mern-access/issues"},"bin":{"mern-access":"cli.js"},"dist":{"shasum":"0e9230f02b3d896ee60fa9f18ec7d0bde7f08e7b","tarball":"https://registry.npmjs.org/mern-access/-/mern-access-2.1.2.tgz","fileCount":10,"integrity":"sha512-vLAZ8d23JhdiOeeQqWBikJc9iMtmrxfU44cHdK18vlXqDgtU6d6w2y6YYvnRtpEuZQ5cczx5M705j5bC99hU1g==","signatures":[{"sig":"MEQCIEiOy9ff1egYT9tPvsZ114o8rBgk3xIF+47kNjYYCTvBAiBoMZeriYJYCPP4KM+xjqFU1QxPtPIV+TG3XDuv+YTacw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":35837},"main":"index.js","gitHead":"0197a8f7af65660cc7f72e2a48233b1ce97d5ee5","scripts":{"test":"echo \"No tests specified\" && exit 0"},"_npmUser":{"name":"sbonelo","email":"sbonelosthole97@gmail.com"},"repository":{"url":"git+https://github.com/sbonelosth/mern-access.git","type":"git"},"_npmVersion":"10.9.2","description":"Plug-and-play authentication module for MERN apps (OTP + JWT, refresh rotation, revocation).","directories":{},"_nodeVersion":"22.16.0","dependencies":{"ms":"^2.1.3","bcrypt":"^5.1.1","jsonwebtoken":"^9.0.2"},"_hasShrinkwrap":false,"peerDependencies":{"express":">=4.18.0","mongoose":">=7.0.0"},"optionalDependencies":{"nodemailer":"^6.9.13"},"_npmOperationalInternal":{"tmp":"tmp/mern-access_2.1.2_1760291939740_0.3760673228517786","host":"s3://npm-registry-packages-npm-production"}},"2.1.3":{"name":"mern-access","version":"2.1.3","description":"Plug-and-play authentication module for MERN apps (OTP + JWT, refresh rotation, revocation).","main":"index.js","license":"MIT","author":{"name":"Sbonelo Dube","email":"sbonelosthole97@gmail.com"},"repository":{"type":"git","url":"git+https://github.com/sbonelosth/mern-access.git"},"keywords":["mern","auth","authentication","jwt","otp","refresh-tokens","express","mongoose","node"],"bin":{"mern-access":"cli.js"},"scripts":{"test":"echo \"No tests specified\" && exit 0"},"dependencies":{"bcrypt":"^5.1.1","jsonwebtoken":"^9.0.2","ms":"^2.1.3"},"peerDependencies":{"express":">=4.18.0","mongoose":">=7.0.0"},"optionalDependencies":{"nodemailer":"^6.9.13"},"_id":"mern-access@2.1.3","gitHead":"a27a739ae35e524a37e25078e05bbc537a11edff","bugs":{"url":"https://github.com/sbonelosth/mern-access/issues"},"homepage":"https://github.com/sbonelosth/mern-access#readme","_nodeVersion":"22.16.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-a351Uzv9EnfYpMsoh/DkGxJUKQ8IQjrJoa76TijlkIuMzu9ADSyE6oHUUzEKf5k8ipZP06phRdumkHxNqFjVKg==","shasum":"50797d94a3b813a5cd25a30596cc302fc7d5281e","tarball":"https://registry.npmjs.org/mern-access/-/mern-access-2.1.3.tgz","fileCount":10,"unpackedSize":36340,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGSsjlRjCaS+C3mUtmfpRpXxSsZaTUayIJw+HaprSyMQAiBj5EfRyxccIh8euWR0oj3o93deKjFvrStZMeUUCC0tPA=="}]},"_npmUser":{"name":"sbonelo","email":"sbonelosthole97@gmail.com"},"directories":{},"maintainers":[{"name":"sithembile","email":"sthembilevezi04@gmail.com"},{"name":"sbonelo","email":"sbonelosthole97@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mern-access_2.1.3_1762149244067_0.8698835238332885"},"_hasShrinkwrap":false}},"time":{"created":"2025-08-24T23:22:28.754Z","modified":"2025-11-03T05:54:04.538Z","1.0.0":"2025-08-24T23:22:29.064Z","1.0.1":"2025-08-25T10:01:20.212Z","1.0.2":"2025-08-25T10:20:23.186Z","2.0.0":"2025-09-01T21:15:22.834Z","2.0.2":"2025-09-01T21:25:53.822Z","2.0.3":"2025-09-01T21:34:16.485Z","2.0.4":"2025-09-01T21:38:19.132Z","2.0.5":"2025-09-01T21:41:54.708Z","2.0.6":"2025-09-01T22:16:12.215Z","2.0.7":"2025-09-02T00:58:06.933Z","2.0.8":"2025-09-02T01:42:04.097Z","2.0.9":"2025-09-02T09:01:48.815Z","2.0.10":"2025-09-02T09:42:33.926Z","2.0.11":"2025-09-02T17:47:21.403Z","2.0.12":"2025-09-02T23:17:08.549Z","2.1.0":"2025-09-02T23:51:25.998Z","2.1.1":"2025-09-07T03:49:28.291Z","2.1.2":"2025-10-12T17:58:59.917Z","2.1.3":"2025-11-03T05:54:04.303Z"},"bugs":{"url":"https://github.com/sbonelosth/mern-access/issues"},"author":{"name":"Sbonelo Dube","email":"sbonelosthole97@gmail.com"},"license":"MIT","homepage":"https://github.com/sbonelosth/mern-access#readme","keywords":["mern","auth","authentication","jwt","otp","refresh-tokens","express","mongoose","node"],"repository":{"type":"git","url":"git+https://github.com/sbonelosth/mern-access.git"},"description":"Plug-and-play authentication module for MERN apps (OTP + JWT, refresh rotation, revocation).","maintainers":[{"name":"sithembile","email":"sthembilevezi04@gmail.com"},{"name":"sbonelo","email":"sbonelosthole97@gmail.com"}],"readme":"# mern-access\n\n```mern-access``` is a plug-and-play authentication and authorization solution for MERN applications. It provides a ready-made set of Express routes, middleware, and utilities for handling user sign-up, login, email verification via OTP, password reset, and session management — all wired up with a MongoDB + Mongoose connection.\n\n---\n\n## 1. Quick Start (Scaffold a New Project)\n\nRun one of the following on an empty project folder to create a ready-to-use auth setup:\n\n```bash\n# Without nodemailer\nnpx mern-access init\n\n# With nodemailer\nnpx mern-access init --with-nodemailer\n```\n\nThis generates the following structure:\n\n```\nmy-app/\n├── models/\n│   └── User.js\n├── auth.config.js\n├── index.js\n├── .env\n├── package.json\n├── node_modules/\n└── ...\n```\n\n---\n\n## 2. File Samples\n\n### models/User.js\n```js\nconst mongoose = require(\"mongoose\");\n\nconst refreshTokenSchema = new mongoose.Schema({\n  tokenHash: { type: String, index: true },\n  userAgent: String,\n  ip: String,\n  createdAt: { type: Date, default: Date.now },\n  expiresAt: { type: Date, index: true }\n}, { _id: false });\n\nconst UserSchema = new mongoose.Schema({\n  email: { type: String, unique: true, index: true, required: true },\n  username: { type: String, unique: true, index: true, required: true },\n  password: { type: String, required: true },\n  role: { type: String, default: \"user\" },\n  isEmailVerified: { type: Boolean, default: false },\n  otp: String,\n  otpExpiry: Date,\n  refreshTokens: { type: [refreshTokenSchema], default: [] }\n}, { timestamps: true });\n\nmodule.exports = mongoose.models.User || mongoose.model(\"User\", UserSchema);\n```\n\n---\n\n### auth.config.js\n```js\nrequire(\"dotenv\").config();\nconst User = require(\"./models/User\");\n\nmodule.exports = {\n  User,\n  jwt: {\n    accessSecret: process.env.ACCESS_SECRET,\n    refreshSecret: process.env.REFRESH_SECRET,\n    accessExpiry: \"1h\",\n    refreshExpiry: \"1d\"\n  },\n  email: {\n    subject: \"Email Verification Code\",\n    body: ({ username, otp, otpExpiry }) =>\n      `<p>Hello ${username}, your code is <b>${otp}</b>${otpExpiry ? \" and valid for \" + otpExpiry : \"\"}.</p>`\n  },\n  otpExpiry: \"1h\",\n  async sendEmail({ to, subject, html }) {\n    // Console version (default). If using nodemailer, replace with transporter.sendMail.\n    console.log(\"EMAIL SENT:\", to, subject, html);\n  },\n  mapUserData: (user) => ({\n    username: user.username,\n    email: user.email,\n    role: user.role,\n    isEmailVerified: user.isEmailVerified,\n    createdAt: user.createdAt,\n    updatedAt: user.updatedAt,\n  })\n};\n```\n---\n\n### Notes\n\n```sendEmail``` can be any async function (nodemailer, external API, or console).\n\n```mapUserData``` controls what user info is exposed in responses.\n\n---\n\n### index.js\n```js\nconst express = require(\"express\");\nconst cors = require(\"cors\");\nconst mongoose = require(\"mongoose\");\nconst { initMernAccess } = require(\"mern-access\");\nconst config = require(\"./auth.config\");\n\n(async () => {\n  try {\n    await mongoose.connect(process.env.MONGO_URI);\n    console.log(\"✅ Connected to MongoDB\");\n\n    const { router, protect } = initMernAccess(config);\n\n    const app = express();\n    app.use(express.json());\n    app.use(cors({ origin: true, credentials: true }));\n\n    app.use(\"/auth\", router);\n\n    // Example protected route\n    app.get(\"/me\", protect, (req, res) => {\n      res.json({ ok: true, user: req.user });\n    });\n\n    const PORT = process.env.PORT || 4001;\n    app.listen(PORT, () => console.log(`🚀 Server running on http://localhost:${PORT}`));\n  } catch (err) {\n    console.error(\"❌ [mern-access] connection failed:\", err.message);\n    process.exit(1);\n  }\n})();\n```\n\n---\n\n### .env\n```env\nACCESS_SECRET=your-random-access-secret\nREFRESH_SECRET=your-random-refresh-secret\nMONGO_URI=mongodb://localhost:27017/yourdb\nPORT=4001\n\n# Email (SMTP)\nSMTP_HOST=smtp.yourprovider.com\nSMTP_PORT=587\nSMTP_SECURE=false\nSMTP_USER=your@email.com\nSMTP_PASS=yourpassword\nEMAIL_FROM=\"Your App <no-reply@yourapp.com>\"\n```\n\n---\n\n## 3. Setup\n\n1. Replace values in `.env` with your actual secrets & SMTP config.\n2. Run the dev server:\n\n```bash\nnpm run dev\n```\n\n---\n\n## 4. Routes\n\n### POST `/auth/signup`\n**Description:** Create new user and send OTP  \n**Body:**  \n```json\n{\n  \"email\": \"alice@example.com\",\n  \"username\": \"alice\",\n  \"password\": \"Secret123\",\n  \"role\": \"user\" // optional\n}\n```\n**Returns:**  \n```json\n{\n  \"success\": true,\n  \"user\": { \"username\": \"...\", \"email\": \"...\", \"role\": \"...\", \"isEmailVerified\": false, ... },\n  \"accessToken\": \"...\",\n  \"message\": \"Signup successful. Verification code sent.\"\n}\n```\n\n---\n\n### POST `/auth/verify`\n**Description:**  \n- **Send or resend OTP:**  \n  **Body:**  \n  ```json\n  { \"email\": \"alice@example.com\" }\n  ```\n  **Returns:**  \n  ```json\n  {\n    \"user\": { ... },\n    \"success\": true,\n    \"isOtpSent\": true,\n    \"message\": \"New verification code sent\"\n  }\n  ```\n\n- **Send or resend OTP by username:**  \n  **Body:**  \n  ```json\n  { \"id\": \"alice\" }\n  ```\n\n- **Verify OTP:**  \n  **Body:**  \n  ```json\n  { \"id\": \"alice@example.com\", \"otp\": \"123456\" }\n  ```\n  **Returns:**  \n  ```json\n  {\n    \"success\": true,\n    \"user\": { ... },\n    \"accessToken\": \"...\",\n    \"message\": \"Account verified\"\n  }\n  ```\n\n---\n\n### POST `/auth/login`\n**Description:** Login with email or username and password  \n**Body:**  \n```json\n{\n  \"id\": \"alice@example.com\", // or \"alice\"\n  \"password\": \"Secret123\"\n}\n```\n**Returns:**  \n```json\n{\n  \"success\": true,\n  \"user\": { ... },\n  \"accessToken\": \"...\",\n  \"message\": \"Login successful\"\n}\n```\n\n---\n\n### POST `/auth/access`\n**Description:** Refresh access token using access token  \n**Headers:**  \n`Authorization: Bearer <accessToken>`  \n**Returns:**  \n```json\n{\n  \"success\": true,\n  \"user\": { ... },\n  \"accessToken\": \"...\",\n  \"message\": \"Access token renewed\"\n}\n```\n\n---\n\n### POST `/auth/reset-password`\n**Description:**  \n- **Send or resend OTP for password reset:**  \n  **Body:**  \n  ```json\n  { \"id\": \"alice@example.com\" }\n  ```\n  *(No `otp` field means send code)*  \n  **Returns:**  \n  ```json\n  {\n    \"user\": { ... },\n    \"success\": true,\n    \"isOtpSent\": true,\n    \"message\": \"Password reset code sent\"\n  }\n  ```\n\n- **Reset password with OTP:**  \n  **Body:**  \n  ```json\n  { \"id\": \"alice@example.com\", \"otp\": \"123456\", \"newPwd\": \"NewSecret123\" }\n  ```\n  **Returns:**  \n  ```json\n  {\n    \"success\": true,\n    \"message\": \"Password reset successful\"\n  }\n  ```\n\n---\n\n### POST `/auth/logout-everywhere`\n**Description:** Logout from all sessions  \n**Body:**  \n```json\n{ \"username\": \"alice\" }\n```\n**Returns:**  \n```json\n{\n  \"success\": true,\n  \"message\": \"Logged out from all devices\"\n}\n```\n\n---\n\n## curl Examples\n\nSignup:\n```bash\ncurl -X POST http://localhost:4001/auth/signup -H \"Content-Type: application/json\" -d '{\"email\":\"alice@example.com\",\"username\":\"alice\",\"password\":\"Secret123\"}'\n```\n\nVerify (send/resend code):\n```bash\ncurl -X POST http://localhost:4001/auth/verify -H \"Content-Type: application/json\" -d '{\"email\":\"alice@example.com\"}'\n```\n\nVerify (with OTP):\n```bash\ncurl -X POST http://localhost:4001/auth/verify -H \"Content-Type: application/json\" -d '{\"email\":\"alice@example.com\",\"otp\":\"123456\"}'\n```\n\nLogin:\n```bash\ncurl -X POST http://localhost:4001/auth/login -H \"Content-Type: application/json\" -d '{\"id\":\"alice\",\"password\":\"Secret123\"}'\n```\n\nReset password (send code):\n```bash\ncurl -X POST http://localhost:4001/auth/reset-password -H \"Content-Type: application/json\" -d '{\"id\":\"alice@example.com\",\"newPwd\":\"NewSecret123\"}'\n```\n\nReset password (with OTP):\n```bash\ncurl -X POST http://localhost:4001/auth/reset-password -H \"Content-Type: application/json\" -d '{\"id\":\"alice@example.com\",\"otp\":\"123456\",\"newPwd\":\"NewSecret123\"}'\n```\n\n---\n\n## 5. Using in an Existing Project\n\nInstall the package:\n\n```bash\nnpm install mern-access\n```\n\nAdd config:\n\n```js\nconst { initMernAccess } = require(\"mern-access\");\nconst config = require(\"./auth.config\");\n\n// Connect to MongoDB before initializing mern-access\nconst { router, protect } = initMernAccess(config);\napp.use(\"/auth\", router);\n```\n\nProtect routes:\n\n```js\napp.get(\"/me\", protect, (req, res) => {\n  res.json({ user: req.user });\n});\n```\n\n---\n\n## License\n\nMIT License\n\nCopyright (c) 2025","readmeFilename":"README.md"}