auth-verify 1.12.1 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/readme.md +13 -1
- package/src/otp/index.js +62 -3
- package/rest-api/index.js +0 -196
- package/rest-api/public/index.html +0 -376
package/package.json
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"express": "^5.1.0",
|
|
9
9
|
"ioredis": "^5.8.1",
|
|
10
10
|
"jsonwebtoken": "^9.0.2",
|
|
11
|
+
"mailage": "^0.0.1",
|
|
11
12
|
"node-telegram-bot-api": "^0.66.0",
|
|
12
13
|
"nodemailer": "^7.0.6",
|
|
13
14
|
"path": "^0.12.7",
|
|
@@ -15,7 +16,7 @@
|
|
|
15
16
|
"uuid": "^9.0.1"
|
|
16
17
|
},
|
|
17
18
|
"name": "auth-verify",
|
|
18
|
-
"version": "1.
|
|
19
|
+
"version": "1.13.0",
|
|
19
20
|
"description": "A simple Node.js library for sending and verifying OTP via email, SMS and Telegram bot. And generating TOTP codes and QR codes. And handling JWT with Cookies. And also handling passwordless logins with passkeys/webauth. And handling magiclink passwordless logins",
|
|
20
21
|
"main": "index.js",
|
|
21
22
|
"scripts": {
|
package/readme.md
CHANGED
|
@@ -343,6 +343,18 @@ otp.sender({
|
|
|
343
343
|
secure: false
|
|
344
344
|
})
|
|
345
345
|
```
|
|
346
|
+
|
|
347
|
+
#### Using API
|
|
348
|
+
```js
|
|
349
|
+
otp.sender({
|
|
350
|
+
via: "email",
|
|
351
|
+
service: "api",
|
|
352
|
+
sender: "your_email@gmail.com",
|
|
353
|
+
apiService: "resend", // "postmark", "sendgrid"
|
|
354
|
+
apiKey: "your-api-key"
|
|
355
|
+
})
|
|
356
|
+
```
|
|
357
|
+
|
|
346
358
|
#### SMS sender
|
|
347
359
|
#### 📘 Auth-Verify SMS API — Full Guide
|
|
348
360
|
Auth-Verify supports sending OTPs via SMS using **30+ global SMS providers**.
|
|
@@ -1827,4 +1839,4 @@ auth-verify/
|
|
|
1827
1839
|
|
|
1828
1840
|
Contributions welcome! Open issues / PRs for bugs, improvements, or API suggestions.
|
|
1829
1841
|
|
|
1830
|
-
MIT © 2025 — Jahongir Sobirov
|
|
1842
|
+
MIT © 2025 - 2026 — Jahongir Sobirov
|
package/src/otp/index.js
CHANGED
|
@@ -2,6 +2,7 @@ const {generateSecureOTP, resendGeneratedOTP, sendSMS} = require('../helpers/hel
|
|
|
2
2
|
const Redis = require("ioredis");
|
|
3
3
|
const nodemailer = require("nodemailer");
|
|
4
4
|
const TelegramBot = require("node-telegram-bot-api");
|
|
5
|
+
const { Transport, Resend, SendGrid, Postmark } = require('mailage')
|
|
5
6
|
|
|
6
7
|
class OTPManager {
|
|
7
8
|
constructor(otpOptions = {}){
|
|
@@ -35,6 +36,10 @@ class OTPManager {
|
|
|
35
36
|
this.senderHost = otpOptions.sender.host;
|
|
36
37
|
this.senderPort = otpOptions.sender.port;
|
|
37
38
|
this.senderSecure = otpOptions.sender.secure;
|
|
39
|
+
this.senderApiService = otpOptions.sender.api.service;
|
|
40
|
+
this.senderApiKey = otpOptions.sender.api.key;
|
|
41
|
+
this.senderApiSecret = otpOptions.sender.api.secret;
|
|
42
|
+
this.senderApiUrl = otpOptions.sender.api.url;
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
}
|
|
@@ -167,6 +172,44 @@ class OTPManager {
|
|
|
167
172
|
maxConnections: 3,
|
|
168
173
|
maxMessages: 50
|
|
169
174
|
});
|
|
175
|
+
}else if(this.senderConfig.service === 'api'){
|
|
176
|
+
if(this.senderConfig.apiService === 'sendgrid'){
|
|
177
|
+
|
|
178
|
+
(async function(){
|
|
179
|
+
const apiMail = new Transport(
|
|
180
|
+
new SendGrid({
|
|
181
|
+
apiKey: this.senderConfig.apiKey,
|
|
182
|
+
sender: this.senderConfig.sender
|
|
183
|
+
})
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
apiMail.send({to,subject,text, html})
|
|
187
|
+
})()
|
|
188
|
+
}else if(this.senderConfig.apiService === 'resend'){
|
|
189
|
+
(async function() {
|
|
190
|
+
const apiMail = new Transport(
|
|
191
|
+
new Resend({
|
|
192
|
+
apiKey: this.senderConfig.apiKey,
|
|
193
|
+
sender: this.senderConfig.sender
|
|
194
|
+
})
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
apiMail.send({to, subject, text, html})
|
|
198
|
+
})()
|
|
199
|
+
}else if(this.senderConfig.apiService === 'postmark'){
|
|
200
|
+
(async function(params) {
|
|
201
|
+
const apiMail = new Transport(
|
|
202
|
+
new Postmark({
|
|
203
|
+
apiKey: this.senderConfig.apiKey,
|
|
204
|
+
sender: this.senderConfig.sender
|
|
205
|
+
})
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
apiMail.send({to, subject, text, html})
|
|
209
|
+
})()
|
|
210
|
+
}else{
|
|
211
|
+
throw new Error(`Unsupported email service: ${this.senderConfig.apiService}`);
|
|
212
|
+
}
|
|
170
213
|
} else {
|
|
171
214
|
throw new Error(`Unsupported email service: ${this.senderConfig.service}`);
|
|
172
215
|
}
|
|
@@ -547,7 +590,10 @@ class OTPManager {
|
|
|
547
590
|
}else if(this.senderConfig.via === 'whatsapp'){
|
|
548
591
|
await this.#sendWhatsApp(reciever, mailOption);
|
|
549
592
|
return true;
|
|
550
|
-
}else {
|
|
593
|
+
}else if(this.senderConfig.via === 'api'){
|
|
594
|
+
await this.#sendEmailApi(reciever, mailOption);
|
|
595
|
+
return true;
|
|
596
|
+
}else{
|
|
551
597
|
throw new Error("senderConfig.via should be 'email' or 'sms'")
|
|
552
598
|
}
|
|
553
599
|
}
|
|
@@ -558,15 +604,15 @@ class OTPManager {
|
|
|
558
604
|
|
|
559
605
|
#sendEmail(reciever, mailOption){
|
|
560
606
|
return this.generate(mailOption.otpLen).set(reciever, (err)=>{
|
|
561
|
-
if(err) throw err
|
|
607
|
+
if(err) throw err;
|
|
562
608
|
|
|
563
609
|
this.message({
|
|
564
610
|
to: reciever,
|
|
565
611
|
subject: mailOption.subject || "Your OTP code",
|
|
566
612
|
text: mailOption.text || `Your OTP code is ${this.code}`,
|
|
567
613
|
html: mailOption.html || `Your OTP code is ${this.code}`
|
|
614
|
+
});
|
|
568
615
|
});
|
|
569
|
-
});
|
|
570
616
|
}
|
|
571
617
|
|
|
572
618
|
#sendSMS(reciever, smsOption){
|
|
@@ -590,6 +636,19 @@ class OTPManager {
|
|
|
590
636
|
});
|
|
591
637
|
});
|
|
592
638
|
}
|
|
639
|
+
|
|
640
|
+
#sendEmailApi(reciever, mailOption){
|
|
641
|
+
return this.generate(mailOption.otpLen).set(reciever, (err)=> {
|
|
642
|
+
if(err) throw err;
|
|
643
|
+
|
|
644
|
+
this.message({
|
|
645
|
+
to: reciever,
|
|
646
|
+
subject: mailOption.subject || "Your OTP code",
|
|
647
|
+
text: mailOption.text || `Your OTP code is ${this.code}`,
|
|
648
|
+
html: mailOption.html || `Your OTP code is ${this.code}`
|
|
649
|
+
})
|
|
650
|
+
})
|
|
651
|
+
}
|
|
593
652
|
}
|
|
594
653
|
|
|
595
654
|
module.exports = OTPManager;
|
package/rest-api/index.js
DELETED
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
const express = require("express");
|
|
2
|
-
const cors = require("cors");
|
|
3
|
-
const AuthVerify = require("../index");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
|
|
6
|
-
const app = express();
|
|
7
|
-
app.use(cors());
|
|
8
|
-
app.use(express.json());
|
|
9
|
-
app.use(express.static("public"));
|
|
10
|
-
|
|
11
|
-
const router = express.Router(); // FIXED
|
|
12
|
-
|
|
13
|
-
const auth = new AuthVerify();
|
|
14
|
-
|
|
15
|
-
// If user enters any route ("/", "/docs", etc) send homepage
|
|
16
|
-
app.get("/", (req, res) => {
|
|
17
|
-
res.sendFile(path.join(__dirname, "public/index.html"));
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
// Generating OTP (just generate code, no sending)
|
|
21
|
-
router.post('/auth/otp/generate/:leng', async (req, res) => {
|
|
22
|
-
try {
|
|
23
|
-
let { leng } = req.params;
|
|
24
|
-
const length = leng ? parseInt(leng) : 6; // default 6 digits if undefined
|
|
25
|
-
|
|
26
|
-
const otp = auth.otp.generate(length);
|
|
27
|
-
|
|
28
|
-
return res.json({
|
|
29
|
-
ok: true,
|
|
30
|
-
code: otp.code,
|
|
31
|
-
length
|
|
32
|
-
});
|
|
33
|
-
} catch (err) {
|
|
34
|
-
console.error(err);
|
|
35
|
-
return res.status(500).json({ ok: false, error: "Failed to generate OTP", message: err.message });
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// ----------- Gmail Provider -------------
|
|
41
|
-
router.post('/auth/otp/send/gmail/:to', async (req, res) => {
|
|
42
|
-
try {
|
|
43
|
-
const { to } = req.params;
|
|
44
|
-
const { email, pass, subject, text, html } = req.body.sender || {};
|
|
45
|
-
|
|
46
|
-
if (!email || !pass) {
|
|
47
|
-
return res.status(400).json({ error: "Missing sender email or password" });
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
auth.otp.sender({
|
|
51
|
-
via: 'email',
|
|
52
|
-
service: 'gmail',
|
|
53
|
-
sender: email,
|
|
54
|
-
pass
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
const info = await auth.otp.send(to, {
|
|
58
|
-
subject: subject || "Your OTP Code",
|
|
59
|
-
text: text || "",
|
|
60
|
-
html: html || ""
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
return res.json({
|
|
64
|
-
ok: true,
|
|
65
|
-
provider: "gmail",
|
|
66
|
-
to
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
} catch (err) {
|
|
70
|
-
console.error(err);
|
|
71
|
-
return res.status(500).json({ error: "Failed to send OTP" });
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
// ----------- Brevo Provider -------------
|
|
76
|
-
router.post('/auth/otp/send/email/brevo/:to', async (req, res) => {
|
|
77
|
-
try {
|
|
78
|
-
const { to } = req.params;
|
|
79
|
-
const { name, email, subject, text, html, apikey } = req.body.sender || {};
|
|
80
|
-
|
|
81
|
-
if (!email || !apikey) {
|
|
82
|
-
return res.status(400).json({ error: "Missing email or Brevo API key" });
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const otp = auth.otp.generate(6);
|
|
86
|
-
|
|
87
|
-
const payload = {
|
|
88
|
-
sender: { name: name || "AuthVerify", email },
|
|
89
|
-
to: [{ email: to }],
|
|
90
|
-
subject: subject || "Your OTP Code",
|
|
91
|
-
htmlContent: html || `<b>Your OTP code is: ${otp.code}</b>`,
|
|
92
|
-
textContent: text || `Your OTP code is: ${otp.code}`
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const response = await axios.post("https://api.brevo.com/v3/smtp/email", payload, {
|
|
96
|
-
headers: { "api-key": apikey, "Content-Type": "application/json" }
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
return res.json({ ok: true, provider: "brevo", to, code: otp.code, providerData: response.data });
|
|
100
|
-
} catch (err) {
|
|
101
|
-
console.error(err);
|
|
102
|
-
return res.status(500).json({ error: "Failed to send OTP" });
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
router.post('/auth/otp/send/email/sendgrid/:to', async (req, res) => {
|
|
107
|
-
try {
|
|
108
|
-
const { to } = req.params;
|
|
109
|
-
const { apikey, subject, text, html } = req.body.sender || {};
|
|
110
|
-
|
|
111
|
-
if (!apikey) return res.status(400).json({ error: "Missing SendGrid API key" });
|
|
112
|
-
|
|
113
|
-
const otp = auth.otp.generate(6);
|
|
114
|
-
|
|
115
|
-
const payload = {
|
|
116
|
-
personalizations: [{ to: [{ email: to }] }],
|
|
117
|
-
from: { email: "no-reply@yourdomain.com", name: "AuthVerify" },
|
|
118
|
-
subject: subject || "Your OTP Code",
|
|
119
|
-
content: [
|
|
120
|
-
{ type: "text/plain", value: text || `Your OTP code is: ${otp.code}` },
|
|
121
|
-
{ type: "text/html", value: html || `<b>Your OTP code is: ${otp.code}</b>` }
|
|
122
|
-
]
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
const response = await axios.post("https://api.sendgrid.com/v3/mail/send", payload, {
|
|
126
|
-
headers: { "Authorization": `Bearer ${apikey}`, "Content-Type": "application/json" }
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
return res.json({ ok: true, provider: "sendgrid", to, code: otp.code, providerData: response.data });
|
|
130
|
-
} catch (err) {
|
|
131
|
-
console.error(err);
|
|
132
|
-
return res.status(500).json({ error: "Failed to send OTP via SendGrid" });
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
// ------ OTP SMS -------
|
|
138
|
-
router.post('/auth/otp/send/sms/:to', async (req, res)=>{
|
|
139
|
-
try {
|
|
140
|
-
const { to } = req.params;
|
|
141
|
-
const { provider, apiKey, apiSecret, sender, text } = req.body.sender || {};
|
|
142
|
-
|
|
143
|
-
if(!provider || !apiKey || !apiSecret || !sender) return res.status(400).json({error: "Missing sender provider or apiKey or apiSecret or sender"});
|
|
144
|
-
|
|
145
|
-
auth.otp.sender({
|
|
146
|
-
via: 'sms',
|
|
147
|
-
provider,
|
|
148
|
-
apiKey,
|
|
149
|
-
apiSecret,
|
|
150
|
-
sender
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
const info = await auth.otp.send(to, {text});
|
|
154
|
-
|
|
155
|
-
return res.json({
|
|
156
|
-
ok: true,
|
|
157
|
-
provider,
|
|
158
|
-
to
|
|
159
|
-
});
|
|
160
|
-
} catch(err){
|
|
161
|
-
console.error(err);
|
|
162
|
-
return res.status(500).json({ error: "Failed to send OTP with SMS" });
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
// Verify OTP sent by email
|
|
167
|
-
router.post('/auth/otp/verify/:to', async (req, res) => {
|
|
168
|
-
try {
|
|
169
|
-
const { to } = req.params;
|
|
170
|
-
const { otp } = req.body;
|
|
171
|
-
|
|
172
|
-
if (!otp) {
|
|
173
|
-
return res.status(400).json({ success: false, message: "Missing OTP code" });
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const valid = await auth.otp.verify(to, otp);
|
|
177
|
-
|
|
178
|
-
if (valid) {
|
|
179
|
-
return res.status(200).json({ success: true, message: "OTP verified", to });
|
|
180
|
-
} else {
|
|
181
|
-
return res.status(400).json({ success: false, message: "OTP is incorrect", to });
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
} catch (err) {
|
|
185
|
-
console.error(err);
|
|
186
|
-
return res.status(500).json({ success: false, message: "Failed to verify OTP", error: err.message });
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
// mount router
|
|
192
|
-
app.use("/api", router); // IMPORTANT!!
|
|
193
|
-
|
|
194
|
-
app.listen(3000, () => {
|
|
195
|
-
console.log("REST API running on port 3000");
|
|
196
|
-
});
|
|
@@ -1,376 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en" data-theme="dark">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
6
|
-
<title>Auth-verify REST API Guide</title>
|
|
7
|
-
|
|
8
|
-
<!-- Tailwind + DaisyUI (dark mode) -->
|
|
9
|
-
<script src="https://cdn.tailwindcss.com"></script>
|
|
10
|
-
<link href="https://cdn.jsdelivr.net/npm/daisyui@4.12.10/dist/full.min.css" rel="stylesheet" type="text/css" />
|
|
11
|
-
|
|
12
|
-
<!-- Highlight.js (Monokai-Sublime) -->
|
|
13
|
-
<link rel="stylesheet"
|
|
14
|
-
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/monokai-sublime.min.css"/>
|
|
15
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
|
16
|
-
<script>hljs.highlightAll();</script>
|
|
17
|
-
|
|
18
|
-
<style>
|
|
19
|
-
pre code { @apply rounded-lg p-4 overflow-x-auto; }
|
|
20
|
-
.copy-btn { @apply btn btn-xs btn-ghost absolute top-2 right-2; }
|
|
21
|
-
</style>
|
|
22
|
-
</head>
|
|
23
|
-
|
|
24
|
-
<body class="bg-base-100 text-base-content min-h-screen">
|
|
25
|
-
|
|
26
|
-
<!-- Header -->
|
|
27
|
-
<header class="bg-primary text-primary-content py-12">
|
|
28
|
-
<div class="container mx-auto text-center">
|
|
29
|
-
<h1 class="text-4xl md:text-5xl font-bold mb-3">Auth-verify REST API Guide</h1>
|
|
30
|
-
<p class="text-lg opacity-90">
|
|
31
|
-
Generate, send (Gmail / SMS) and verify OTPs – examples in 6 languages
|
|
32
|
-
</p>
|
|
33
|
-
</div>
|
|
34
|
-
</header>
|
|
35
|
-
|
|
36
|
-
<div class="container mx-auto px-4 py-10">
|
|
37
|
-
|
|
38
|
-
<!-- Intro -->
|
|
39
|
-
<section class="prose prose-invert max-w-none mb-12">
|
|
40
|
-
<h2>Overview</h2>
|
|
41
|
-
<p>All requests go to <code class="bg-base-300 px-2 py-1 rounded">http://localhost:3000/api</code> (replace with your production URL).</p>
|
|
42
|
-
<ul class="list-disc pl-6">
|
|
43
|
-
<li><strong>Generate:</strong> <code>POST /auth/otp/generate/{length}</code></li>
|
|
44
|
-
<li><strong>Gmail:</strong> <code>POST /auth/otp/send/gmail/{to_email}</code></li>
|
|
45
|
-
<li><strong>SMS:</strong> <code>POST /auth/otp/send/sms/{to_phone}</code></li>
|
|
46
|
-
<li><strong>Verify:</strong> <code>POST /auth/otp/verify/{to}</code></li>
|
|
47
|
-
</ul>
|
|
48
|
-
<div class="alert alert-info mt-4">
|
|
49
|
-
<strong>Never</strong> hard-code credentials. Use environment variables (shown in each example).
|
|
50
|
-
</div>
|
|
51
|
-
</section>
|
|
52
|
-
|
|
53
|
-
<!-- Language Tabs -->
|
|
54
|
-
<div class="tabs tabs-boxed mb-6">
|
|
55
|
-
<a class="tab tab-active" data-lang="python">Python</a>
|
|
56
|
-
<a class="tab" data-lang="php">PHP</a>
|
|
57
|
-
<a class="tab" data-lang="go">Go</a>
|
|
58
|
-
<a class="tab" data-lang="java">Java</a>
|
|
59
|
-
<a class="tab" data-lang="csharp">C#</a>
|
|
60
|
-
<a class="tab" data-lang="ruby">Ruby</a>
|
|
61
|
-
</div>
|
|
62
|
-
|
|
63
|
-
<!-- Code Panels -->
|
|
64
|
-
<div id="code-panels" class="space-y-8">
|
|
65
|
-
|
|
66
|
-
<!-- ==================== PYTHON ==================== -->
|
|
67
|
-
<div class="panel" data-lang="python">
|
|
68
|
-
<h3 class="text-2xl font-semibold mb-3">Python (requests)</h3>
|
|
69
|
-
<div class="relative">
|
|
70
|
-
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
71
|
-
<pre><code class="language-python">import os
|
|
72
|
-
import requests
|
|
73
|
-
from dotenv import load_dotenv # pip install python-dotenv
|
|
74
|
-
|
|
75
|
-
load_dotenv() # reads .env file
|
|
76
|
-
BASE_URL = os.getenv("OTP_BASE_URL", "http://localhost:3000/api")
|
|
77
|
-
GMAIL_USER = os.getenv("GMAIL_USER")
|
|
78
|
-
GMAIL_PASS = os.getenv("GMAIL_APP_PASS")
|
|
79
|
-
TEST_EMAIL = os.getenv("TEST_EMAIL")
|
|
80
|
-
|
|
81
|
-
def generate_otp(length=6):
|
|
82
|
-
r = requests.post(f"{BASE_URL}/auth/otp/generate/{length}")
|
|
83
|
-
r.raise_for_status()
|
|
84
|
-
return r.json()
|
|
85
|
-
|
|
86
|
-
def send_otp_gmail(to_email, otp):
|
|
87
|
-
payload = {
|
|
88
|
-
"sender": {
|
|
89
|
-
"email": GMAIL_USER,
|
|
90
|
-
"pass": GMAIL_PASS,
|
|
91
|
-
"subject": "Your OTP Code",
|
|
92
|
-
"text": f"Your OTP code is: {otp}",
|
|
93
|
-
"html": f"<b>Your OTP code is: {otp}</b>"
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
r = requests.post(f"{BASE_URL}/auth/otp/send/gmail/{to_email}", json=payload)
|
|
97
|
-
r.raise_for_status()
|
|
98
|
-
return r.json()
|
|
99
|
-
|
|
100
|
-
def verify_otp(to, otp):
|
|
101
|
-
r = requests.post(f"{BASE_URL}/auth/otp/verify/{to}", json={"otp": otp})
|
|
102
|
-
r.raise_for_status()
|
|
103
|
-
return r.json()
|
|
104
|
-
|
|
105
|
-
if __name__ == "__main__":
|
|
106
|
-
otp_data = generate_otp()
|
|
107
|
-
code = otp_data.get("code")
|
|
108
|
-
print("OTP:", code)
|
|
109
|
-
|
|
110
|
-
send_otp_gmail(TEST_EMAIL, code)
|
|
111
|
-
# verify_otp(TEST_EMAIL, code)
|
|
112
|
-
</code></pre>
|
|
113
|
-
</div>
|
|
114
|
-
</div>
|
|
115
|
-
|
|
116
|
-
<!-- ==================== PHP ==================== -->
|
|
117
|
-
<div class="panel hidden" data-lang="php">
|
|
118
|
-
<h3 class="text-2xl font-semibold mb-3">PHP (cURL)</h3>
|
|
119
|
-
<div class="relative">
|
|
120
|
-
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
121
|
-
<pre><code class="language-php"><?php
|
|
122
|
-
putenv('GMAIL_USER=jahongir.nodirovich.2007@gmail.com');
|
|
123
|
-
putenv('GMAIL_APP_PASS=YOUR_APP_PASSWORD');
|
|
124
|
-
putenv('TEST_EMAIL=jahongir.sobirov.2007@mail.ru');
|
|
125
|
-
|
|
126
|
-
$BASE_URL = getenv('OTP_BASE_URL') ?: 'http://localhost:3000/api';
|
|
127
|
-
|
|
128
|
-
function post($url, $data = null) {
|
|
129
|
-
$ch = curl_init($url);
|
|
130
|
-
curl_setopt_array($ch, [
|
|
131
|
-
CURLOPT_RETURNTRANSFER => true,
|
|
132
|
-
CURLOPT_POST => true,
|
|
133
|
-
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
|
|
134
|
-
CURLOPT_POSTFIELDS => $data ? json_encode($data) : null
|
|
135
|
-
]);
|
|
136
|
-
$out = curl_exec($ch);
|
|
137
|
-
curl_close($ch);
|
|
138
|
-
return json_decode($out, true);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
$otp = post("$BASE_URL/auth/otp/generate/6");
|
|
142
|
-
echo "OTP: " . $otp['code'] . PHP_EOL;
|
|
143
|
-
|
|
144
|
-
$payload = [
|
|
145
|
-
"sender" => [
|
|
146
|
-
"email" => getenv('GMAIL_USER'),
|
|
147
|
-
"pass" => getenv('GMAIL_APP_PASS'),
|
|
148
|
-
"subject"=> "Your OTP Code",
|
|
149
|
-
"text" => "Your OTP code is: {$otp['code']}",
|
|
150
|
-
"html" => "<b>Your OTP code is: {$otp['code']}</b>"
|
|
151
|
-
]
|
|
152
|
-
];
|
|
153
|
-
post("$BASE_URL/auth/otp/send/gmail/" . getenv('TEST_EMAIL'), $payload);
|
|
154
|
-
</code></pre>
|
|
155
|
-
</div>
|
|
156
|
-
</div>
|
|
157
|
-
|
|
158
|
-
<!-- ==================== GO ==================== -->
|
|
159
|
-
<div class="panel hidden" data-lang="go">
|
|
160
|
-
<h3 class="text-2xl font-semibold mb-3">Go (net/http)</h3>
|
|
161
|
-
<div class="relative">
|
|
162
|
-
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
163
|
-
<pre><code class="language-go">package main
|
|
164
|
-
|
|
165
|
-
import (
|
|
166
|
-
"encoding/json"
|
|
167
|
-
"fmt"
|
|
168
|
-
"net/http"
|
|
169
|
-
"os"
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
var baseURL = os.Getenv("OTP_BASE_URL")
|
|
173
|
-
if baseURL == "" { baseURL = "http://localhost:3000/api" }
|
|
174
|
-
|
|
175
|
-
func postJSON(url string, payload interface{}) map[string]interface{} {
|
|
176
|
-
b, _ := json.Marshal(payload)
|
|
177
|
-
resp, _ := http.Post(url, "application/json", bytes.NewBuffer(b))
|
|
178
|
-
defer resp.Body.Close()
|
|
179
|
-
var out map[string]interface{}
|
|
180
|
-
json.NewDecoder(resp.Body).Decode(&out)
|
|
181
|
-
return out
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
func main() {
|
|
185
|
-
// 1. Generate
|
|
186
|
-
otp := postJSON(fmt.Sprintf("%s/auth/otp/generate/6", baseURL), nil)
|
|
187
|
-
code := otp["code"].(string)
|
|
188
|
-
fmt.Println("OTP:", code)
|
|
189
|
-
|
|
190
|
-
// 2. Gmail
|
|
191
|
-
gmailPayload := map[string]interface{}{
|
|
192
|
-
"sender": map[string]string{
|
|
193
|
-
"email": os.Getenv("GMAIL_USER"),
|
|
194
|
-
"pass": os.Getenv("GMAIL_APP_PASS"),
|
|
195
|
-
"subject": "Your OTP Code",
|
|
196
|
-
"text": fmt.Sprintf("Your OTP code is: %s", code),
|
|
197
|
-
"html": fmt.Sprintf("<b>Your OTP code is: %s</b>", code),
|
|
198
|
-
},
|
|
199
|
-
}
|
|
200
|
-
postJSON(fmt.Sprintf("%s/auth/otp/send/gmail/%s", baseURL, os.Getenv("TEST_EMAIL")), gmailPayload)
|
|
201
|
-
}
|
|
202
|
-
</code></pre>
|
|
203
|
-
</div>
|
|
204
|
-
</div>
|
|
205
|
-
|
|
206
|
-
<!-- ==================== JAVA ==================== -->
|
|
207
|
-
<div class="panel hidden" data-lang="java">
|
|
208
|
-
<h3 class="text-2xl font-semibold mb-3">Java (HttpClient)</h3>
|
|
209
|
-
<div class="relative">
|
|
210
|
-
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
211
|
-
<pre><code class="language-java">import java.net.URI;
|
|
212
|
-
import java.net.http.*;
|
|
213
|
-
import java.util.Map;
|
|
214
|
-
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
215
|
-
|
|
216
|
-
public class OTPDemo {
|
|
217
|
-
private static final String BASE = System.getenv("OTP_BASE_URL") != null ?
|
|
218
|
-
System.getenv("OTP_BASE_URL") : "http://localhost:3000/api";
|
|
219
|
-
private static final HttpClient client = HttpClient.newHttpClient();
|
|
220
|
-
private static final ObjectMapper mapper = new ObjectMapper();
|
|
221
|
-
|
|
222
|
-
public static void main(String[] args) throws Exception {
|
|
223
|
-
// 1. Generate
|
|
224
|
-
var genReq = HttpRequest.newBuilder()
|
|
225
|
-
.uri(URI.create(BASE + "/auth/otp/generate/6"))
|
|
226
|
-
.POST(HttpRequest.BodyPublishers.noBody())
|
|
227
|
-
.build();
|
|
228
|
-
var genResp = client.send(genReq, HttpResponse.BodyHandlers.ofString());
|
|
229
|
-
@SuppressWarnings("unchecked")
|
|
230
|
-
Map<String,Object> otp = mapper.readValue(genResp.body(), Map.class);
|
|
231
|
-
String code = (String) otp.get("code");
|
|
232
|
-
System.out.println("OTP: " + code);
|
|
233
|
-
|
|
234
|
-
// 2. Gmail
|
|
235
|
-
Map<String,Object> sender = Map.of(
|
|
236
|
-
"email", System.getenv("GMAIL_USER"),
|
|
237
|
-
"pass", System.getenv("GMAIL_APP_PASS"),
|
|
238
|
-
"subject","Your OTP Code",
|
|
239
|
-
"text", "Your OTP code is: "+code,
|
|
240
|
-
"html", "<b>Your OTP code is: "+code+"</b>"
|
|
241
|
-
);
|
|
242
|
-
var payload = Map.of("sender", sender);
|
|
243
|
-
var gmailReq = HttpRequest.newBuilder()
|
|
244
|
-
.uri(URI.create(BASE + "/auth/otp/send/gmail/" + System.getenv("TEST_EMAIL")))
|
|
245
|
-
.header("Content-Type","application/json")
|
|
246
|
-
.POST(HttpRequest.BodyPublishers.ofString(mapper.writeValueAsString(payload)))
|
|
247
|
-
.build();
|
|
248
|
-
client.send(gmailReq, HttpResponse.BodyHandlers.ofString());
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
</code></pre>
|
|
252
|
-
</div>
|
|
253
|
-
</div>
|
|
254
|
-
|
|
255
|
-
<!-- ==================== C# ==================== -->
|
|
256
|
-
<div class="panel hidden" data-lang="csharp">
|
|
257
|
-
<h3 class="text-2xl font-semibold mb-3">C# (HttpClient)</h3>
|
|
258
|
-
<div class="relative">
|
|
259
|
-
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
260
|
-
<pre><code class="language-csharp">using System;
|
|
261
|
-
using System.Net.Http;
|
|
262
|
-
using System.Text;
|
|
263
|
-
using System.Threading.Tasks;
|
|
264
|
-
using Newtonsoft.Json;
|
|
265
|
-
using System.Collections.Generic;
|
|
266
|
-
|
|
267
|
-
class Program {
|
|
268
|
-
static readonly HttpClient client = new HttpClient();
|
|
269
|
-
static readonly string baseUrl = Environment.GetEnvironmentVariable("OTP_BASE_URL") ?? "http://localhost:3000/api";
|
|
270
|
-
|
|
271
|
-
static async Task Main() {
|
|
272
|
-
// 1. Generate
|
|
273
|
-
var otpResp = await client.PostAsync($"{baseUrl}/auth/otp/generate/6", null);
|
|
274
|
-
var otpJson = await otpResp.Content.ReadAsStringAsync();
|
|
275
|
-
dynamic otp = JsonConvert.DeserializeObject(otpJson);
|
|
276
|
-
string code = otp.code;
|
|
277
|
-
Console.WriteLine($"OTP: {code}");
|
|
278
|
-
|
|
279
|
-
// 2. Gmail
|
|
280
|
-
var sender = new Dictionary<string, string>{
|
|
281
|
-
{"email", Environment.GetEnvironmentVariable("GMAIL_USER")},
|
|
282
|
-
{"pass", Environment.GetEnvironmentVariable("GMAIL_APP_PASS")},
|
|
283
|
-
{"subject","Your OTP Code"},
|
|
284
|
-
{"text", $"Your OTP code is: {code}"},
|
|
285
|
-
{"html", $"<b>Your OTP code is: {code}</b>"}
|
|
286
|
-
};
|
|
287
|
-
var payload = new { sender };
|
|
288
|
-
var content = new StringContent(JsonConvert.SerializeObject(payload), Encoding.UTF8, "application/json");
|
|
289
|
-
await client.PostAsync($"{baseUrl}/auth/otp/send/gmail/{Environment.GetEnvironmentVariable("TEST_EMAIL")}", content);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
</code></pre>
|
|
293
|
-
</div>
|
|
294
|
-
</div>
|
|
295
|
-
|
|
296
|
-
<!-- ==================== RUBY ==================== -->
|
|
297
|
-
<div class="panel hidden" data-lang="ruby">
|
|
298
|
-
<h3 class="text-2xl font-semibold mb-3">Ruby (net/http)</h3>
|
|
299
|
-
<div class="relative">
|
|
300
|
-
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
301
|
-
<pre><code class="language-ruby">require 'net/http'
|
|
302
|
-
require 'json'
|
|
303
|
-
require 'uri'
|
|
304
|
-
|
|
305
|
-
BASE = ENV['OTP_BASE_URL'] || 'http://localhost:3000/api'
|
|
306
|
-
GMAIL = ENV['GMAIL_USER']
|
|
307
|
-
PASS = ENV['GMAIL_APP_PASS']
|
|
308
|
-
EMAIL = ENV['TEST_EMAIL']
|
|
309
|
-
|
|
310
|
-
def post(uri, payload = nil)
|
|
311
|
-
req = Net::HTTP::Post.new(uri)
|
|
312
|
-
req['Content-Type'] = 'application/json'
|
|
313
|
-
req.body = payload.to_json if payload
|
|
314
|
-
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
|
315
|
-
http.request(req)
|
|
316
|
-
end
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
# 1. Generate
|
|
320
|
-
uri = URI("#{BASE}/auth/otp/generate/6")
|
|
321
|
-
otp = JSON.parse(post(uri).body)
|
|
322
|
-
code = otp['code']
|
|
323
|
-
puts "OTP: #{code}"
|
|
324
|
-
|
|
325
|
-
# 2. Gmail
|
|
326
|
-
payload = {
|
|
327
|
-
sender: {
|
|
328
|
-
email: GMAIL,
|
|
329
|
-
pass: PASS,
|
|
330
|
-
subject: 'Your OTP Code',
|
|
331
|
-
text: "Your OTP code is: #{code}",
|
|
332
|
-
html: "<b>Your OTP code is: #{code}</b>"
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
post(URI("#{BASE}/auth/otp/send/gmail/#{EMAIL}"), payload)
|
|
336
|
-
</code></pre>
|
|
337
|
-
</div>
|
|
338
|
-
</div>
|
|
339
|
-
|
|
340
|
-
</div>
|
|
341
|
-
</div>
|
|
342
|
-
|
|
343
|
-
<!-- Footer -->
|
|
344
|
-
<footer class="footer footer-center p-6 bg-base-300 text-base-content">
|
|
345
|
-
<div>
|
|
346
|
-
<p>Auth-verify REST API Guide <span class="font-bold">by <a class="nav-link" href="https://github.com/Jahongir2007">Jahongir Sobirov</a> © 2025</span></p>
|
|
347
|
-
</div>
|
|
348
|
-
</footer>
|
|
349
|
-
|
|
350
|
-
<!-- JS: tab switching + copy -->
|
|
351
|
-
<script>
|
|
352
|
-
// Tab handling
|
|
353
|
-
document.querySelectorAll('.tab').forEach(tab => {
|
|
354
|
-
tab.addEventListener('click', () => {
|
|
355
|
-
document.querySelectorAll('.tab').forEach(t => t.classList.remove('tab-active'));
|
|
356
|
-
tab.classList.add('tab-active');
|
|
357
|
-
|
|
358
|
-
const lang = tab.getAttribute('data-lang');
|
|
359
|
-
document.querySelectorAll('.panel').forEach(p => {
|
|
360
|
-
p.classList.toggle('hidden', p.getAttribute('data-lang') !== lang);
|
|
361
|
-
});
|
|
362
|
-
});
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
// Copy button
|
|
366
|
-
function copyCode(btn) {
|
|
367
|
-
const code = btn.parentElement.querySelector('pre code').innerText;
|
|
368
|
-
navigator.clipboard.writeText(code).then(() => {
|
|
369
|
-
const original = btn.innerHTML;
|
|
370
|
-
btn.innerHTML = 'Copied!';
|
|
371
|
-
setTimeout(() => btn.innerHTML = original, 1500);
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
</script>
|
|
375
|
-
</body>
|
|
376
|
-
</html>
|