say-auth 1.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/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Sanyii
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # 🔐 say-auth
2
+
3
+ > Enterprise-grade authentication library for Next.js with MFA, JWT, and security features
4
+
5
+ [![npm version](https://badge.fury.io/js/say-auth.svg)](https://www.npmjs.com/package/say-auth)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org/)
8
+ [![Next.js](https://img.shields.io/badge/Next.js-13%2B-black.svg)](https://nextjs.org/)
9
+ [![Downloads](https://img.shields.io/npm/dm/say-auth.svg)](https://www.npmjs.com/package/say-auth)
10
+
11
+ ## ✨ Features
12
+
13
+ - 🔐 **JWT Authentication** - Secure token-based authentication
14
+ - 🔄 **Auto Token Refresh** - Automatic token refresh with request queuing
15
+ - 🛡️ **MFA Support** - Two-factor authentication with TOTP
16
+ - 🚫 **Token Blacklisting** - Revoke tokens on logout
17
+ - 📝 **Audit Logging** - Track all authentication events
18
+ - 🖥️ **Device Fingerprinting** - Prevent session hijacking
19
+ - ⚡ **Rate Limiting** - Brute force protection
20
+ - 🔒 **AES-256 Encryption** - Secure token storage
21
+ - 📦 **Zero Config** - Works out of the box
22
+ - 🎯 **TypeScript** - Full type safety
23
+
24
+ ## 📦 Installation
25
+
26
+ ```bash
27
+ npm install say-auth
28
+ # or
29
+ pnpm add say-auth
30
+ # or
31
+ yarn add say-auth
32
+ 🚀 Quick Start
33
+ 1. Wrap your app with AuthProvider
34
+ tsx
35
+ // app/layout.tsx
36
+ import { AuthProvider } from 'say-auth';
37
+
38
+ export default function RootLayout({ children }) {
39
+ return (
40
+ <html>
41
+ <body>
42
+ <AuthProvider apiUrl={process.env.NEXT_PUBLIC_API_URL}>
43
+ {children}
44
+ </AuthProvider>
45
+ </body>
46
+ </html>
47
+ );
48
+ }
49
+ 2. Create login page
50
+ tsx
51
+ // app/login/page.tsx
52
+ 'use client';
53
+ import { useAuth } from 'say-auth';
54
+
55
+ export default function LoginPage() {
56
+ const { login, isLoading } = useAuth();
57
+
58
+ const handleSubmit = async (e) => {
59
+ e.preventDefault();
60
+ const formData = new FormData(e.currentTarget);
61
+ await login({
62
+ email: formData.get('email'),
63
+ password: formData.get('password'),
64
+ });
65
+ };
66
+
67
+ return (
68
+ <form onSubmit={handleSubmit}>
69
+ <input name="email" type="email" required />
70
+ <input name="password" type="password" required />
71
+ <button type="submit" disabled={isLoading}>
72
+ {isLoading ? 'Loading...' : 'Login'}
73
+ </button>
74
+ </form>
75
+ );
76
+ }
77
+ 3. Protect routes
78
+ tsx
79
+ // app/dashboard/page.tsx
80
+ 'use client';
81
+ import { ProtectedRoute, useAuth } from 'say-auth';
82
+
83
+ export default function DashboardPage() {
84
+ const { user } = useAuth();
85
+
86
+ return (
87
+ <ProtectedRoute>
88
+ <h1>Welcome, {user?.name}!</h1>
89
+ </ProtectedRoute>
90
+ );
91
+ }
92
+ 📖 API Reference
93
+ useAuth()
94
+ Main authentication hook that returns:
95
+
96
+ Property Type Description
97
+ user User | null Current user data
98
+ isAuthenticated boolean Authentication status
99
+ isLoading boolean Loading state
100
+ login() (credentials) => Promise Login function
101
+ logout() () => Promise Logout function
102
+ refreshSession() () => Promise Refresh session
103
+ ProtectedRoute
104
+ Component for protecting routes:
105
+
106
+ tsx
107
+ <ProtectedRoute requiredRole="admin">
108
+ <AdminPanel />
109
+ </ProtectedRoute>
110
+ 🔧 Requirements
111
+ Next.js 13+
112
+
113
+ React 18+
114
+
115
+ Axios 1.6+
116
+
117
+ 🤝 Contributing
118
+ Contributions are welcome! Please read our contributing guidelines.
119
+
120
+ 📄 License
121
+ MIT © Sanyii
@@ -0,0 +1,312 @@
1
+ import { AxiosInstance } from 'axios';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import React$1 from 'react';
4
+
5
+ interface User {
6
+ id: string;
7
+ email: string;
8
+ name: string;
9
+ role?: string;
10
+ avatar?: string;
11
+ mfaEnabled: boolean;
12
+ mfaSecret?: string;
13
+ backupCodes?: string[];
14
+ createdAt?: Date;
15
+ updatedAt?: Date;
16
+ }
17
+ interface AuthTokens {
18
+ accessToken: string;
19
+ refreshToken: string;
20
+ expiresIn: number;
21
+ }
22
+ interface LoginCredentials {
23
+ email: string;
24
+ password: string;
25
+ }
26
+ interface RegisterData {
27
+ email: string;
28
+ password: string;
29
+ name: string;
30
+ }
31
+ interface AuthState {
32
+ user: User | null;
33
+ tokens: AuthTokens | null;
34
+ isAuthenticated: boolean;
35
+ isLoading: boolean;
36
+ error: string | null;
37
+ }
38
+ interface MFAVerify {
39
+ userId: string;
40
+ code: string;
41
+ trustDevice?: boolean;
42
+ }
43
+ interface TokenBlacklistEntry {
44
+ token: string;
45
+ userId: string;
46
+ expiresAt: Date;
47
+ reason: 'logout' | 'revoke' | 'security';
48
+ }
49
+ interface AuditEntry$1 {
50
+ action: string;
51
+ userId?: string;
52
+ email?: string;
53
+ ipAddress: string;
54
+ userAgent: string;
55
+ timestamp: Date;
56
+ details?: Record<string, any>;
57
+ success: boolean;
58
+ errorMessage?: string;
59
+ }
60
+
61
+ declare class AuthService {
62
+ private static instance;
63
+ private axiosInstance;
64
+ private state;
65
+ private listeners;
66
+ private baseURL;
67
+ private constructor();
68
+ static getInstance(apiUrl?: string): AuthService;
69
+ private makeRequest;
70
+ healthCheck(): Promise<boolean>;
71
+ healthCheckWithRetry(retries?: number): Promise<boolean>;
72
+ private loadInitialState;
73
+ private notifyListeners;
74
+ subscribe(listener: (state: AuthState) => void): () => void;
75
+ private updateState;
76
+ login(credentials: LoginCredentials): Promise<{
77
+ requiresMFA: boolean;
78
+ user?: User;
79
+ }>;
80
+ verifyMFA(code: string, trustDevice?: boolean): Promise<User>;
81
+ register(data: RegisterData): Promise<User>;
82
+ logout(reason?: 'logout' | 'revoke' | 'security'): Promise<void>;
83
+ refreshSession(): Promise<void>;
84
+ logoutAllDevices(): Promise<void>;
85
+ changePassword(currentPassword: string, newPassword: string): Promise<void>;
86
+ hasRole(role: string | string[]): boolean;
87
+ getAxiosInstance(): AxiosInstance;
88
+ getUser(): User | null;
89
+ getState(): AuthState;
90
+ }
91
+
92
+ declare class SecureTokenStorage {
93
+ private static instance;
94
+ private encryptionKey;
95
+ private memoryCache;
96
+ private readonly TOKEN_VERSION;
97
+ private readonly SUPPORTED_VERSIONS;
98
+ private constructor();
99
+ static getInstance(): SecureTokenStorage;
100
+ private getOrCreateEncryptionKey;
101
+ private generateEncryptionKey;
102
+ private encrypt;
103
+ private decrypt;
104
+ setTokens(tokens: AuthTokens): void;
105
+ getTokens(): AuthTokens | null;
106
+ private migrateTokenFormat;
107
+ shouldRefreshToken(): boolean;
108
+ getTokenAge(): number | null;
109
+ setUser(user: User): void;
110
+ getUser(): User | null;
111
+ clear(): void;
112
+ hardClear(): void;
113
+ getStorageInfo(): {
114
+ hasTokens: boolean;
115
+ hasUser: boolean;
116
+ tokenVersion: string | null;
117
+ };
118
+ }
119
+
120
+ declare class TokenManager {
121
+ private static instance;
122
+ private refreshPromise;
123
+ static getInstance(): TokenManager;
124
+ getAccessToken(): string | null;
125
+ isTokenExpired(): boolean;
126
+ refreshToken(refreshFn: () => Promise<AuthTokens>): Promise<string>;
127
+ private performRefresh;
128
+ }
129
+
130
+ declare class TokenBlacklist {
131
+ private static instance;
132
+ private blacklist;
133
+ private cleanupInterval;
134
+ private constructor();
135
+ static getInstance(): TokenBlacklist;
136
+ addToBlacklist(token: string, userId: string, reason: TokenBlacklistEntry['reason']): Promise<void>;
137
+ isBlacklisted(token: string): Promise<boolean>;
138
+ revokeAllUserTokens(userId: string): Promise<void>;
139
+ private hashToken;
140
+ private syncToBackend;
141
+ private revokeAllOnBackend;
142
+ private startCleanup;
143
+ stopCleanup(): void;
144
+ }
145
+
146
+ declare class MFAService {
147
+ private static instance;
148
+ static getInstance(): MFAService;
149
+ setupMFA(userId: string, email: string): Promise<{
150
+ secret: string;
151
+ qrCode: string;
152
+ backupCodes: string[];
153
+ }>;
154
+ verifyAndEnableMFA(userId: string, code: string): Promise<boolean>;
155
+ verifyMFA(userId: string, code: string): Promise<boolean>;
156
+ private generateBackupCodes;
157
+ private saveMFAConfig;
158
+ private getUserMFASecret;
159
+ }
160
+
161
+ declare class RateLimiter {
162
+ private static instance;
163
+ private attempts;
164
+ private readonly MAX_ATTEMPTS;
165
+ private readonly WINDOW_MS;
166
+ private readonly BLOCK_DURATION_MS;
167
+ static getInstance(): RateLimiter;
168
+ checkRateLimit(identifier: string): {
169
+ allowed: boolean;
170
+ waitTime?: number;
171
+ };
172
+ reset(identifier: string): void;
173
+ }
174
+
175
+ declare enum AuditAction {
176
+ LOGIN_SUCCESS = "LOGIN_SUCCESS",
177
+ LOGIN_FAILURE = "LOGIN_FAILURE",
178
+ LOGOUT = "LOGOUT",
179
+ REGISTER_SUCCESS = "REGISTER_SUCCESS",
180
+ PASSWORD_CHANGE = "PASSWORD_CHANGE",
181
+ TOKEN_REFRESH = "TOKEN_REFRESH",
182
+ SESSION_TERMINATED = "SESSION_TERMINATED",
183
+ MFA_ENABLED = "MFA_ENABLED",
184
+ MFA_DISABLED = "MFA_DISABLED",
185
+ MFA_VERIFIED = "MFA_VERIFIED"
186
+ }
187
+ interface AuditEntry {
188
+ action: AuditAction;
189
+ userId?: string;
190
+ email?: string;
191
+ ipAddress: string;
192
+ userAgent: string;
193
+ timestamp: Date;
194
+ details?: Record<string, any>;
195
+ success: boolean;
196
+ errorMessage?: string;
197
+ }
198
+ declare class AuditLogger {
199
+ private static instance;
200
+ private logQueue;
201
+ private isFlushing;
202
+ static getInstance(): AuditLogger;
203
+ log(entry: Omit<AuditEntry, 'timestamp' | 'ipAddress' | 'userAgent'>): Promise<void>;
204
+ private scheduleFlush;
205
+ private flush;
206
+ private getClientIP;
207
+ }
208
+
209
+ declare class SessionManager {
210
+ private static instance;
211
+ private activeSessions;
212
+ static getInstance(): SessionManager;
213
+ registerSession(userId: string): Promise<string>;
214
+ validateSession(userId: string, deviceId: string): Promise<boolean>;
215
+ terminateSession(userId: string): Promise<void>;
216
+ terminateAllOtherSessions(userId: string, currentDeviceId: string): Promise<void>;
217
+ getActiveSessionsCount(userId: string): number;
218
+ }
219
+
220
+ declare class DeviceFingerprint {
221
+ static generate(): Promise<string>;
222
+ private static hashObject;
223
+ }
224
+
225
+ declare function useAuth(apiUrl?: string): {
226
+ login: (credentials: LoginCredentials) => Promise<{
227
+ requiresMFA: boolean;
228
+ user?: User;
229
+ }>;
230
+ verifyMFA: (code: string, trustDevice?: boolean) => Promise<User>;
231
+ register: (data: RegisterData) => Promise<User>;
232
+ logout: () => Promise<void>;
233
+ refreshSession: () => Promise<void>;
234
+ hasRole: (role: string | string[]) => boolean;
235
+ user: User | null;
236
+ tokens: AuthTokens | null;
237
+ isAuthenticated: boolean;
238
+ isLoading: boolean;
239
+ error: string | null;
240
+ };
241
+
242
+ declare function useProtectedRoute(redirectTo?: string, requiredRole?: string | string[]): {
243
+ isAuthenticated: boolean;
244
+ isLoading: boolean;
245
+ };
246
+
247
+ interface AuthProviderProps {
248
+ children: React$1.ReactNode;
249
+ apiUrl?: string;
250
+ }
251
+ declare function AuthProvider({ children, apiUrl }: AuthProviderProps): react_jsx_runtime.JSX.Element;
252
+
253
+ interface ProtectedRouteProps {
254
+ children: React.ReactNode;
255
+ requiredRole?: string | string[];
256
+ fallback?: React.ReactNode;
257
+ }
258
+ declare function ProtectedRoute({ children, requiredRole, fallback }: ProtectedRouteProps): react_jsx_runtime.JSX.Element | null;
259
+
260
+ interface MFASetupProps {
261
+ userId: string;
262
+ email: string;
263
+ onComplete: () => void;
264
+ onCancel: () => void;
265
+ }
266
+ declare function MFASetup({ userId, email, onComplete, onCancel }: MFASetupProps): react_jsx_runtime.JSX.Element;
267
+
268
+ interface MFAVerificationProps {
269
+ onSubmit: (code: string, trustDevice: boolean) => Promise<void>;
270
+ onBack?: () => void;
271
+ }
272
+ declare function MFAVerification({ onSubmit, onBack }: MFAVerificationProps): react_jsx_runtime.JSX.Element;
273
+
274
+ declare function SessionWarning({ warningMinutes }: {
275
+ warningMinutes?: number;
276
+ }): react_jsx_runtime.JSX.Element | null;
277
+
278
+ declare function setupAuthInterceptors(axiosInstance: AxiosInstance, baseURL: string): void;
279
+
280
+ declare function cn(...classes: (string | undefined | false)[]): string;
281
+ declare function getErrorMessage(error: unknown): string;
282
+ declare function isValidEmail(email: string): boolean;
283
+
284
+ declare const AUTH_CONFIG: {
285
+ tokenRefreshThreshold: number;
286
+ maxLoginAttempts: number;
287
+ lockoutDuration: number;
288
+ sessionTimeout: number;
289
+ mfaCodeLength: number;
290
+ backupCodesCount: number;
291
+ trustDeviceDuration: number;
292
+ };
293
+ declare const STORAGE_KEYS: {
294
+ tokens: string;
295
+ user: string;
296
+ encryptionKey: string;
297
+ deviceId: string;
298
+ trustedDevices: string;
299
+ };
300
+ declare const API_ENDPOINTS: {
301
+ login: string;
302
+ logout: string;
303
+ register: string;
304
+ refresh: string;
305
+ mfaSetup: string;
306
+ mfaVerify: string;
307
+ mfaDisable: string;
308
+ changePassword: string;
309
+ audit: string;
310
+ };
311
+
312
+ export { API_ENDPOINTS, AUTH_CONFIG, AuditAction, type AuditEntry$1 as AuditEntry, AuditLogger, AuthProvider, AuthService, type AuthState, type AuthTokens, DeviceFingerprint, type LoginCredentials, MFAService, MFASetup, MFAVerification, type MFAVerify, ProtectedRoute, RateLimiter, type RegisterData, STORAGE_KEYS, SessionManager, SessionWarning, TokenBlacklist, type TokenBlacklistEntry, TokenManager, SecureTokenStorage as TokenStorage, type User, cn, getErrorMessage, isValidEmail, setupAuthInterceptors, useAuth, useProtectedRoute };