navojit-auth 1.6.2 → 4.0.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/dist/index.d.mts +30 -45
- package/dist/index.d.ts +30 -45
- package/dist/index.js +166 -103
- package/dist/index.mjs +176 -103
- package/package.json +10 -6
package/dist/index.d.mts
CHANGED
|
@@ -1,51 +1,44 @@
|
|
|
1
1
|
import { FastifyInstance } from 'fastify';
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* 1. JWT TYPE AUGMENTATION
|
|
5
|
-
* Isse sign() aur verify() karte waqt 'email' ya 'sid' ka error kabhi nahi aayega.
|
|
6
|
-
* TypeScript ko ab pata hai ki Navojit Auth ke payload mein kya-kya hota hai.
|
|
7
|
-
*/
|
|
8
|
-
declare module "@fastify/jwt" {
|
|
9
|
-
interface FastifyJWT {
|
|
10
|
-
payload: {
|
|
11
|
-
sub: string;
|
|
12
|
-
email: string;
|
|
13
|
-
role: string;
|
|
14
|
-
orgId?: string;
|
|
15
|
-
sid?: string;
|
|
16
|
-
impersonatedBy?: string;
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* 2. THE CONTRACT (Interfaces)
|
|
22
|
-
* Developers iska use karke Drizzle, Prisma ya SQL ka naya adapter bana sakte hain.
|
|
23
|
-
*/
|
|
24
3
|
interface AuthAdapter {
|
|
25
4
|
createUser(data: any): Promise<any>;
|
|
26
5
|
findUserByEmail(email: string): Promise<any>;
|
|
27
6
|
findUserById(id: string): Promise<any>;
|
|
28
7
|
}
|
|
29
|
-
interface User {
|
|
30
|
-
id: string;
|
|
31
|
-
email: string;
|
|
32
|
-
role: string;
|
|
33
|
-
orgId?: string;
|
|
34
|
-
passkeys?: any[];
|
|
35
|
-
[key: string]: any;
|
|
36
|
-
}
|
|
37
8
|
interface AuthConfig {
|
|
38
9
|
adapter: AuthAdapter;
|
|
39
10
|
secret: string;
|
|
40
|
-
redisClient?: any;
|
|
41
|
-
rpName?: string;
|
|
42
|
-
rpID?: string;
|
|
43
11
|
prefix?: string;
|
|
12
|
+
accessExpiry?: string;
|
|
13
|
+
refreshExpiry?: string;
|
|
14
|
+
}
|
|
15
|
+
interface OmniTokens {
|
|
16
|
+
access_token: string;
|
|
17
|
+
refresh_token: string;
|
|
18
|
+
sid: string;
|
|
19
|
+
}
|
|
20
|
+
declare class NavojitAuth {
|
|
21
|
+
private config;
|
|
22
|
+
constructor(config: AuthConfig);
|
|
23
|
+
generateOmniTokens(user: any, options?: {
|
|
24
|
+
mfa_v?: boolean;
|
|
25
|
+
am?: string[];
|
|
26
|
+
}): OmniTokens;
|
|
27
|
+
verifyToken(token: string): any;
|
|
28
|
+
generatePasskeyOptions(userId: string, userEmail: string, rpID?: string): Promise<any>;
|
|
29
|
+
verifyPasskey(expectedChallenge: string, responseBody: any, origin: string, rpID?: string): Promise<any>;
|
|
30
|
+
hashPassword(password: string): Promise<string>;
|
|
31
|
+
verifyPassword(hash: string, password: string): Promise<boolean>;
|
|
32
|
+
verifyAndFetchUser(email: string, otp: string): Promise<any>;
|
|
33
|
+
/**
|
|
34
|
+
* 🟢 FASTIFY CONNECTOR
|
|
35
|
+
*/
|
|
36
|
+
attach(server: FastifyInstance): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* 🔵 EXPRESS CONNECTOR
|
|
39
|
+
*/
|
|
40
|
+
express(): any;
|
|
44
41
|
}
|
|
45
|
-
/**
|
|
46
|
-
* 3. THE BRIDGE (Mongoose Adapter)
|
|
47
|
-
* Built-in support for Mongoose users.
|
|
48
|
-
*/
|
|
49
42
|
declare class MongooseAdapter implements AuthAdapter {
|
|
50
43
|
private model;
|
|
51
44
|
constructor(model: any);
|
|
@@ -53,13 +46,5 @@ declare class MongooseAdapter implements AuthAdapter {
|
|
|
53
46
|
findUserById(id: string): Promise<any>;
|
|
54
47
|
createUser(data: any): Promise<any>;
|
|
55
48
|
}
|
|
56
|
-
/**
|
|
57
|
-
* 4. THE ENGINE (NavojitAuth)
|
|
58
|
-
*/
|
|
59
|
-
declare class NavojitAuth {
|
|
60
|
-
private config;
|
|
61
|
-
constructor(config: AuthConfig);
|
|
62
|
-
attach(server: FastifyInstance): Promise<void>;
|
|
63
|
-
}
|
|
64
49
|
|
|
65
|
-
export { type AuthAdapter, type AuthConfig, MongooseAdapter, NavojitAuth, type
|
|
50
|
+
export { type AuthAdapter, type AuthConfig, MongooseAdapter, NavojitAuth, type OmniTokens };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,51 +1,44 @@
|
|
|
1
1
|
import { FastifyInstance } from 'fastify';
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* 1. JWT TYPE AUGMENTATION
|
|
5
|
-
* Isse sign() aur verify() karte waqt 'email' ya 'sid' ka error kabhi nahi aayega.
|
|
6
|
-
* TypeScript ko ab pata hai ki Navojit Auth ke payload mein kya-kya hota hai.
|
|
7
|
-
*/
|
|
8
|
-
declare module "@fastify/jwt" {
|
|
9
|
-
interface FastifyJWT {
|
|
10
|
-
payload: {
|
|
11
|
-
sub: string;
|
|
12
|
-
email: string;
|
|
13
|
-
role: string;
|
|
14
|
-
orgId?: string;
|
|
15
|
-
sid?: string;
|
|
16
|
-
impersonatedBy?: string;
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* 2. THE CONTRACT (Interfaces)
|
|
22
|
-
* Developers iska use karke Drizzle, Prisma ya SQL ka naya adapter bana sakte hain.
|
|
23
|
-
*/
|
|
24
3
|
interface AuthAdapter {
|
|
25
4
|
createUser(data: any): Promise<any>;
|
|
26
5
|
findUserByEmail(email: string): Promise<any>;
|
|
27
6
|
findUserById(id: string): Promise<any>;
|
|
28
7
|
}
|
|
29
|
-
interface User {
|
|
30
|
-
id: string;
|
|
31
|
-
email: string;
|
|
32
|
-
role: string;
|
|
33
|
-
orgId?: string;
|
|
34
|
-
passkeys?: any[];
|
|
35
|
-
[key: string]: any;
|
|
36
|
-
}
|
|
37
8
|
interface AuthConfig {
|
|
38
9
|
adapter: AuthAdapter;
|
|
39
10
|
secret: string;
|
|
40
|
-
redisClient?: any;
|
|
41
|
-
rpName?: string;
|
|
42
|
-
rpID?: string;
|
|
43
11
|
prefix?: string;
|
|
12
|
+
accessExpiry?: string;
|
|
13
|
+
refreshExpiry?: string;
|
|
14
|
+
}
|
|
15
|
+
interface OmniTokens {
|
|
16
|
+
access_token: string;
|
|
17
|
+
refresh_token: string;
|
|
18
|
+
sid: string;
|
|
19
|
+
}
|
|
20
|
+
declare class NavojitAuth {
|
|
21
|
+
private config;
|
|
22
|
+
constructor(config: AuthConfig);
|
|
23
|
+
generateOmniTokens(user: any, options?: {
|
|
24
|
+
mfa_v?: boolean;
|
|
25
|
+
am?: string[];
|
|
26
|
+
}): OmniTokens;
|
|
27
|
+
verifyToken(token: string): any;
|
|
28
|
+
generatePasskeyOptions(userId: string, userEmail: string, rpID?: string): Promise<any>;
|
|
29
|
+
verifyPasskey(expectedChallenge: string, responseBody: any, origin: string, rpID?: string): Promise<any>;
|
|
30
|
+
hashPassword(password: string): Promise<string>;
|
|
31
|
+
verifyPassword(hash: string, password: string): Promise<boolean>;
|
|
32
|
+
verifyAndFetchUser(email: string, otp: string): Promise<any>;
|
|
33
|
+
/**
|
|
34
|
+
* 🟢 FASTIFY CONNECTOR
|
|
35
|
+
*/
|
|
36
|
+
attach(server: FastifyInstance): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* 🔵 EXPRESS CONNECTOR
|
|
39
|
+
*/
|
|
40
|
+
express(): any;
|
|
44
41
|
}
|
|
45
|
-
/**
|
|
46
|
-
* 3. THE BRIDGE (Mongoose Adapter)
|
|
47
|
-
* Built-in support for Mongoose users.
|
|
48
|
-
*/
|
|
49
42
|
declare class MongooseAdapter implements AuthAdapter {
|
|
50
43
|
private model;
|
|
51
44
|
constructor(model: any);
|
|
@@ -53,13 +46,5 @@ declare class MongooseAdapter implements AuthAdapter {
|
|
|
53
46
|
findUserById(id: string): Promise<any>;
|
|
54
47
|
createUser(data: any): Promise<any>;
|
|
55
48
|
}
|
|
56
|
-
/**
|
|
57
|
-
* 4. THE ENGINE (NavojitAuth)
|
|
58
|
-
*/
|
|
59
|
-
declare class NavojitAuth {
|
|
60
|
-
private config;
|
|
61
|
-
constructor(config: AuthConfig);
|
|
62
|
-
attach(server: FastifyInstance): Promise<void>;
|
|
63
|
-
}
|
|
64
49
|
|
|
65
|
-
export { type AuthAdapter, type AuthConfig, MongooseAdapter, NavojitAuth, type
|
|
50
|
+
export { type AuthAdapter, type AuthConfig, MongooseAdapter, NavojitAuth, type OmniTokens };
|
package/dist/index.js
CHANGED
|
@@ -34,127 +34,190 @@ __export(index_exports, {
|
|
|
34
34
|
NavojitAuth: () => NavojitAuth
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(index_exports);
|
|
37
|
-
var
|
|
38
|
-
var import_argon2 = __toESM(require("argon2"));
|
|
37
|
+
var import_jsonwebtoken = __toESM(require("jsonwebtoken"));
|
|
39
38
|
var import_uuid = require("uuid");
|
|
40
|
-
var
|
|
41
|
-
|
|
42
|
-
this.model = model;
|
|
43
|
-
}
|
|
44
|
-
model;
|
|
45
|
-
async findUserByEmail(email) {
|
|
46
|
-
return await this.model.findOne({ email });
|
|
47
|
-
}
|
|
48
|
-
async findUserById(id) {
|
|
49
|
-
return await this.model.findById(id);
|
|
50
|
-
}
|
|
51
|
-
async createUser(data) {
|
|
52
|
-
const user = new this.model(data);
|
|
53
|
-
await user.save();
|
|
54
|
-
return user;
|
|
55
|
-
}
|
|
56
|
-
};
|
|
39
|
+
var import_server = require("@simplewebauthn/server");
|
|
40
|
+
var argon2 = __toESM(require("argon2"));
|
|
57
41
|
var NavojitAuth = class {
|
|
58
42
|
config;
|
|
59
43
|
constructor(config) {
|
|
60
44
|
this.config = {
|
|
61
|
-
rpName: "Navojit Auth",
|
|
62
|
-
rpID: "localhost",
|
|
63
45
|
prefix: "/auth",
|
|
46
|
+
accessExpiry: "15m",
|
|
47
|
+
refreshExpiry: "30d",
|
|
64
48
|
...config
|
|
65
49
|
};
|
|
66
50
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
51
|
+
// --------------------------------------------------
|
|
52
|
+
// 🚀 CORE 1: UNIVERSAL TOKENS (Syncs with Py/Rust)
|
|
53
|
+
// --------------------------------------------------
|
|
54
|
+
generateOmniTokens(user, options = {}) {
|
|
55
|
+
const sid = (0, import_uuid.v4)();
|
|
56
|
+
const userId = user.id || user._id.toString();
|
|
57
|
+
const access_token = import_jsonwebtoken.default.sign(
|
|
58
|
+
{
|
|
59
|
+
sub: userId,
|
|
60
|
+
email: user.email,
|
|
61
|
+
role: user.role || "member",
|
|
62
|
+
sid,
|
|
63
|
+
mfa_v: options.mfa_v || false,
|
|
64
|
+
am: options.am || ["pwd"],
|
|
65
|
+
typ: "access"
|
|
66
|
+
},
|
|
67
|
+
this.config.secret,
|
|
68
|
+
{ expiresIn: this.config.accessExpiry }
|
|
69
|
+
);
|
|
70
|
+
const refresh_token = import_jsonwebtoken.default.sign(
|
|
71
|
+
{
|
|
72
|
+
sub: userId,
|
|
73
|
+
sid,
|
|
74
|
+
typ: "refresh"
|
|
75
|
+
},
|
|
76
|
+
this.config.secret,
|
|
77
|
+
{ expiresIn: this.config.refreshExpiry }
|
|
78
|
+
);
|
|
79
|
+
return { access_token, refresh_token, sid };
|
|
80
|
+
}
|
|
81
|
+
verifyToken(token) {
|
|
82
|
+
try {
|
|
83
|
+
return import_jsonwebtoken.default.verify(token, this.config.secret);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
if (error.name === "TokenExpiredError") {
|
|
86
|
+
return { error: "expired", message: "Token has expired" };
|
|
75
87
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return reply.code(401).send({ error: "Unauthorized" });
|
|
88
|
+
return { error: "invalid", message: error.message };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// --------------------------------------------------
|
|
92
|
+
// 👁️ CORE 2: BIOMETRICS & PASSKEYS (World-Class Security)
|
|
93
|
+
// --------------------------------------------------
|
|
94
|
+
async generatePasskeyOptions(userId, userEmail, rpID = "navojit.com") {
|
|
95
|
+
return (0, import_server.generateRegistrationOptions)({
|
|
96
|
+
rpName: "Navojit Ecosystem",
|
|
97
|
+
rpID,
|
|
98
|
+
userID: Buffer.from(userId),
|
|
99
|
+
userName: userEmail,
|
|
100
|
+
attestationType: "none",
|
|
101
|
+
authenticatorSelection: {
|
|
102
|
+
residentKey: "required",
|
|
103
|
+
userVerification: "preferred"
|
|
93
104
|
}
|
|
94
105
|
});
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
106
|
+
}
|
|
107
|
+
async verifyPasskey(expectedChallenge, responseBody, origin, rpID = "navojit.com") {
|
|
108
|
+
try {
|
|
109
|
+
const verification = await (0, import_server.verifyRegistrationResponse)({
|
|
110
|
+
response: responseBody,
|
|
111
|
+
expectedChallenge,
|
|
112
|
+
expectedOrigin: origin,
|
|
113
|
+
expectedRPID: rpID
|
|
114
|
+
});
|
|
115
|
+
return verification;
|
|
116
|
+
} catch (error) {
|
|
117
|
+
return { verified: false, error: error.message };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// --------------------------------------------------
|
|
121
|
+
// 🔐 CORE 3: ARGON2 & OTP LOGIC
|
|
122
|
+
// --------------------------------------------------
|
|
123
|
+
async hashPassword(password) {
|
|
124
|
+
return await argon2.hash(password);
|
|
125
|
+
}
|
|
126
|
+
async verifyPassword(hash2, password) {
|
|
127
|
+
return await argon2.verify(hash2, password);
|
|
128
|
+
}
|
|
129
|
+
async verifyAndFetchUser(email, otp) {
|
|
130
|
+
if (!otp || otp.length < 4) throw new Error("Invalid OTP");
|
|
131
|
+
let user = await this.config.adapter.findUserByEmail(email);
|
|
132
|
+
if (!user) {
|
|
133
|
+
user = await this.config.adapter.createUser({
|
|
134
|
+
email,
|
|
135
|
+
password: await this.hashPassword("NAV_SECURE_" + (0, import_uuid.v4)()),
|
|
136
|
+
role: "member",
|
|
137
|
+
isVerified: true
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return user;
|
|
141
|
+
}
|
|
142
|
+
// ==========================================
|
|
143
|
+
// 3. THE CONNECTORS (Purane Features Wapas!)
|
|
144
|
+
// ==========================================
|
|
145
|
+
/**
|
|
146
|
+
* 🟢 FASTIFY CONNECTOR
|
|
147
|
+
*/
|
|
148
|
+
async attach(server) {
|
|
149
|
+
const { prefix, adapter } = this.config;
|
|
150
|
+
server.post(`${prefix}/otp/verify`, async (req, reply) => {
|
|
151
|
+
try {
|
|
152
|
+
const user = await this.verifyAndFetchUser(
|
|
153
|
+
req.body.email,
|
|
154
|
+
req.body.otp
|
|
155
|
+
);
|
|
156
|
+
const tokens = this.generateOmniTokens(user, {
|
|
157
|
+
mfa_v: true,
|
|
158
|
+
am: ["otp"]
|
|
114
159
|
});
|
|
160
|
+
return { success: true, ...tokens, gateway: "navojit-v4-fastify" };
|
|
161
|
+
} catch (e) {
|
|
162
|
+
return reply.code(400).send({ error: e.message });
|
|
115
163
|
}
|
|
116
|
-
const sessionId = (0, import_uuid.v4)();
|
|
117
|
-
const token = server.jwt.sign({
|
|
118
|
-
sub: user.id,
|
|
119
|
-
email: user.email,
|
|
120
|
-
role: user.role,
|
|
121
|
-
orgId: user.orgId,
|
|
122
|
-
sid: sessionId
|
|
123
|
-
});
|
|
124
|
-
await trackSession(user.id, sessionId);
|
|
125
|
-
return { success: true, token, message: "Welcome to Navojit Ecosystem!" };
|
|
126
164
|
});
|
|
127
|
-
server.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
165
|
+
server.addHook("preHandler", async (req, reply) => {
|
|
166
|
+
if (req.routerPath.startsWith(`${prefix}/profile`)) {
|
|
167
|
+
const token = req.headers.authorization?.split(" ")[1];
|
|
168
|
+
if (!token) return reply.code(401).send({ error: "Missing Token" });
|
|
169
|
+
const decoded = this.verifyToken(token);
|
|
170
|
+
if (decoded.error)
|
|
171
|
+
return reply.code(401).send({ error: decoded.error });
|
|
172
|
+
req.user = decoded;
|
|
132
173
|
}
|
|
133
|
-
const sessionId = (0, import_uuid.v4)();
|
|
134
|
-
const token = server.jwt.sign({
|
|
135
|
-
sub: user.id,
|
|
136
|
-
email: user.email,
|
|
137
|
-
role: user.role,
|
|
138
|
-
orgId: user.orgId,
|
|
139
|
-
sid: sessionId
|
|
140
|
-
});
|
|
141
|
-
await trackSession(user.id, sessionId);
|
|
142
|
-
return { success: true, token };
|
|
143
174
|
});
|
|
144
|
-
server.
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
175
|
+
server.get(`${prefix}/profile`, async (req, reply) => {
|
|
176
|
+
const user = await adapter.findUserById(req.user.sub);
|
|
177
|
+
return {
|
|
178
|
+
success: true,
|
|
179
|
+
user: { id: user.id, email: user.email, role: user.role }
|
|
180
|
+
};
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* 🔵 EXPRESS CONNECTOR
|
|
185
|
+
*/
|
|
186
|
+
express() {
|
|
187
|
+
const express = require("express");
|
|
188
|
+
const router = express.Router();
|
|
189
|
+
const { prefix, adapter } = this.config;
|
|
190
|
+
router.post(`${prefix}/otp/verify`, async (req, res) => {
|
|
191
|
+
try {
|
|
192
|
+
const user = await this.verifyAndFetchUser(
|
|
193
|
+
req.body.email,
|
|
194
|
+
req.body.otp
|
|
195
|
+
);
|
|
196
|
+
const tokens = this.generateOmniTokens(user, {
|
|
197
|
+
mfa_v: true,
|
|
198
|
+
am: ["otp"]
|
|
199
|
+
});
|
|
200
|
+
res.json({ success: true, ...tokens, gateway: "navojit-v4-express" });
|
|
201
|
+
} catch (e) {
|
|
202
|
+
res.status(400).json({ error: e.message });
|
|
203
|
+
}
|
|
157
204
|
});
|
|
205
|
+
return router;
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
var MongooseAdapter = class {
|
|
209
|
+
constructor(model) {
|
|
210
|
+
this.model = model;
|
|
211
|
+
}
|
|
212
|
+
model;
|
|
213
|
+
async findUserByEmail(email) {
|
|
214
|
+
return await this.model.findOne({ email });
|
|
215
|
+
}
|
|
216
|
+
async findUserById(id) {
|
|
217
|
+
return await this.model.findById(id);
|
|
218
|
+
}
|
|
219
|
+
async createUser(data) {
|
|
220
|
+
return await new this.model(data).save();
|
|
158
221
|
}
|
|
159
222
|
};
|
|
160
223
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.mjs
CHANGED
|
@@ -1,125 +1,198 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
1
8
|
// src/index.ts
|
|
2
|
-
import jwt from "
|
|
3
|
-
import argon2 from "argon2";
|
|
9
|
+
import jwt from "jsonwebtoken";
|
|
4
10
|
import { v4 as uuidv4 } from "uuid";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
async findUserByEmail(email) {
|
|
11
|
-
return await this.model.findOne({ email });
|
|
12
|
-
}
|
|
13
|
-
async findUserById(id) {
|
|
14
|
-
return await this.model.findById(id);
|
|
15
|
-
}
|
|
16
|
-
async createUser(data) {
|
|
17
|
-
const user = new this.model(data);
|
|
18
|
-
await user.save();
|
|
19
|
-
return user;
|
|
20
|
-
}
|
|
21
|
-
};
|
|
11
|
+
import {
|
|
12
|
+
generateRegistrationOptions,
|
|
13
|
+
verifyRegistrationResponse
|
|
14
|
+
} from "@simplewebauthn/server";
|
|
15
|
+
import * as argon2 from "argon2";
|
|
22
16
|
var NavojitAuth = class {
|
|
23
17
|
config;
|
|
24
18
|
constructor(config) {
|
|
25
19
|
this.config = {
|
|
26
|
-
rpName: "Navojit Auth",
|
|
27
|
-
rpID: "localhost",
|
|
28
20
|
prefix: "/auth",
|
|
21
|
+
accessExpiry: "15m",
|
|
22
|
+
refreshExpiry: "30d",
|
|
29
23
|
...config
|
|
30
24
|
};
|
|
31
25
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
26
|
+
// --------------------------------------------------
|
|
27
|
+
// 🚀 CORE 1: UNIVERSAL TOKENS (Syncs with Py/Rust)
|
|
28
|
+
// --------------------------------------------------
|
|
29
|
+
generateOmniTokens(user, options = {}) {
|
|
30
|
+
const sid = uuidv4();
|
|
31
|
+
const userId = user.id || user._id.toString();
|
|
32
|
+
const access_token = jwt.sign(
|
|
33
|
+
{
|
|
34
|
+
sub: userId,
|
|
35
|
+
email: user.email,
|
|
36
|
+
role: user.role || "member",
|
|
37
|
+
sid,
|
|
38
|
+
mfa_v: options.mfa_v || false,
|
|
39
|
+
am: options.am || ["pwd"],
|
|
40
|
+
typ: "access"
|
|
41
|
+
},
|
|
42
|
+
this.config.secret,
|
|
43
|
+
{ expiresIn: this.config.accessExpiry }
|
|
44
|
+
);
|
|
45
|
+
const refresh_token = jwt.sign(
|
|
46
|
+
{
|
|
47
|
+
sub: userId,
|
|
48
|
+
sid,
|
|
49
|
+
typ: "refresh"
|
|
50
|
+
},
|
|
51
|
+
this.config.secret,
|
|
52
|
+
{ expiresIn: this.config.refreshExpiry }
|
|
53
|
+
);
|
|
54
|
+
return { access_token, refresh_token, sid };
|
|
55
|
+
}
|
|
56
|
+
verifyToken(token) {
|
|
57
|
+
try {
|
|
58
|
+
return jwt.verify(token, this.config.secret);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (error.name === "TokenExpiredError") {
|
|
61
|
+
return { error: "expired", message: "Token has expired" };
|
|
40
62
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return reply.code(401).send({ error: "Unauthorized" });
|
|
63
|
+
return { error: "invalid", message: error.message };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// --------------------------------------------------
|
|
67
|
+
// 👁️ CORE 2: BIOMETRICS & PASSKEYS (World-Class Security)
|
|
68
|
+
// --------------------------------------------------
|
|
69
|
+
async generatePasskeyOptions(userId, userEmail, rpID = "navojit.com") {
|
|
70
|
+
return generateRegistrationOptions({
|
|
71
|
+
rpName: "Navojit Ecosystem",
|
|
72
|
+
rpID,
|
|
73
|
+
userID: Buffer.from(userId),
|
|
74
|
+
userName: userEmail,
|
|
75
|
+
attestationType: "none",
|
|
76
|
+
authenticatorSelection: {
|
|
77
|
+
residentKey: "required",
|
|
78
|
+
userVerification: "preferred"
|
|
58
79
|
}
|
|
59
80
|
});
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
}
|
|
82
|
+
async verifyPasskey(expectedChallenge, responseBody, origin, rpID = "navojit.com") {
|
|
83
|
+
try {
|
|
84
|
+
const verification = await verifyRegistrationResponse({
|
|
85
|
+
response: responseBody,
|
|
86
|
+
expectedChallenge,
|
|
87
|
+
expectedOrigin: origin,
|
|
88
|
+
expectedRPID: rpID
|
|
89
|
+
});
|
|
90
|
+
return verification;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
return { verified: false, error: error.message };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// --------------------------------------------------
|
|
96
|
+
// 🔐 CORE 3: ARGON2 & OTP LOGIC
|
|
97
|
+
// --------------------------------------------------
|
|
98
|
+
async hashPassword(password) {
|
|
99
|
+
return await argon2.hash(password);
|
|
100
|
+
}
|
|
101
|
+
async verifyPassword(hash2, password) {
|
|
102
|
+
return await argon2.verify(hash2, password);
|
|
103
|
+
}
|
|
104
|
+
async verifyAndFetchUser(email, otp) {
|
|
105
|
+
if (!otp || otp.length < 4) throw new Error("Invalid OTP");
|
|
106
|
+
let user = await this.config.adapter.findUserByEmail(email);
|
|
107
|
+
if (!user) {
|
|
108
|
+
user = await this.config.adapter.createUser({
|
|
109
|
+
email,
|
|
110
|
+
password: await this.hashPassword("NAV_SECURE_" + uuidv4()),
|
|
111
|
+
role: "member",
|
|
112
|
+
isVerified: true
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return user;
|
|
116
|
+
}
|
|
117
|
+
// ==========================================
|
|
118
|
+
// 3. THE CONNECTORS (Purane Features Wapas!)
|
|
119
|
+
// ==========================================
|
|
120
|
+
/**
|
|
121
|
+
* 🟢 FASTIFY CONNECTOR
|
|
122
|
+
*/
|
|
123
|
+
async attach(server) {
|
|
124
|
+
const { prefix, adapter } = this.config;
|
|
125
|
+
server.post(`${prefix}/otp/verify`, async (req, reply) => {
|
|
126
|
+
try {
|
|
127
|
+
const user = await this.verifyAndFetchUser(
|
|
128
|
+
req.body.email,
|
|
129
|
+
req.body.otp
|
|
130
|
+
);
|
|
131
|
+
const tokens = this.generateOmniTokens(user, {
|
|
132
|
+
mfa_v: true,
|
|
133
|
+
am: ["otp"]
|
|
79
134
|
});
|
|
135
|
+
return { success: true, ...tokens, gateway: "navojit-v4-fastify" };
|
|
136
|
+
} catch (e) {
|
|
137
|
+
return reply.code(400).send({ error: e.message });
|
|
80
138
|
}
|
|
81
|
-
const sessionId = uuidv4();
|
|
82
|
-
const token = server.jwt.sign({
|
|
83
|
-
sub: user.id,
|
|
84
|
-
email: user.email,
|
|
85
|
-
role: user.role,
|
|
86
|
-
orgId: user.orgId,
|
|
87
|
-
sid: sessionId
|
|
88
|
-
});
|
|
89
|
-
await trackSession(user.id, sessionId);
|
|
90
|
-
return { success: true, token, message: "Welcome to Navojit Ecosystem!" };
|
|
91
139
|
});
|
|
92
|
-
server.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
140
|
+
server.addHook("preHandler", async (req, reply) => {
|
|
141
|
+
if (req.routerPath.startsWith(`${prefix}/profile`)) {
|
|
142
|
+
const token = req.headers.authorization?.split(" ")[1];
|
|
143
|
+
if (!token) return reply.code(401).send({ error: "Missing Token" });
|
|
144
|
+
const decoded = this.verifyToken(token);
|
|
145
|
+
if (decoded.error)
|
|
146
|
+
return reply.code(401).send({ error: decoded.error });
|
|
147
|
+
req.user = decoded;
|
|
97
148
|
}
|
|
98
|
-
const sessionId = uuidv4();
|
|
99
|
-
const token = server.jwt.sign({
|
|
100
|
-
sub: user.id,
|
|
101
|
-
email: user.email,
|
|
102
|
-
role: user.role,
|
|
103
|
-
orgId: user.orgId,
|
|
104
|
-
sid: sessionId
|
|
105
|
-
});
|
|
106
|
-
await trackSession(user.id, sessionId);
|
|
107
|
-
return { success: true, token };
|
|
108
149
|
});
|
|
109
|
-
server.
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
150
|
+
server.get(`${prefix}/profile`, async (req, reply) => {
|
|
151
|
+
const user = await adapter.findUserById(req.user.sub);
|
|
152
|
+
return {
|
|
153
|
+
success: true,
|
|
154
|
+
user: { id: user.id, email: user.email, role: user.role }
|
|
155
|
+
};
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* 🔵 EXPRESS CONNECTOR
|
|
160
|
+
*/
|
|
161
|
+
express() {
|
|
162
|
+
const express = __require("express");
|
|
163
|
+
const router = express.Router();
|
|
164
|
+
const { prefix, adapter } = this.config;
|
|
165
|
+
router.post(`${prefix}/otp/verify`, async (req, res) => {
|
|
166
|
+
try {
|
|
167
|
+
const user = await this.verifyAndFetchUser(
|
|
168
|
+
req.body.email,
|
|
169
|
+
req.body.otp
|
|
170
|
+
);
|
|
171
|
+
const tokens = this.generateOmniTokens(user, {
|
|
172
|
+
mfa_v: true,
|
|
173
|
+
am: ["otp"]
|
|
174
|
+
});
|
|
175
|
+
res.json({ success: true, ...tokens, gateway: "navojit-v4-express" });
|
|
176
|
+
} catch (e) {
|
|
177
|
+
res.status(400).json({ error: e.message });
|
|
178
|
+
}
|
|
122
179
|
});
|
|
180
|
+
return router;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
var MongooseAdapter = class {
|
|
184
|
+
constructor(model) {
|
|
185
|
+
this.model = model;
|
|
186
|
+
}
|
|
187
|
+
model;
|
|
188
|
+
async findUserByEmail(email) {
|
|
189
|
+
return await this.model.findOne({ email });
|
|
190
|
+
}
|
|
191
|
+
async findUserById(id) {
|
|
192
|
+
return await this.model.findById(id);
|
|
193
|
+
}
|
|
194
|
+
async createUser(data) {
|
|
195
|
+
return await new this.model(data).save();
|
|
123
196
|
}
|
|
124
197
|
};
|
|
125
198
|
export {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "navojit-auth",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
|
+
"description": "The World's Smartest Universal Auth Engine. Supports Passkeys, Fastify, Mongoose, Drizzle, and Cross-Language Sync.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
],
|
|
18
18
|
"scripts": {
|
|
19
19
|
"dev": "tsx watch src/index.ts",
|
|
20
|
-
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
20
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean --external express",
|
|
21
21
|
"prepublishOnly": "npm run build",
|
|
22
22
|
"lint": "tsc",
|
|
23
23
|
"test": "vitest run",
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"mongoose",
|
|
31
31
|
"mongodb",
|
|
32
32
|
"multi-tenant",
|
|
33
|
-
"universal-auth"
|
|
33
|
+
"universal-auth",
|
|
34
|
+
"passkeys"
|
|
34
35
|
],
|
|
35
36
|
"author": "Kashish Singh",
|
|
36
37
|
"license": "MIT",
|
|
@@ -49,12 +50,15 @@
|
|
|
49
50
|
"argon2": "^0.44.0",
|
|
50
51
|
"dotenv": "^17.4.1",
|
|
51
52
|
"google-auth-library": "^10.6.2",
|
|
53
|
+
"jsonwebtoken": "^9.0.2",
|
|
52
54
|
"resend": "^6.10.0",
|
|
53
55
|
"uuid": "^13.0.0",
|
|
54
56
|
"zod": "^4.3.6"
|
|
55
57
|
},
|
|
56
58
|
"devDependencies": {
|
|
57
|
-
"@types/
|
|
59
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
60
|
+
"@types/node": "^22.19.17",
|
|
61
|
+
"@types/uuid": "^10.0.0",
|
|
58
62
|
"@vitest/coverage-v8": "^4.1.4",
|
|
59
63
|
"drizzle-orm": "^0.30.0",
|
|
60
64
|
"fastify": "^4.26.2",
|
|
@@ -64,4 +68,4 @@
|
|
|
64
68
|
"typescript": "^5.7.0",
|
|
65
69
|
"vitest": "^4.1.4"
|
|
66
70
|
}
|
|
67
|
-
}
|
|
71
|
+
}
|