auth-events 1.0.0 → 1.2.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/README.md +3 -308
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
# auth-events
|
|
2
|
-
|
|
3
1
|

|
|
4
|
-
|
|
5
2
|
# Auth Events
|
|
6
3
|
|
|
7
4
|
**`auth-events`** is a lightweight, powerful, and extensible Node.js library to handle authentication and user-related events in your application. It provides an **event-driven architecture** for login, logout, registration, password changes, role updates, device tracking, risk signals, and more.
|
|
@@ -27,312 +24,10 @@ Designed for **security, audit, and automation**, it works with any auth provide
|
|
|
27
24
|
|
|
28
25
|
---
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
npm install auth-events
|
|
34
|
-
# or
|
|
35
|
-
yarn add auth-events
|
|
36
|
-
|
|
37
|
-
auth-events
|
|
38
|
-
|
|
39
|
-
A lightweight event layer for authentication flows.
|
|
40
|
-
|
|
41
|
-
🧠 Mental Model (Read this first)
|
|
42
|
-
|
|
43
|
-
Think of authentication in two responsibilities:
|
|
44
|
-
|
|
45
|
-
Auth Code → WHAT happened?
|
|
46
|
-
Auth Events → WHAT should we do about it?
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
Examples:
|
|
50
|
-
|
|
51
|
-
Login happened → emit event
|
|
52
|
-
|
|
53
|
-
Password changed → emit event
|
|
54
|
-
|
|
55
|
-
New device detected → emit event
|
|
56
|
-
|
|
57
|
-
auth-events only broadcasts facts.
|
|
58
|
-
It does not decide business logic.
|
|
59
|
-
|
|
60
|
-
This keeps authentication clean and predictable.
|
|
61
|
-
|
|
62
|
-
❓ Why auth-events?
|
|
63
|
-
|
|
64
|
-
Authentication logic often becomes bloated over time.
|
|
65
|
-
|
|
66
|
-
A simple login flow slowly accumulates responsibilities:
|
|
67
|
-
|
|
68
|
-
audit logging
|
|
69
|
-
|
|
70
|
-
security checks
|
|
71
|
-
|
|
72
|
-
analytics
|
|
73
|
-
|
|
74
|
-
notifications
|
|
75
|
-
|
|
76
|
-
Soon, your auth code becomes:
|
|
77
|
-
|
|
78
|
-
hard to read
|
|
79
|
-
|
|
80
|
-
hard to test
|
|
81
|
-
|
|
82
|
-
risky to change
|
|
83
|
-
|
|
84
|
-
auth-events solves this by introducing a small event layer after authentication.
|
|
85
|
-
|
|
86
|
-
Auth code emits events.
|
|
87
|
-
Side-effects live elsewhere.
|
|
88
|
-
|
|
89
|
-
🧩 How it works
|
|
90
|
-
|
|
91
|
-
Your app emits an auth event (e.g. "login").
|
|
92
|
-
|
|
93
|
-
One or more listeners react to that event.
|
|
94
|
-
|
|
95
|
-
Authentication logic stays clean and focused.
|
|
96
|
-
|
|
97
|
-
There is no coupling between auth code and side-effects.
|
|
98
|
-
|
|
99
|
-
📁 Recommended File Structure
|
|
100
|
-
|
|
101
|
-
This is where most projects go wrong — structure matters.
|
|
102
|
-
|
|
103
|
-
src/
|
|
104
|
-
├─ auth/
|
|
105
|
-
│ ├─ auth.controller.ts # login, signup, logout
|
|
106
|
-
│ ├─ auth.service.ts # password verification, token logic
|
|
107
|
-
│
|
|
108
|
-
├─ auth-events/
|
|
109
|
-
│ ├─ index.ts # single AuthEvents instance
|
|
110
|
-
│ ├─ listeners/
|
|
111
|
-
│ │ ├─ audit.listener.ts
|
|
112
|
-
│ │ ├─ security.listener.ts
|
|
113
|
-
│ │ ├─ analytics.listener.ts
|
|
114
|
-
│ │ └─ notification.listener.ts
|
|
115
|
-
│
|
|
116
|
-
├─ app.ts
|
|
117
|
-
└─ server.ts
|
|
118
|
-
|
|
119
|
-
🧩 Step 1: Create one global AuthEvents instance
|
|
120
|
-
|
|
121
|
-
📍 src/auth-events/index.ts
|
|
122
|
-
|
|
123
|
-
import { AuthEvents } from "auth-events";
|
|
124
|
-
|
|
125
|
-
export const authEvents = new AuthEvents();
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
⚠️ Important
|
|
129
|
-
|
|
130
|
-
Your application should have only one AuthEvents instance.
|
|
131
|
-
All listeners and emitters must use this same instance.
|
|
132
|
-
|
|
133
|
-
🧩 Step 2: Emit events inside existing auth code
|
|
134
|
-
|
|
135
|
-
📍 src/auth/auth.controller.ts
|
|
136
|
-
|
|
137
|
-
import { authEvents } from "../auth-events";
|
|
138
|
-
|
|
139
|
-
export const login = async (req, res) => {
|
|
140
|
-
const user = await authService.login(req.body);
|
|
141
|
-
|
|
142
|
-
// Emit fact, not logic
|
|
143
|
-
await authEvents.emit("login", {
|
|
144
|
-
userId: user.id,
|
|
145
|
-
ip: req.ip,
|
|
146
|
-
userAgent: req.headers["user-agent"],
|
|
147
|
-
sessionId: req.sessionID
|
|
148
|
-
)});
|
|
149
|
-
|
|
150
|
-
res.json({ token: user.token });
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
✅ Login responsibility = done
|
|
155
|
-
❌ No logging
|
|
156
|
-
❌ No analytics
|
|
157
|
-
❌ No security rules
|
|
158
|
-
|
|
159
|
-
🧩 Step 3: Attach operations using listeners
|
|
160
|
-
|
|
161
|
-
This is where auth-events shines.
|
|
162
|
-
|
|
163
|
-
🔐 Security listener
|
|
164
|
-
|
|
165
|
-
📍 auth-events/listeners/security.listener.ts
|
|
166
|
-
|
|
167
|
-
import { authEvents } from "../index";
|
|
168
|
-
|
|
169
|
-
authEvents.on("login", async (event) => {
|
|
170
|
-
if (isNewDevice(event)) {
|
|
171
|
-
await authEvents.emit("new_device_detected", event);
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
📜 Audit logging
|
|
176
|
-
|
|
177
|
-
📍 auth-events/listeners/audit.listener.ts
|
|
178
|
-
|
|
179
|
-
authEvents.on("login", async (event) => {
|
|
180
|
-
await AuditLog.create({
|
|
181
|
-
userId: event.userId,
|
|
182
|
-
action: "LOGIN",
|
|
183
|
-
ip: event.ip,
|
|
184
|
-
userAgent: event.userAgent
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
📊 Analytics tracking
|
|
189
|
-
|
|
190
|
-
📍 auth-events/listeners/analytics.listener.ts
|
|
191
|
-
|
|
192
|
-
authEvents.on("login", async (event) => {
|
|
193
|
-
analytics.track("user_login", {
|
|
194
|
-
userId: event.userId,
|
|
195
|
-
device: event.userAgent
|
|
196
|
-
)});
|
|
197
|
-
|
|
198
|
-
🔔 Notifications
|
|
199
|
-
|
|
200
|
-
📍 auth-events/listeners/notification.listener.ts
|
|
201
|
-
|
|
202
|
-
authEvents.on("new_device_detected", async (event) => {
|
|
203
|
-
await sendEmail({
|
|
204
|
-
to: event.userId,
|
|
205
|
-
subject: "New device login detected"
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
// Emit a login event
|
|
212
|
-
await authEvents.login({
|
|
213
|
-
userId: "123",
|
|
214
|
-
email: "user@example.com",
|
|
215
|
-
ip: "192.168.1.1",
|
|
216
|
-
deviceId: "device_abc123",
|
|
217
|
-
deviceType: "desktop",
|
|
218
|
-
browser: "Chrome",
|
|
219
|
-
country: "IN",
|
|
220
|
-
riskScore: 25,
|
|
221
|
-
status: "success"
|
|
222
|
-
});
|
|
223
|
-
AuthEvent Interface
|
|
224
|
-
Each event provides a rich set of fields for security, auditing, and automation:
|
|
225
|
-
|
|
226
|
-
export interface AuthEvent {
|
|
227
|
-
type: AuthEventType;
|
|
228
|
-
userId: string;
|
|
229
|
-
email?: string;
|
|
230
|
-
roles?: string[];
|
|
231
|
-
|
|
232
|
-
// Session & Token
|
|
233
|
-
sessionId?: string;
|
|
234
|
-
tokenId?: string;
|
|
235
|
-
tokenType?: "jwt" | "session";
|
|
236
|
-
tokenIssuedAt?: Date;
|
|
237
|
-
tokenExpiresAt?: Date;
|
|
238
|
-
|
|
239
|
-
// Network
|
|
240
|
-
ip?: string;
|
|
241
|
-
isp?: string;
|
|
242
|
-
country?: string;
|
|
243
|
-
region?: string;
|
|
244
|
-
city?: string;
|
|
245
|
-
timezone?: string;
|
|
246
|
-
|
|
247
|
-
// Device
|
|
248
|
-
deviceId?: string;
|
|
249
|
-
deviceType?: "mobile" | "desktop" | "tablet";
|
|
250
|
-
os?: string;
|
|
251
|
-
browser?: string;
|
|
252
|
-
userAgent?: string;
|
|
253
|
-
|
|
254
|
-
// Auth context
|
|
255
|
-
provider?: "google" | "firebase" | "auth0" | "custom";
|
|
256
|
-
origin?: "web" | "mobile" | "api";
|
|
257
|
-
referrer?: string;
|
|
258
|
-
status?: "success" | "failed";
|
|
259
|
-
failureReason?: string;
|
|
260
|
-
attemptCount?: number;
|
|
261
|
-
authMethod?: "password" | "oauth" | "magic_link" | "otp";
|
|
262
|
-
|
|
263
|
-
// Risk signals
|
|
264
|
-
riskScore?: number;
|
|
265
|
-
deviceRiskScore?: number;
|
|
266
|
-
geoVelocityRisk?: boolean;
|
|
267
|
-
isNewDevice?: boolean;
|
|
268
|
-
isNewIP?: boolean;
|
|
269
|
-
isProxy?: boolean;
|
|
270
|
-
isTor?: boolean;
|
|
271
|
-
isBot?: boolean;
|
|
272
|
-
|
|
273
|
-
// Update / Change tracking
|
|
274
|
-
changedFields?: string[];
|
|
275
|
-
previousValues?: Record<string, any>;
|
|
276
|
-
newValues?: Record<string, any>;
|
|
277
|
-
updatedBy?: string;
|
|
278
|
-
updatedVia?: "user" | "admin" | "api" | "system";
|
|
279
|
-
changeId?: string;
|
|
280
|
-
previousVersion?: number;
|
|
281
|
-
newVersion?: number;
|
|
282
|
-
|
|
283
|
-
// Meta & audit
|
|
284
|
-
metadata?: Record<string, any>;
|
|
285
|
-
requestId?: string;
|
|
286
|
-
correlationId?: string;
|
|
287
|
-
|
|
288
|
-
timestamp: Date;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
AuthEventType
|
|
293
|
-
All supported events (enterprise-ready):
|
|
294
|
-
|
|
295
|
-
export type AuthEventType =
|
|
296
|
-
| "login" | "logout" | "register" | "login_failed"
|
|
297
|
-
| "suspicious_login" | "brute_force_detected"
|
|
298
|
-
| "account_locked" | "account_unlocked"
|
|
299
|
-
| "ip_blocked" | "device_blocked" | "location_changed"
|
|
300
|
-
| "otp_sent" | "otp_verified" | "otp_failed"
|
|
301
|
-
| "mfa_enabled" | "mfa_disabled" | "email_verified" | "phone_verified"
|
|
302
|
-
| "session_created" | "session_revoked" | "session_expired" | "session_refreshed"
|
|
303
|
-
| "token_issued" | "token_refreshed" | "token_revoked" | "token_expired"
|
|
304
|
-
| "new_device_detected" | "device_trusted" | "device_untrusted" | "device_removed"
|
|
305
|
-
| "password_changed" | "role_changed"
|
|
306
|
-
| "profile_updated" | "email_changed" | "phone_changed"
|
|
307
|
-
| "rate_limit_exceeded" | "policy_violation" | "security_rule_triggered" | "risk_score_updated"
|
|
308
|
-
| "provider_linked" | "provider_unlinked" | "provider_login" | "provider_error";
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
Developer Tips
|
|
314
|
-
|
|
315
|
-
Use changedFields, previousValues, and newValues for auditing profile updates.
|
|
316
|
-
|
|
317
|
-
Use riskScore, isNewDevice, and geoVelocityRisk for security automation.
|
|
318
|
-
|
|
319
|
-
Handlers can return actions: allow, block, or challenge (OTP, 2FA).
|
|
320
|
-
|
|
321
|
-
Inject logger, db, cache in context for advanced workflows.
|
|
322
|
-
|
|
323
|
-
Can be extended with plugins or middleware for notifications (email/SMS), dashboards, or analytics.
|
|
324
|
-
|
|
27
|
+
documentation
|
|
325
28
|
|
|
29
|
+
https://auth-events-web.vercel.app/
|
|
326
30
|
|
|
327
|
-
Why use auth-events?
|
|
328
|
-
Single source of truth for all auth-related actions
|
|
329
|
-
Real-time risk analysis and automation
|
|
330
|
-
Easy to integrate with any auth provider (custom, Firebase, Auth0, etc.)
|
|
331
|
-
Enterprise-ready with audit trails, session management, MFA, and device trust
|
|
332
31
|
|
|
333
32
|
|
|
334
|
-
|
|
335
|
-
Webhooks / external notifications
|
|
336
|
-
Priority-based handler execution
|
|
337
|
-
Event persistence for audit / replay
|
|
338
|
-
Rule engine for automated security actions
|
|
33
|
+
Keep Coding Keep Exploring
|