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 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.12.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"&lt;b&gt;Your OTP code is: {otp}&lt;/b&gt;"
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">&lt;?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" => "&lt;b&gt;Your OTP code is: {$otp['code']}&lt;/b&gt;"
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("&lt;b&gt;Your OTP code is: %s&lt;/b&gt;", 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&lt;String,Object&gt; 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&lt;String,Object&gt; 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", "&lt;b&gt;Your OTP code is: "+code+"&lt;/b&gt;"
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&lt;string, string&gt;{
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", $"&lt;b&gt;Your OTP code is: {code}&lt;/b&gt;"}
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: "&lt;b&gt;Your OTP code is: #{code}&lt;/b&gt;"
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> &copy; 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>