auth-verify 1.6.1 → 1.7.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/authverify.client.js +71 -0
- package/package.json +2 -2
- package/readme.md +141 -1
- package/src/otp/index.js +83 -15
- package/src/totp/index.js +5 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
window.AuthVerify = class AuthVerify {
|
|
2
|
+
constructor(options = {}){
|
|
3
|
+
this.apiBase = options.apiBase || 'http://localhost:3000';
|
|
4
|
+
this.qrContainer = options.qrEl || null;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// Fetch QR code from backend and display
|
|
8
|
+
post(url){
|
|
9
|
+
this.fetchPostUrl = url;
|
|
10
|
+
return this;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
get(url){
|
|
14
|
+
this.fetchGetUrl = url;
|
|
15
|
+
return this;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async qr() {
|
|
19
|
+
if (!this.qrContainer) return;
|
|
20
|
+
try {
|
|
21
|
+
const res = await fetch(`${this.apiBase}${this.fetchGetUrl}`);
|
|
22
|
+
const data = await res.json();
|
|
23
|
+
if (data.qr) {
|
|
24
|
+
this.qrContainer.src = data.qr;
|
|
25
|
+
} else {
|
|
26
|
+
this.showResponse('No QR received');
|
|
27
|
+
}
|
|
28
|
+
} catch (err) {
|
|
29
|
+
console.error(err);
|
|
30
|
+
this.showResponse('Error fetching QR');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
showResponse(msg){
|
|
35
|
+
console.log("[AuthVerify]", msg);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async data(payload){
|
|
39
|
+
try {
|
|
40
|
+
const res = await fetch(`${this.apiBase}${this.fetchPostUrl}`, {
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: { 'Content-Type': 'application/json' },
|
|
43
|
+
body: JSON.stringify(payload)
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const data = await res.json();
|
|
47
|
+
|
|
48
|
+
// if backend returned jwt we store it but still return whole data
|
|
49
|
+
if (data.token) {
|
|
50
|
+
this.jwt = data.token;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return data;
|
|
54
|
+
|
|
55
|
+
} catch(err){
|
|
56
|
+
console.error(err);
|
|
57
|
+
return { error: true, message: err.message };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
header(){
|
|
62
|
+
if(!this.jwt) return {};
|
|
63
|
+
return {
|
|
64
|
+
Authorization: `Bearer ${this.jwt}`
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async verify(code){
|
|
69
|
+
return this.data({code});
|
|
70
|
+
}
|
|
71
|
+
}
|
package/package.json
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
"uuid": "^9.0.1"
|
|
13
13
|
},
|
|
14
14
|
"name": "auth-verify",
|
|
15
|
-
"version": "1.
|
|
16
|
-
"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",
|
|
15
|
+
"version": "1.7.0",
|
|
16
|
+
"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/webauthn",
|
|
17
17
|
"main": "index.js",
|
|
18
18
|
"scripts": {
|
|
19
19
|
"test": "jest --runInBand"
|
package/readme.md
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
- ✅ Session management (in-memory or Redis).
|
|
9
9
|
- ✅ OAuth 2.0 integration for Google, Facebook, GitHub, X (Twitter), Linkedin, and additional providers like Apple, Discord, Slack, Microsoft, Telegram,and WhatsApp.
|
|
10
10
|
- ⚙️ Developer extensibility: custom senders via `auth.register.sender()` and chainable sending via `auth.use(name).send(...)`.
|
|
11
|
+
- ✅ Frontend client SDK (`authverify.client.js`) for browser usage: QR display, OTP verification, JWT requests, and auth headers; works without modules, just `<script>`.
|
|
11
12
|
- ✅ Automatic JWT cookie handling for Express apps, supporting secure, HTTP-only cookies and optional auto-verification.
|
|
13
|
+
- ✅ Passwordless login and registration with passkeys and webauthn.
|
|
12
14
|
- ✅ Fully asynchronous/Promise-based API, with callback support where applicable.
|
|
13
15
|
- ✅ Chainable OTP workflow with cooldowns, max attempts, and resend functionality.
|
|
14
16
|
---
|
|
@@ -247,6 +249,15 @@ auth.otp.setSender({
|
|
|
247
249
|
// if smtp service: host, port, secure (boolean)
|
|
248
250
|
});
|
|
249
251
|
|
|
252
|
+
// or you can use sender() method
|
|
253
|
+
// auth.otp.sender({
|
|
254
|
+
// via: 'email',
|
|
255
|
+
// sender: 'your@address.com',
|
|
256
|
+
// pass: 'app-password-or-smtp-pass',
|
|
257
|
+
// service: 'gmail' // or 'smtp'
|
|
258
|
+
// // if smtp service: host, port, secure (boolean)
|
|
259
|
+
// });
|
|
260
|
+
|
|
250
261
|
// sms example (the internal helper expects provider/apiKey or mock flag)
|
|
251
262
|
auth.otp.setSender({
|
|
252
263
|
via: 'sms',
|
|
@@ -283,6 +294,21 @@ auth.otp.setSender({
|
|
|
283
294
|
});
|
|
284
295
|
```
|
|
285
296
|
|
|
297
|
+
### 🛫 Simple and easy sending OTP codes
|
|
298
|
+
|
|
299
|
+
OTP codes can be simply and easily sent by `send()` method.
|
|
300
|
+
|
|
301
|
+
```js
|
|
302
|
+
auth.otp.send('johndoe@mail.com', {otpLen: 5, subject: "Email verification", html: `Your OTP code is ${auth.otp.code}`}, (err)=>{
|
|
303
|
+
if(err) console.log(err)
|
|
304
|
+
console.log('OTP sent!');
|
|
305
|
+
});
|
|
306
|
+
```
|
|
307
|
+
or you can simple use it like this:
|
|
308
|
+
```js
|
|
309
|
+
auth.otp.send('johndoe@mail.com');
|
|
310
|
+
```
|
|
311
|
+
|
|
286
312
|
### ⛓️ Generate → Save → Send (chainable)
|
|
287
313
|
|
|
288
314
|
OTP generation is chainable: `generate()` returns the OTP manager instance.
|
|
@@ -344,6 +370,13 @@ auth.otp.verify({ check: 'user@example.com', code: '123456' }, (err, isValid)=>{
|
|
|
344
370
|
if(isValid) console.log('Correct code!');
|
|
345
371
|
else console.log('Incorrect code!');
|
|
346
372
|
});
|
|
373
|
+
|
|
374
|
+
// or you can use it like this:
|
|
375
|
+
// auth.otp.verify('user@example.com','123456', (err, isValid)=>{
|
|
376
|
+
// if(err) console.log(err);
|
|
377
|
+
// if(isValid) console.log('Correct code!');
|
|
378
|
+
// else console.log('Incorrect code!');
|
|
379
|
+
// });
|
|
347
380
|
```
|
|
348
381
|
|
|
349
382
|
### Resend and cooldown / max attempts
|
|
@@ -540,7 +573,7 @@ console.log(uri);
|
|
|
540
573
|
### generate QR code image
|
|
541
574
|
(send this PNG to frontend or show in UI)
|
|
542
575
|
```js
|
|
543
|
-
const qr = await auth.totp.qrcode(uri);
|
|
576
|
+
const qr = await auth.totp.qrcode(uri); // or you can use await auth.totp.qr(uri);
|
|
544
577
|
console.log(qr); // data:image/png;base64,...
|
|
545
578
|
```
|
|
546
579
|
### generate a TOTP code
|
|
@@ -571,6 +604,112 @@ if (auth.totp.verify({ secret, token })) {
|
|
|
571
604
|
}
|
|
572
605
|
```
|
|
573
606
|
---
|
|
607
|
+
|
|
608
|
+
## auth-verify client
|
|
609
|
+
### 1️⃣ Introduction
|
|
610
|
+
|
|
611
|
+
**AuthVerify Client** is a lightweight frontend JavaScript library for TOTP / JWT authentication.
|
|
612
|
+
It works with your backend APIs to:
|
|
613
|
+
- Display QR codes for TOTP enrollment
|
|
614
|
+
- Verify user OTP codes
|
|
615
|
+
- Request JWT tokens from backend
|
|
616
|
+
- Send authenticated requests easily
|
|
617
|
+
|
|
618
|
+
Works like jQuery: just include the script in HTML, no module or bundler needed.
|
|
619
|
+
|
|
620
|
+
## 2️⃣ Installation
|
|
621
|
+
```html
|
|
622
|
+
<script src="https://cdn.jsdelivr.net/gh/jahongir2007/auth-verify/authverify.client.js"></script>
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
### 3️⃣ Initialization
|
|
626
|
+
```js
|
|
627
|
+
const qrImage = document.getElementById('qrImage');
|
|
628
|
+
|
|
629
|
+
const auth = new AuthVerify({
|
|
630
|
+
apiBase: 'http://localhost:3000', // Your backend API base URL
|
|
631
|
+
qrEl: qrImage // Image element to display QR
|
|
632
|
+
});
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
### 4️⃣ Generating QR Code
|
|
636
|
+
```js
|
|
637
|
+
auth.get('/api/qr').qr();
|
|
638
|
+
```
|
|
639
|
+
- Fetches QR code from backend
|
|
640
|
+
- Displays it in the `qrEl` image element
|
|
641
|
+
|
|
642
|
+
### 5️⃣ Sending Data / JWT Requests
|
|
643
|
+
```js
|
|
644
|
+
const payload = { name: 'John', age: 23 };
|
|
645
|
+
|
|
646
|
+
const token = await auth.post('/api/sign-jwt').data(payload);
|
|
647
|
+
console.log('JWT token:', token);
|
|
648
|
+
```
|
|
649
|
+
- `post(url)` sets endpoint
|
|
650
|
+
- `data(payload)` sends JSON payload
|
|
651
|
+
- If backend returns a token, it is stored in `auth.jwt`
|
|
652
|
+
|
|
653
|
+
### 6️⃣ Verifying OTP
|
|
654
|
+
```js
|
|
655
|
+
const result = await auth.post('/api/verify-totp').verify('123456');
|
|
656
|
+
console.log(result); // e.g. { verified: true }
|
|
657
|
+
```
|
|
658
|
+
- Wraps the OTP code in `{ code: '...' }`
|
|
659
|
+
- Sends to backend for verification
|
|
660
|
+
|
|
661
|
+
### 7️⃣ Sending Authenticated Requests
|
|
662
|
+
```js
|
|
663
|
+
const profile = await fetch('http://localhost:3000/api/profile', {
|
|
664
|
+
headers: auth.header()
|
|
665
|
+
}).then(res => res.json());
|
|
666
|
+
|
|
667
|
+
console.log(profile);
|
|
668
|
+
```
|
|
669
|
+
- `auth.header()` returns `{ Authorization: "Bearer <jwt>" }`
|
|
670
|
+
- Easy to attach JWT to any request
|
|
671
|
+
|
|
672
|
+
### 8️⃣ Method Summary
|
|
673
|
+
| Method | Description |
|
|
674
|
+
| --------------- | ----------------------------------------------- |
|
|
675
|
+
| `get(url)` | Set GET endpoint |
|
|
676
|
+
| `post(url)` | Set POST endpoint |
|
|
677
|
+
| `qr()` | Fetch QR from backend and display |
|
|
678
|
+
| `data(payload)` | Send payload to backend; stores JWT if returned |
|
|
679
|
+
| `verify(code)` | Send OTP code to backend |
|
|
680
|
+
| `header()` | Return JWT auth header object |
|
|
681
|
+
|
|
682
|
+
### 9️⃣ Example HTML
|
|
683
|
+
```html
|
|
684
|
+
<img id="qrImage" />
|
|
685
|
+
<div id="response"></div>
|
|
686
|
+
<button id="getQRBtn">Get QR</button>
|
|
687
|
+
<button id="sendBtn">Send Data</button>
|
|
688
|
+
|
|
689
|
+
<script src="authverify.client.js"></script>
|
|
690
|
+
<script>
|
|
691
|
+
const qrImage = document.getElementById('qrImage');
|
|
692
|
+
const responseDiv = document.getElementById('response');
|
|
693
|
+
|
|
694
|
+
const auth = new AuthVerify({ apiBase: 'http://localhost:3000', qrEl: qrImage });
|
|
695
|
+
|
|
696
|
+
document.getElementById('getQRBtn').addEventListener('click', () => auth.get('/api/qr').qr());
|
|
697
|
+
|
|
698
|
+
document.getElementById('sendBtn').addEventListener('click', async () => {
|
|
699
|
+
const payload = { name: 'Jahongir' };
|
|
700
|
+
const result = await auth.post('/api/sign-jwt').data(payload);
|
|
701
|
+
responseDiv.textContent = JSON.stringify(result, null, 2);
|
|
702
|
+
});
|
|
703
|
+
</script>
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
### 10️⃣ Tips for Developers
|
|
707
|
+
- Always call `auth.get('/api/qr').qr()` **after page loads**
|
|
708
|
+
- Use `auth.header()` for any authenticated request
|
|
709
|
+
- Backend must provide endpoints for `/api/qr`, `/api/verify-totp`, `/api/sign-jwt`
|
|
710
|
+
|
|
711
|
+
---
|
|
712
|
+
|
|
574
713
|
## 🌍 OAuth 2.0 Integration (v1.2.0+)
|
|
575
714
|
`auth.oauth` supports login via Google, Facebook, GitHub, X (Twitter), Linkedin, Microsoft, Telegram, Slack, WhatsApp, Apple and Discord.
|
|
576
715
|
### Providers & Routes table
|
|
@@ -917,6 +1056,7 @@ auth-verify/
|
|
|
917
1056
|
│ ├─ totpmanager.test.js
|
|
918
1057
|
│ ├─ passkeymanager.test.js
|
|
919
1058
|
├─ babel.config.js
|
|
1059
|
+
├─ authverify.client.js
|
|
920
1060
|
```
|
|
921
1061
|
|
|
922
1062
|
---
|
package/src/otp/index.js
CHANGED
|
@@ -27,6 +27,16 @@ class OTPManager {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
if(otpOptions.sender){
|
|
31
|
+
this.senderVia = otpOptions.sender.via;
|
|
32
|
+
this.senderService = otpOptions.sender.service;
|
|
33
|
+
this.senderMail = otpOptions.sender.sender;
|
|
34
|
+
this.senderPass = otpOptions.sender.pass;
|
|
35
|
+
this.senderHost = otpOptions.sender.host;
|
|
36
|
+
this.senderPort = otpOptions.sender.port;
|
|
37
|
+
this.senderSecure = otpOptions.sender.secure;
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
}
|
|
31
41
|
|
|
32
42
|
// generate(length = 6, callback) {
|
|
@@ -50,6 +60,11 @@ class OTPManager {
|
|
|
50
60
|
this.senderConfig = config;
|
|
51
61
|
}
|
|
52
62
|
|
|
63
|
+
sender(config){
|
|
64
|
+
if(!config.via) throw new Error("⚠️ Sender type { via } is required. It shouldbe 'email' or 'sms' or 'telegram'");
|
|
65
|
+
this.senderConfig = config;
|
|
66
|
+
}
|
|
67
|
+
|
|
53
68
|
async set(receiverEmailorPhone, callback){
|
|
54
69
|
const expiryInSeconds = Math.floor(this.otpExpiry / 1000);
|
|
55
70
|
|
|
@@ -534,33 +549,39 @@ class OTPManager {
|
|
|
534
549
|
// return this._verifyInternal(identifier, code);
|
|
535
550
|
// }
|
|
536
551
|
|
|
537
|
-
async verify(
|
|
552
|
+
async verify(options, code, callback) {
|
|
553
|
+
|
|
538
554
|
const handleError = (err) => {
|
|
539
|
-
|
|
540
|
-
if (err.message
|
|
541
|
-
else if (err.message.includes("Invalid")) err = new Error("Invalid OTP");
|
|
555
|
+
if (err.message?.includes("expired")) return new Error("OTP expired");
|
|
556
|
+
if (err.message?.includes("Invalid")) return new Error("Invalid OTP");
|
|
542
557
|
return err;
|
|
543
558
|
};
|
|
544
559
|
|
|
545
|
-
//
|
|
546
|
-
if (
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
560
|
+
// shape normalize
|
|
561
|
+
if (typeof options === "string" && typeof code === "string") {
|
|
562
|
+
// options as check string
|
|
563
|
+
options = { check: options, code: code };
|
|
564
|
+
code = undefined; // remove
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// callback detect
|
|
568
|
+
if (typeof code === "function") {
|
|
569
|
+
callback = code;
|
|
553
570
|
}
|
|
554
571
|
|
|
555
|
-
// promise style
|
|
556
572
|
try {
|
|
557
|
-
|
|
573
|
+
const res = await this._verifyInternal(options.check, options.code);
|
|
574
|
+
if (callback) return callback(null, res);
|
|
575
|
+
return res;
|
|
558
576
|
} catch (err) {
|
|
559
|
-
|
|
577
|
+
err = handleError(err);
|
|
578
|
+
if (callback) return callback(err);
|
|
579
|
+
throw err;
|
|
560
580
|
}
|
|
561
581
|
}
|
|
562
582
|
|
|
563
583
|
|
|
584
|
+
|
|
564
585
|
// helper used by verify()
|
|
565
586
|
// async _verifyInternal(identifier, code) {
|
|
566
587
|
// // memory
|
|
@@ -1118,7 +1139,54 @@ class OTPManager {
|
|
|
1118
1139
|
console.log("🚀 Telegram verification bot ready!");
|
|
1119
1140
|
}
|
|
1120
1141
|
|
|
1142
|
+
async send(reciever, mailOption , callback){
|
|
1121
1143
|
|
|
1144
|
+
if(typeof mailOption == 'function'){
|
|
1145
|
+
callback = mailOption;
|
|
1146
|
+
mailOption = {}
|
|
1147
|
+
}else if(!mailOption){
|
|
1148
|
+
mailOption = {};
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
const sendProcess = async () => {
|
|
1152
|
+
// const otpCode = this.generate(mailOption.otpLen = 6).set(reciever);
|
|
1153
|
+
// console.log(otpCode);
|
|
1154
|
+
if(this.senderConfig.via == 'email'){
|
|
1155
|
+
await this.#sendEmail(reciever, mailOption);
|
|
1156
|
+
}else if(this.senderConfig.via == 'sms'){
|
|
1157
|
+
await this.#sendSMS(reciever, mailOption);
|
|
1158
|
+
}else {
|
|
1159
|
+
throw new Error("senderConfig.via should be 'email' or 'sms'")
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
if(callback) sendProcess().then(result => callback(null, result)).catch(error => callback(error));
|
|
1164
|
+
else return sendProcess();
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
#sendEmail(reciever, mailOption){
|
|
1168
|
+
return this.generate(mailOption.otpLen).set(reciever, (err)=>{
|
|
1169
|
+
if(err) throw err
|
|
1170
|
+
|
|
1171
|
+
this.message({
|
|
1172
|
+
to: reciever,
|
|
1173
|
+
subject: mailOption.subject || "Your OTP code",
|
|
1174
|
+
text: mailOption.text || `Your OTP code is ${this.code}`,
|
|
1175
|
+
html: mailOption.html || `Your OTP code is ${this.code}`
|
|
1176
|
+
});
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
#sendSMS(reciever, code, smsOption){
|
|
1181
|
+
return this.generate(smsOption.otpLen).set(reciever, (err)=>{
|
|
1182
|
+
if(err) throw err;
|
|
1183
|
+
|
|
1184
|
+
this.message({
|
|
1185
|
+
to: reciever,
|
|
1186
|
+
text: smsOption.text || `Your OTP code is ${code}`
|
|
1187
|
+
});
|
|
1188
|
+
});
|
|
1189
|
+
}
|
|
1122
1190
|
}
|
|
1123
1191
|
|
|
1124
1192
|
module.exports = OTPManager;
|
package/src/totp/index.js
CHANGED
|
@@ -43,6 +43,11 @@ class TOTPManager {
|
|
|
43
43
|
return await qrcode.toDataURL(uri);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
async qr(uri) {
|
|
47
|
+
const qrcode = require("qrcode");
|
|
48
|
+
return await qrcode.toDataURL(uri);
|
|
49
|
+
}
|
|
50
|
+
|
|
46
51
|
// internal HOTP algorithm
|
|
47
52
|
_hotp(secret, counter) {
|
|
48
53
|
const key = base32.decode(secret);
|