-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 51bd5b0
Showing
11 changed files
with
1,132 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
PORT = | ||
MONGODB_URI = | ||
OTP_VALIDITY_PERIOD_MINUTES = 5 | ||
OTP_SIZE = 4 | ||
# every 2 minutes | ||
# CRON_SCHEDULE = */2 * * * * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
# 📱 OTP Service | ||
|
||
This is a free OTP (One-Time Password) service built with Node.js, Express.js, Mongoose, and node-cron for handling OTP generation, verification, and automatic expiration. | ||
|
||
## Features | ||
|
||
✨ Generate a one-time password (OTP) for a given email. | ||
|
||
🔐 Verify an OTP for a given email. | ||
|
||
⏰ Automatic OTP expiration and cleanup using cron jobs. | ||
|
||
⚙️ Configurable OTP size and validity period. | ||
|
||
🚀 Error handling for invalid OTPs and expired OTPs. | ||
|
||
## Installation | ||
|
||
1. Clone the repository: | ||
|
||
```bash | ||
git clone https://github.com/sauravhathi/otp-service.git | ||
cd otp-service | ||
``` | ||
|
||
2. Install dependencies: | ||
|
||
```bash | ||
npm install | ||
|
||
# or | ||
|
||
yarn | ||
``` | ||
|
||
3. Set up your environment variables by creating a `.env` file in the project root directory and configuring the following variables: | ||
|
||
```env | ||
MONGODB_URI=<your-mongodb-connection-uri> | ||
OTP_VALIDITY_PERIOD_MINUTES=2 | ||
OTP_SIZE=4 | ||
CRON_SCHEDULE=*/2 * * * * | ||
``` | ||
|
||
- `MONGODB_URI`: MongoDB connection URI. | ||
- `OTP_VALIDITY_PERIOD_MINUTES`: Validity period for OTPs in minutes. | ||
- `OTP_SIZE`: Size of the OTP (number of digits). | ||
- `CRON_SCHEDULE`: Cron schedule for automatic OTP cleanup. | ||
|
||
4. Start the server: | ||
|
||
```bash | ||
npm dev | ||
|
||
# or | ||
|
||
yarn dev | ||
``` | ||
|
||
## API Endpoints | ||
|
||
### Generate OTP 🚀 | ||
|
||
- **Endpoint**: POST `/api/otp` | ||
- **Request Body**: | ||
```json | ||
{ | ||
"email": "[email protected]" | ||
} | ||
``` | ||
- **Response**: | ||
```json | ||
{ | ||
"otp": 1234 | ||
} | ||
``` | ||
|
||
### Verify OTP 🔐 | ||
|
||
- **Endpoint**: POST `/api/otp/verify` | ||
- **Request Body**: | ||
```json | ||
{ | ||
"email": "[email protected]", | ||
"otp": 1234 | ||
} | ||
``` | ||
- **Response**: | ||
```json | ||
{ | ||
"message": "OTP is valid" | ||
} | ||
``` | ||
|
||
## Scheduled OTP Cleanup ⏰ | ||
|
||
The service automatically clears expired OTPs based on the configured cron schedule. | ||
|
||
## Donate ☕ | ||
|
||
If you find this project useful and want to support its development, consider buying us a coffee! | ||
|
||
<img src="https://github.com/sauravhathi/myperfectice-extension/assets/61316762/274f2172-8dcc-4fe9-aa51-fd3542429c3e" alt="support" style="width: 200px"> | ||
|
||
Donate: `saurav.34@paytm` | ||
|
||
<a href="https://www.buymeacoffee.com/sauravhathi" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/arial-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a> | ||
|
||
## License | ||
|
||
This project is licensed under the MIT License. See the [LICENSE](https://github.com/sauravhathi/otp-service/blob/main/LICENSE) file for details. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
const Otp = require('../models/otpModel'); | ||
const validityPeriodMinutes = process.env.OTP_VALIDITY_PERIOD_MINUTES; | ||
|
||
const generateOTP = (size) => { | ||
if (size < 1 || size > 10) { | ||
throw new Error('OTP size must be between 1 and 10 digits.'); | ||
} | ||
|
||
const min = 10 ** (size - 1); | ||
const max = 10 ** size - 1; | ||
return Math.floor(Math.random() * (max - min + 1)) + min; | ||
} | ||
|
||
const otpController = { | ||
generateOtp: async (email) => { | ||
try { | ||
// Check if an OTP has already been generated for this email | ||
const existingOtp = await Otp.findOne({ | ||
email: email, | ||
createdAt: { | ||
$gte: new Date(new Date() - validityPeriodMinutes * 60 * 1000), // Calculate the time window | ||
}, | ||
}); | ||
|
||
if (existingOtp) { | ||
return existingOtp.otp; | ||
} | ||
|
||
const otp = generateOTP(process.env.OTP_SIZE); | ||
|
||
const otpDocument = new Otp({ | ||
id: new Date().getTime(), | ||
email: email, | ||
otp: otp, | ||
createdAt: new Date(), | ||
}); | ||
|
||
await otpDocument.save(); | ||
|
||
return otp; | ||
} catch (error) { | ||
throw new Error('Failed to generate OTP'); | ||
} | ||
}, | ||
verifyOtp: async (email, otp) => { | ||
try { | ||
|
||
if (otp.toString().length !== parseInt(process.env.OTP_SIZE)) { | ||
throw new Error('Invalid OTP'); | ||
} | ||
|
||
const otpDocument = await Otp.findOneAndDelete({ | ||
email: email, | ||
otp: otp, | ||
createdAt: { $gte: new Date(new Date() - 1000 * 60 * validityPeriodMinutes) } | ||
}); | ||
|
||
if (!otpDocument) { | ||
throw new Error('Invalid OTP'); | ||
} | ||
|
||
return true; | ||
} catch (error) { | ||
throw new Error(error.message); | ||
} | ||
}, | ||
clearExpiredOtps: async () => { | ||
try { | ||
// Clear expired OTPs | ||
const cutoffTime = new Date(new Date() - 1000 * 60 * validityPeriodMinutes); | ||
await Otp.deleteMany({ createdAt: { $lt: cutoffTime } }); | ||
} catch (error) { | ||
throw new Error('Failed to clear expired OTPs'); | ||
} | ||
}, | ||
}; | ||
|
||
module.exports = otpController; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
const cron = require('node-cron'); | ||
const otpController = require('./otpController'); | ||
const cronSchedule = process.env.CRON_SCHEDULE || '0 0 * * *'; | ||
|
||
// Clear expired OTPs every day at midnight by default | ||
cron.schedule(cronSchedule, async () => { | ||
try { | ||
await otpController.clearExpiredOtps(); | ||
console.log('Cleared expired OTPs'); | ||
} catch (error) { | ||
console.log(error.message); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
const mongoose = require('mongoose'); | ||
|
||
const otpSchema = new mongoose.Schema({ | ||
id: String, | ||
email: String, | ||
otp: Number, | ||
createdAt: Date, | ||
}); | ||
|
||
const Otp = mongoose.model('Otp', otpSchema); | ||
|
||
module.exports = Otp; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
const express = require('express'); | ||
const otpController = require('../controllers/otpController'); | ||
|
||
const router = express.Router(); | ||
|
||
router.post('/otp', async (req, res) => { | ||
try {const { email } = req.body; | ||
|
||
const otp = await otpController.generateOtp(email); | ||
|
||
console.log("generate otp", otp); | ||
|
||
res.status(200).json({ otp }); | ||
} catch (error) { | ||
res.status(400).json({ error: error.message }); | ||
} | ||
}); | ||
|
||
router.post('/otp/verify', async (req, res) => { | ||
try { | ||
const { email, otp } = req.body; | ||
await otpController.verifyOtp(email, otp); | ||
|
||
res.status(200).json({ message: 'OTP is valid' }); | ||
} catch (error) { | ||
res.status(400).json({ error: error.message }); | ||
} | ||
}); | ||
|
||
module.exports = router; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
const isValidEmail = (email) => { | ||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | ||
return emailRegex.test(email); | ||
} | ||
|
||
module.exports = {isValidEmail} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
const express = require('express'); | ||
const cors = require('cors'); | ||
const mongoose = require('mongoose'); | ||
const dotenv = require('dotenv'); | ||
dotenv.config(); | ||
|
||
const app = express(); | ||
|
||
mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true }); | ||
const db = mongoose.connection; | ||
db.on('error', console.error.bind(console, 'MongoDB connection error:')); | ||
db.once('open', () => { | ||
console.log('Connected to MongoDB'); | ||
}); | ||
|
||
app.use(cors()); | ||
app.use(express.json()); | ||
|
||
const {isValidEmail} = require('./app/utils/validator'); | ||
const otpRoutes = require('./app/routes/otpRoutes'); | ||
const schedulerController = require('./app/controllers/schedulerController'); | ||
|
||
const middleware = (req, res, next) => { | ||
const { email } = req.body; | ||
if (!isValidEmail(email)) { | ||
console.log('middleware'); | ||
return res.status(400).json({ error: 'Invalid email' }); | ||
} | ||
next(); | ||
}; | ||
|
||
app.use('/api', middleware, otpRoutes); | ||
|
||
const PORT = process.env.PORT || 3000; | ||
app.listen(PORT, () => { | ||
console.log(`Server is running on port ${PORT}`); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"name": "otp-service", | ||
"version": "1.0.0", | ||
"main": "index.js", | ||
"description": "This is a free OTP (One-Time Password) service built with Node.js, Express.js, Mongoose, and node-cron for handling OTP generation, verification, and automatic expiration.", | ||
"author": "Saurav Hathi", | ||
"repository": "https://github.com/sauravhathi/otp-service", | ||
"license": "MIT", | ||
"scripts": { | ||
"start": "node index.js", | ||
"dev": "nodemon index.js" | ||
}, | ||
"dependencies": { | ||
"cors": "^2.8.5", | ||
"dotenv": "^16.3.1", | ||
"express": "^4.18.2", | ||
"mongoose": "^7.5.0", | ||
"node-cron": "^3.0.2" | ||
}, | ||
"devDependencies": { | ||
"nodemon": "^3.0.1" | ||
} | ||
} |
Oops, something went wrong.