auth-verify 1.10.0 → 1.11.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/auth-verify.client.js +0 -91
- package/package.json +9 -3
- package/readme.md +25 -14
- package/rest-api/index.js +171 -0
- package/rest-api/public/index.html +376 -0
- package/src/helpers/helper.js +0 -35
- package/src/jwt/index.js +0 -244
- package/src/magiclink/index.js +8 -2
- package/src/otp/index.js +6 -638
- package/tests/cryptomanager.test.js +0 -77
- package/tests/jwa.test.js +0 -45
- package/tests/jwt.middleware.test.js +0 -89
- package/tests/jwtmanager.multitab.test.js +0 -34
- package/tests/jwtmanager.test.js +0 -66
- package/tests/magiclinkmanager.test.js +0 -67
- package/tests/oauth.test.js +0 -77
- package/tests/otpmanager.test.js +0 -51
- package/tests/passkeymanager.test.js +0 -114
- package/tests/totpmanager.test.js +0 -54
package/auth-verify.client.js
CHANGED
|
@@ -80,97 +80,6 @@ window.AuthVerify = class AuthVerify {
|
|
|
80
80
|
return new Uint8Array([...str].map(c => c.charCodeAt(0)));
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
// start(route){
|
|
84
|
-
// this.startRegisterApi = route;
|
|
85
|
-
// return this;
|
|
86
|
-
// }
|
|
87
|
-
|
|
88
|
-
// finish(route){
|
|
89
|
-
// this.finishRegisterApi = route;
|
|
90
|
-
// return this;
|
|
91
|
-
// }
|
|
92
|
-
|
|
93
|
-
// // -----------------------------
|
|
94
|
-
// // REGISTER PASSKEY (full flow)
|
|
95
|
-
// // -----------------------------
|
|
96
|
-
// async registerPasskey(user) {
|
|
97
|
-
// try {
|
|
98
|
-
// // 1️⃣ Get registration options from backend
|
|
99
|
-
// const publicKey = await this.post(`${this.startRegisterApi}`).data({user});
|
|
100
|
-
|
|
101
|
-
// // 2️⃣ Decode challenge & user.id automatically
|
|
102
|
-
// publicKey.challenge = this.base64urlToUint8Array(publicKey.challenge);
|
|
103
|
-
// publicKey.user.id = this.base64urlToUint8Array(publicKey.user.id);
|
|
104
|
-
|
|
105
|
-
// // 3️⃣ Ask browser to create credential
|
|
106
|
-
// const credential = await navigator.credentials.create({ publicKey });
|
|
107
|
-
|
|
108
|
-
// // 4️⃣ Convert ArrayBuffers to base64
|
|
109
|
-
// const data = {
|
|
110
|
-
// id: credential.id,
|
|
111
|
-
// rawId: btoa(String.fromCharCode(...new Uint8Array(credential.rawId))),
|
|
112
|
-
// type: credential.type,
|
|
113
|
-
// response: {
|
|
114
|
-
// clientDataJSON: btoa(String.fromCharCode(...new Uint8Array(credential.response.clientDataJSON))),
|
|
115
|
-
// attestationObject: btoa(String.fromCharCode(...new Uint8Array(credential.response.attestationObject))),
|
|
116
|
-
// },
|
|
117
|
-
// };
|
|
118
|
-
|
|
119
|
-
// // 5️⃣ Send credential to backend to finish registration
|
|
120
|
-
// const result = await this.post(`${this.finishRegisterApi}`).data(data);
|
|
121
|
-
|
|
122
|
-
// return result;
|
|
123
|
-
|
|
124
|
-
// } catch (err) {
|
|
125
|
-
// console.error("[AuthVerify registerPasskey]", err);
|
|
126
|
-
// return { error: true, message: err.message };
|
|
127
|
-
// }
|
|
128
|
-
// }
|
|
129
|
-
|
|
130
|
-
// // -----------------------------
|
|
131
|
-
// // LOGIN / AUTHENTICATE PASSKEY
|
|
132
|
-
// // -----------------------------
|
|
133
|
-
// async loginPasskey(user) {
|
|
134
|
-
// try {
|
|
135
|
-
// // 1️⃣ Get assertion options (challenge) from backend
|
|
136
|
-
// const publicKey = await this.post(`${this.startRegisterApi}`).data({ user, login: true });
|
|
137
|
-
|
|
138
|
-
// // 2️⃣ Decode Base64URL fields
|
|
139
|
-
// publicKey.challenge = this.base64urlToUint8Array(publicKey.challenge);
|
|
140
|
-
// publicKey.allowCredentials = publicKey.allowCredentials.map(cred => ({
|
|
141
|
-
// ...cred,
|
|
142
|
-
// id: this.base64urlToUint8Array(cred.id)
|
|
143
|
-
// }));
|
|
144
|
-
|
|
145
|
-
// // 3️⃣ Ask browser to get credential
|
|
146
|
-
// const credential = await navigator.credentials.get({ publicKey });
|
|
147
|
-
|
|
148
|
-
// // 4️⃣ Convert ArrayBuffers to Base64
|
|
149
|
-
// const data = {
|
|
150
|
-
// id: credential.id,
|
|
151
|
-
// rawId: btoa(String.fromCharCode(...new Uint8Array(credential.rawId))),
|
|
152
|
-
// type: credential.type,
|
|
153
|
-
// response: {
|
|
154
|
-
// clientDataJSON: btoa(String.fromCharCode(...new Uint8Array(credential.response.clientDataJSON))),
|
|
155
|
-
// authenticatorData: btoa(String.fromCharCode(...new Uint8Array(credential.response.authenticatorData))),
|
|
156
|
-
// signature: btoa(String.fromCharCode(...new Uint8Array(credential.response.signature))),
|
|
157
|
-
// userHandle: credential.response.userHandle
|
|
158
|
-
// ? btoa(String.fromCharCode(...new Uint8Array(credential.response.userHandle)))
|
|
159
|
-
// : null,
|
|
160
|
-
// },
|
|
161
|
-
// };
|
|
162
|
-
|
|
163
|
-
// // 5️⃣ Send assertion to backend for verification
|
|
164
|
-
// const result = await this.post(`${this.finishRegisterApi}`).data(data);
|
|
165
|
-
|
|
166
|
-
// return result;
|
|
167
|
-
|
|
168
|
-
// } catch (err) {
|
|
169
|
-
// console.error("[AuthVerify loginPasskey]", err);
|
|
170
|
-
// return { error: true, message: err.message };
|
|
171
|
-
// }
|
|
172
|
-
// }
|
|
173
|
-
|
|
174
83
|
async issue(publicKey){
|
|
175
84
|
publicKey.challenge = this.base64urlToUint8Array(publicKey.challenge);
|
|
176
85
|
publicKey.user.id = this.base64urlToUint8Array(publicKey.user.id);
|
package/package.json
CHANGED
|
@@ -3,20 +3,24 @@
|
|
|
3
3
|
"axios": "^1.12.2",
|
|
4
4
|
"base64url": "^3.0.1",
|
|
5
5
|
"cbor": "^10.0.11",
|
|
6
|
+
"cors": "^2.8.5",
|
|
6
7
|
"crypto": "^1.0.1",
|
|
8
|
+
"express": "^5.1.0",
|
|
7
9
|
"ioredis": "^5.8.1",
|
|
8
10
|
"jsonwebtoken": "^9.0.2",
|
|
9
11
|
"node-telegram-bot-api": "^0.66.0",
|
|
10
12
|
"nodemailer": "^7.0.6",
|
|
13
|
+
"path": "^0.12.7",
|
|
11
14
|
"qrcode": "^1.5.4",
|
|
12
15
|
"uuid": "^9.0.1"
|
|
13
16
|
},
|
|
14
17
|
"name": "auth-verify",
|
|
15
|
-
"version": "1.
|
|
18
|
+
"version": "1.11.0",
|
|
16
19
|
"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",
|
|
17
20
|
"main": "index.js",
|
|
18
21
|
"scripts": {
|
|
19
|
-
"test": "jest --runInBand"
|
|
22
|
+
"test": "jest --runInBand",
|
|
23
|
+
"start:api": "node rest-api/index.js"
|
|
20
24
|
},
|
|
21
25
|
"repository": {
|
|
22
26
|
"type": "git",
|
|
@@ -51,7 +55,9 @@
|
|
|
51
55
|
"passwordless",
|
|
52
56
|
"magiclink",
|
|
53
57
|
"hash",
|
|
54
|
-
"password_hashing"
|
|
58
|
+
"password_hashing",
|
|
59
|
+
"api",
|
|
60
|
+
"rest-api"
|
|
55
61
|
],
|
|
56
62
|
"author": "Jahongir Sobirov",
|
|
57
63
|
"license": "MIT",
|
package/readme.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# auth-verify
|
|
2
2
|
|
|
3
|
-
**AuthVerify** is a modular authentication library for Node.js, providing JWT, OTP, TOTP, Passkeys (WebAuthn), Magic Links, Sessions, and OAuth helpers. You can easily register custom senders for OTPs or notifications.
|
|
3
|
+
**AuthVerify** is a modular authentication library for Node.js, providing JWT, OTP, TOTP, Passkeys (WebAuthn), Magic Links, Sessions, and OAuth helpers. You can easily register custom senders for OTPs or notifications. Auth-verify now supports **REST API** for generating, sending OTP with email/SMS and verifying it.
|
|
4
4
|
- [Installation](https://github.com/Jahongir2007/auth-verify/blob/main/docs/docs.md#-installation)
|
|
5
5
|
- [Initialization](https://github.com/Jahongir2007/auth-verify/blob/main/docs/docs.md#-example-initialize-library-commonjs)
|
|
6
6
|
- [JWT](https://github.com/Jahongir2007/auth-verify/blob/main/docs/docs.md#-jwt-usage)
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
- [Custom Senders](https://github.com/Jahongir2007/auth-verify/blob/main/docs/docs.md#developer-extensibility-custom-senders)
|
|
14
14
|
- [Session Management](https://github.com/Jahongir2007/auth-verify/blob/main/docs/docs.md#-sessionmanager-api-documentation---auth-verify)
|
|
15
15
|
- [Crypto hashing](https://github.com/Jahongir2007/auth-verify/blob/main/docs/docs.md#-cryptomanager-api-guide)
|
|
16
|
+
- [Auth-verify REST API](https://auth-verify-sy2y.onrender.com/)
|
|
16
17
|
---
|
|
17
18
|
|
|
18
19
|
## 🧩 Installation
|
|
@@ -34,9 +35,10 @@ npm install auth-verify
|
|
|
34
35
|
- `OTPManager`: generate, store, send, verify, resend OTPs. Supports `storeTokens: "memory" | "redis" | "none"`. Supports email, SMS helper, Telegram bot, and custom dev senders.
|
|
35
36
|
- `TOTPManager`: generate, verify uri, codes and QR codes.
|
|
36
37
|
- `SessionManager`: simple session creation/verification/destroy with memory or Redis backend.
|
|
37
|
-
- `OAuthManager`: Handle OAuth 2.0 logins for
|
|
38
|
+
- `OAuthManager`: Handle OAuth 2.0 logins for 20+ providers.
|
|
38
39
|
- `PasskeyManager`: Handle passwordless login and registration using WebAuthn/passkey.
|
|
39
40
|
- `MagicLinkManager`: Handle passwordless login with magic link generation and verification.
|
|
41
|
+
- `CrpytoManager`: Handle hashing passwords or another data with algorithms like: `"pbkdf2"` and `"scrypt"`
|
|
40
42
|
---
|
|
41
43
|
|
|
42
44
|
## 🚀 Example: Initialize library (CommonJS)
|
|
@@ -316,6 +318,7 @@ const otp = auth.otp; // internally uses OTPManager
|
|
|
316
318
|
|
|
317
319
|
### ⚙️ Sender Configuration
|
|
318
320
|
#### Email sender
|
|
321
|
+
##### Using Gmail
|
|
319
322
|
```js
|
|
320
323
|
otp.sender({
|
|
321
324
|
via: "email", // or "sms", "telegram", "whatsapp"
|
|
@@ -329,6 +332,17 @@ otp.sender({
|
|
|
329
332
|
|
|
330
333
|
// or you can use otp.setSender({...});
|
|
331
334
|
```
|
|
335
|
+
##### Using SMTP service
|
|
336
|
+
```js
|
|
337
|
+
otp.sender({
|
|
338
|
+
via: "email", // or "sms", "telegram", "whatsapp"
|
|
339
|
+
sender: "your_email@gmail.com",
|
|
340
|
+
pass: "your_app_password",
|
|
341
|
+
host: "smtp.gmail.com",
|
|
342
|
+
port: 587,
|
|
343
|
+
secure: false
|
|
344
|
+
})
|
|
345
|
+
```
|
|
332
346
|
#### SMS sender
|
|
333
347
|
##### Using infobip:
|
|
334
348
|
```js
|
|
@@ -412,6 +426,9 @@ await otp.send("user@example.com", {
|
|
|
412
426
|
text: "Your OTP is 123456",
|
|
413
427
|
html: "<b>123456</b>"
|
|
414
428
|
});
|
|
429
|
+
|
|
430
|
+
// or simply
|
|
431
|
+
await otp.send("user@example.com");
|
|
415
432
|
```
|
|
416
433
|
Supports channels:
|
|
417
434
|
| `via` | Notes |
|
|
@@ -432,10 +449,11 @@ otp.send("user@example.com", options, (err, info) => {
|
|
|
432
449
|
### 🔑 Verify OTP
|
|
433
450
|
```js
|
|
434
451
|
// Promise style
|
|
435
|
-
const result = await otp.verify(
|
|
452
|
+
const result = await otp.verify("user@example.com", "123456");
|
|
453
|
+
//const result = await otp.verify({ check: "user@example.com", code: "123456" }); or you can use this style
|
|
436
454
|
|
|
437
455
|
// Callback style
|
|
438
|
-
otp.verify(
|
|
456
|
+
otp.verify("user@example.com", "123456", (err, success) => {
|
|
439
457
|
if(err) console.error(err.message);
|
|
440
458
|
else console.log("✅ OTP verified");
|
|
441
459
|
});
|
|
@@ -1737,16 +1755,9 @@ auth-verify/
|
|
|
1737
1755
|
│ ├─ /session/index.js
|
|
1738
1756
|
| ├─ /oauth/index.js
|
|
1739
1757
|
│ └─ helpers/helper.js
|
|
1740
|
-
├─
|
|
1741
|
-
│ ├─
|
|
1742
|
-
│ ├─
|
|
1743
|
-
│ ├─ jwtmanager.multitab.test.js
|
|
1744
|
-
│ ├─ jwtmanager.test.js
|
|
1745
|
-
│ ├─ otpmanager.test.js
|
|
1746
|
-
│ ├─ oauth.test.js
|
|
1747
|
-
│ ├─ totpmanager.test.js
|
|
1748
|
-
│ ├─ passkeymanager.test.js
|
|
1749
|
-
│ ├─ magiclinkmanager.test.js
|
|
1758
|
+
├─ rest-api/
|
|
1759
|
+
│ ├─ public/index.html
|
|
1760
|
+
│ ├─ index.js
|
|
1750
1761
|
├─ babel.config.js
|
|
1751
1762
|
├─ auth-verify.client.js
|
|
1752
1763
|
```
|
|
@@ -0,0 +1,171 @@
|
|
|
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
|
+
// ----------- SMTP Provider -------------
|
|
76
|
+
router.post('/auth/otp/send/email/:to', async (req, res) => {
|
|
77
|
+
try {
|
|
78
|
+
const { to } = req.params;
|
|
79
|
+
const { email, pass, host, port, secure, subject, text, html } = req.body.sender || {};
|
|
80
|
+
|
|
81
|
+
if (!email || !pass || !host || !port) {
|
|
82
|
+
return res.status(400).json({ error: "Missing sender email or pass or host or port" });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
auth.otp.sender({
|
|
86
|
+
via: 'email',
|
|
87
|
+
host,
|
|
88
|
+
port,
|
|
89
|
+
secure: secure || false,
|
|
90
|
+
sender: email,
|
|
91
|
+
pass
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const info = await auth.otp.send(to, {
|
|
95
|
+
subject: subject || "Your OTP Code",
|
|
96
|
+
text: text || "",
|
|
97
|
+
html: html || ""
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return res.json({
|
|
101
|
+
ok: true,
|
|
102
|
+
provider: "smtp",
|
|
103
|
+
to
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.error(err);
|
|
108
|
+
return res.status(500).json({ error: "Failed to send OTP" });
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// ------ OTP SMS -------
|
|
113
|
+
router.post('/auth/otp/send/sms/:to', async (req, res)=>{
|
|
114
|
+
try {
|
|
115
|
+
const { to } = req.params;
|
|
116
|
+
const { provider, apiKey, apiSecret, sender, text } = req.body.sender || {};
|
|
117
|
+
|
|
118
|
+
if(!provider || !apiKey || !apiSecret || !sender) return res.status(400).json({error: "Missing sender provider or apiKey or apiSecret or sender"});
|
|
119
|
+
|
|
120
|
+
auth.otp.sender({
|
|
121
|
+
via: 'sms',
|
|
122
|
+
provider,
|
|
123
|
+
apiKey,
|
|
124
|
+
apiSecret,
|
|
125
|
+
sender
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const info = await auth.otp.send(to, {text});
|
|
129
|
+
|
|
130
|
+
return res.json({
|
|
131
|
+
ok: true,
|
|
132
|
+
provider,
|
|
133
|
+
to
|
|
134
|
+
});
|
|
135
|
+
} catch(err){
|
|
136
|
+
console.error(err);
|
|
137
|
+
return res.status(500).json({ error: "Failed to send OTP with SMS" });
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Verify OTP sent by email
|
|
142
|
+
router.post('/auth/otp/verify/:to', async (req, res) => {
|
|
143
|
+
try {
|
|
144
|
+
const { to } = req.params;
|
|
145
|
+
const { otp } = req.body;
|
|
146
|
+
|
|
147
|
+
if (!otp) {
|
|
148
|
+
return res.status(400).json({ success: false, message: "Missing OTP code" });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const valid = await auth.otp.verify(to, otp);
|
|
152
|
+
|
|
153
|
+
if (valid) {
|
|
154
|
+
return res.status(200).json({ success: true, message: "OTP verified", to });
|
|
155
|
+
} else {
|
|
156
|
+
return res.status(400).json({ success: false, message: "OTP is incorrect", to });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
} catch (err) {
|
|
160
|
+
console.error(err);
|
|
161
|
+
return res.status(500).json({ success: false, message: "Failed to verify OTP", error: err.message });
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
// mount router
|
|
167
|
+
app.use("/api", router); // IMPORTANT!!
|
|
168
|
+
|
|
169
|
+
app.listen(3000, () => {
|
|
170
|
+
console.log("REST API running on port 3000");
|
|
171
|
+
});
|