@pixygon/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.
@@ -0,0 +1,310 @@
1
+ /**
2
+ * @pixygon/auth - Shared Types
3
+ * Type definitions for the Pixygon authentication system
4
+ */
5
+
6
+ // ============================================================================
7
+ // User Types
8
+ // ============================================================================
9
+
10
+ export type UserRole = 'user' | 'creator' | 'admin' | 'superadmin';
11
+
12
+ export type SubscriptionTier = 'free' | 'basic' | 'pro' | 'family' | 'enterprise';
13
+
14
+ export interface User {
15
+ _id: string;
16
+ userName: string;
17
+ email: string;
18
+ isVerified: boolean;
19
+ role: UserRole;
20
+
21
+ // Profile
22
+ firstName?: string;
23
+ lastName?: string;
24
+ profilePicture?: string;
25
+
26
+ // Subscription
27
+ subscriptionTier?: SubscriptionTier;
28
+ stripeCustomerId?: string;
29
+
30
+ // Token system
31
+ dailyTokens?: number;
32
+ purchasedTokens?: number;
33
+ bonusTokens?: number;
34
+ subscriptionTokens?: number;
35
+
36
+ // Parental controls (for family plans)
37
+ isChildAccount?: boolean;
38
+ parentUserId?: string;
39
+
40
+ // API access
41
+ apiAccess?: boolean;
42
+
43
+ // Social links
44
+ discordUserId?: string;
45
+ twitchUser?: string;
46
+
47
+ // Timestamps
48
+ createdAt?: string;
49
+ updatedAt?: string;
50
+ }
51
+
52
+ // ============================================================================
53
+ // Token Types
54
+ // ============================================================================
55
+
56
+ export interface TokenPair {
57
+ accessToken: string;
58
+ refreshToken: string;
59
+ expiresIn: number; // seconds until access token expires
60
+ refreshExpiresIn: number; // seconds until refresh token expires
61
+ }
62
+
63
+ export interface TokenPayload {
64
+ id: string;
65
+ role?: UserRole;
66
+ iat: number;
67
+ exp: number;
68
+ }
69
+
70
+ // ============================================================================
71
+ // Auth State Types
72
+ // ============================================================================
73
+
74
+ export type AuthStatus =
75
+ | 'idle' // Initial state
76
+ | 'loading' // Checking auth status
77
+ | 'authenticated' // User is logged in
78
+ | 'unauthenticated' // No valid session
79
+ | 'verifying'; // Email verification pending
80
+
81
+ export interface AuthState {
82
+ user: User | null;
83
+ accessToken: string | null;
84
+ refreshToken: string | null;
85
+ status: AuthStatus;
86
+ isLoading: boolean;
87
+ error: AuthError | null;
88
+ }
89
+
90
+ // ============================================================================
91
+ // Auth Request/Response Types
92
+ // ============================================================================
93
+
94
+ export interface LoginRequest {
95
+ userName: string; // Can be username or email
96
+ password: string;
97
+ }
98
+
99
+ export interface LoginResponse {
100
+ user: User;
101
+ token: string;
102
+ refreshToken?: string;
103
+ expiresIn?: number;
104
+ message?: string;
105
+ }
106
+
107
+ export interface RegisterRequest {
108
+ userName: string;
109
+ email: string;
110
+ password: string;
111
+ confirmPassword?: string;
112
+ }
113
+
114
+ export interface RegisterResponse {
115
+ user: User;
116
+ message?: string;
117
+ }
118
+
119
+ export interface VerifyRequest {
120
+ userName: string;
121
+ verificationCode: string;
122
+ }
123
+
124
+ export interface VerifyResponse {
125
+ user: User;
126
+ token: string;
127
+ refreshToken?: string;
128
+ message?: string;
129
+ }
130
+
131
+ export interface ForgotPasswordRequest {
132
+ email: string;
133
+ }
134
+
135
+ export interface ForgotPasswordResponse {
136
+ message: string;
137
+ }
138
+
139
+ export interface RecoverPasswordRequest {
140
+ email: string;
141
+ recoveryCode: string;
142
+ newPassword: string;
143
+ }
144
+
145
+ export interface RecoverPasswordResponse {
146
+ message: string;
147
+ }
148
+
149
+ export interface RefreshTokenRequest {
150
+ refreshToken: string;
151
+ }
152
+
153
+ export interface RefreshTokenResponse {
154
+ token: string;
155
+ refreshToken: string;
156
+ expiresIn: number;
157
+ }
158
+
159
+ export interface ResendVerificationRequest {
160
+ userName: string;
161
+ }
162
+
163
+ export interface ResendVerificationResponse {
164
+ message: string;
165
+ }
166
+
167
+ // ============================================================================
168
+ // Error Types
169
+ // ============================================================================
170
+
171
+ export type AuthErrorCode =
172
+ | 'INVALID_CREDENTIALS'
173
+ | 'USER_NOT_FOUND'
174
+ | 'USER_EXISTS'
175
+ | 'EMAIL_NOT_VERIFIED'
176
+ | 'INVALID_VERIFICATION_CODE'
177
+ | 'EXPIRED_VERIFICATION_CODE'
178
+ | 'INVALID_RECOVERY_CODE'
179
+ | 'EXPIRED_RECOVERY_CODE'
180
+ | 'TOKEN_EXPIRED'
181
+ | 'TOKEN_INVALID'
182
+ | 'REFRESH_TOKEN_EXPIRED'
183
+ | 'NETWORK_ERROR'
184
+ | 'SERVER_ERROR'
185
+ | 'UNKNOWN_ERROR';
186
+
187
+ export interface AuthError {
188
+ code: AuthErrorCode;
189
+ message: string;
190
+ details?: Record<string, unknown>;
191
+ }
192
+
193
+ // ============================================================================
194
+ // Configuration Types
195
+ // ============================================================================
196
+
197
+ export interface AuthConfig {
198
+ /** Base URL for the auth API (e.g., https://pixygon-server.onrender.com/v1) */
199
+ baseUrl: string;
200
+
201
+ /** App identifier for token storage keys */
202
+ appId: string;
203
+
204
+ /** App display name for branded components */
205
+ appName?: string;
206
+
207
+ /** Enable automatic token refresh (default: true) */
208
+ autoRefresh?: boolean;
209
+
210
+ /** Time before expiry to trigger refresh in seconds (default: 300 = 5 min) */
211
+ refreshThreshold?: number;
212
+
213
+ /** Callback when user logs in */
214
+ onLogin?: (user: User) => void;
215
+
216
+ /** Callback when user logs out */
217
+ onLogout?: () => void;
218
+
219
+ /** Callback when token is refreshed */
220
+ onTokenRefresh?: (tokens: TokenPair) => void;
221
+
222
+ /** Callback when auth error occurs */
223
+ onError?: (error: AuthError) => void;
224
+
225
+ /** Storage mechanism (default: localStorage) */
226
+ storage?: AuthStorage;
227
+
228
+ /** Enable debug logging (default: false) */
229
+ debug?: boolean;
230
+ }
231
+
232
+ export interface AuthStorage {
233
+ getItem: (key: string) => string | null | Promise<string | null>;
234
+ setItem: (key: string, value: string) => void | Promise<void>;
235
+ removeItem: (key: string) => void | Promise<void>;
236
+ }
237
+
238
+ // ============================================================================
239
+ // Context Types
240
+ // ============================================================================
241
+
242
+ export interface AuthContextValue extends AuthState {
243
+ // Auth actions
244
+ login: (credentials: LoginRequest) => Promise<LoginResponse>;
245
+ register: (data: RegisterRequest) => Promise<RegisterResponse>;
246
+ logout: () => Promise<void>;
247
+ verify: (data: VerifyRequest) => Promise<VerifyResponse>;
248
+ resendVerification: (data: ResendVerificationRequest) => Promise<ResendVerificationResponse>;
249
+ forgotPassword: (data: ForgotPasswordRequest) => Promise<ForgotPasswordResponse>;
250
+ recoverPassword: (data: RecoverPasswordRequest) => Promise<RecoverPasswordResponse>;
251
+ refreshTokens: () => Promise<void>;
252
+
253
+ // Utility
254
+ getAccessToken: () => string | null;
255
+ isAuthenticated: boolean;
256
+ isVerified: boolean;
257
+ hasRole: (role: UserRole | UserRole[]) => boolean;
258
+
259
+ // Config
260
+ config: AuthConfig;
261
+ }
262
+
263
+ // ============================================================================
264
+ // Component Props Types
265
+ // ============================================================================
266
+
267
+ export interface LoginFormProps {
268
+ onSuccess?: (user: User) => void;
269
+ onError?: (error: AuthError) => void;
270
+ onNavigateRegister?: () => void;
271
+ onNavigateForgotPassword?: () => void;
272
+ showBranding?: boolean;
273
+ className?: string;
274
+ }
275
+
276
+ export interface RegisterFormProps {
277
+ onSuccess?: (user: User) => void;
278
+ onError?: (error: AuthError) => void;
279
+ onNavigateLogin?: () => void;
280
+ showBranding?: boolean;
281
+ className?: string;
282
+ }
283
+
284
+ export interface VerifyFormProps {
285
+ userName: string;
286
+ onSuccess?: (user: User) => void;
287
+ onError?: (error: AuthError) => void;
288
+ onNavigateLogin?: () => void;
289
+ showBranding?: boolean;
290
+ className?: string;
291
+ }
292
+
293
+ export interface ForgotPasswordFormProps {
294
+ onSuccess?: () => void;
295
+ onError?: (error: AuthError) => void;
296
+ onNavigateLogin?: () => void;
297
+ showBranding?: boolean;
298
+ className?: string;
299
+ }
300
+
301
+ export interface PixygonAuthProps {
302
+ mode: 'login' | 'register' | 'verify' | 'forgot-password' | 'recover-password';
303
+ onSuccess?: (user?: User) => void;
304
+ onError?: (error: AuthError) => void;
305
+ onModeChange?: (mode: string) => void;
306
+ userName?: string; // For verify mode
307
+ showBranding?: boolean;
308
+ theme?: 'dark' | 'light';
309
+ className?: string;
310
+ }
@@ -0,0 +1,167 @@
1
+ /**
2
+ * @pixygon/auth - Token Storage Utilities
3
+ * Unified storage for auth tokens across all Pixygon apps
4
+ */
5
+
6
+ import type { AuthStorage, User, TokenPair } from '../types';
7
+
8
+ // Storage keys - prefixed with app ID for isolation
9
+ const getKeys = (appId: string) => ({
10
+ ACCESS_TOKEN: `${appId}_access_token`,
11
+ REFRESH_TOKEN: `${appId}_refresh_token`,
12
+ USER: `${appId}_user`,
13
+ EXPIRES_AT: `${appId}_expires_at`,
14
+ });
15
+
16
+ // Default storage using localStorage
17
+ const defaultStorage: AuthStorage = {
18
+ getItem: (key: string) => {
19
+ if (typeof window === 'undefined') return null;
20
+ return localStorage.getItem(key);
21
+ },
22
+ setItem: (key: string, value: string) => {
23
+ if (typeof window === 'undefined') return;
24
+ localStorage.setItem(key, value);
25
+ },
26
+ removeItem: (key: string) => {
27
+ if (typeof window === 'undefined') return;
28
+ localStorage.removeItem(key);
29
+ },
30
+ };
31
+
32
+ /**
33
+ * Create a token storage manager for a specific app
34
+ */
35
+ export function createTokenStorage(appId: string, storage: AuthStorage = defaultStorage) {
36
+ const keys = getKeys(appId);
37
+
38
+ return {
39
+ /**
40
+ * Get the stored access token
41
+ */
42
+ getAccessToken: async (): Promise<string | null> => {
43
+ const token = await storage.getItem(keys.ACCESS_TOKEN);
44
+ return token;
45
+ },
46
+
47
+ /**
48
+ * Get the stored refresh token
49
+ */
50
+ getRefreshToken: async (): Promise<string | null> => {
51
+ const token = await storage.getItem(keys.REFRESH_TOKEN);
52
+ return token;
53
+ },
54
+
55
+ /**
56
+ * Get the stored user
57
+ */
58
+ getUser: async (): Promise<User | null> => {
59
+ const userJson = await storage.getItem(keys.USER);
60
+ if (!userJson) return null;
61
+ try {
62
+ return JSON.parse(userJson);
63
+ } catch {
64
+ return null;
65
+ }
66
+ },
67
+
68
+ /**
69
+ * Get token expiration time
70
+ */
71
+ getExpiresAt: async (): Promise<number | null> => {
72
+ const expiresAt = await storage.getItem(keys.EXPIRES_AT);
73
+ return expiresAt ? parseInt(expiresAt, 10) : null;
74
+ },
75
+
76
+ /**
77
+ * Check if access token is expired
78
+ */
79
+ isTokenExpired: async (): Promise<boolean> => {
80
+ const expiresAt = await storage.getItem(keys.EXPIRES_AT);
81
+ if (!expiresAt) return true;
82
+ return Date.now() >= parseInt(expiresAt, 10);
83
+ },
84
+
85
+ /**
86
+ * Check if token will expire within threshold (in seconds)
87
+ */
88
+ willExpireSoon: async (thresholdSeconds: number = 300): Promise<boolean> => {
89
+ const expiresAt = await storage.getItem(keys.EXPIRES_AT);
90
+ if (!expiresAt) return true;
91
+ const expiryTime = parseInt(expiresAt, 10);
92
+ const thresholdMs = thresholdSeconds * 1000;
93
+ return Date.now() >= expiryTime - thresholdMs;
94
+ },
95
+
96
+ /**
97
+ * Store tokens and user data
98
+ */
99
+ setTokens: async (
100
+ accessToken: string,
101
+ refreshToken: string | null,
102
+ expiresIn: number | null,
103
+ user: User
104
+ ): Promise<void> => {
105
+ await storage.setItem(keys.ACCESS_TOKEN, accessToken);
106
+
107
+ if (refreshToken) {
108
+ await storage.setItem(keys.REFRESH_TOKEN, refreshToken);
109
+ }
110
+
111
+ if (expiresIn) {
112
+ const expiresAt = Date.now() + expiresIn * 1000;
113
+ await storage.setItem(keys.EXPIRES_AT, expiresAt.toString());
114
+ }
115
+
116
+ await storage.setItem(keys.USER, JSON.stringify(user));
117
+ },
118
+
119
+ /**
120
+ * Update tokens after refresh
121
+ */
122
+ updateTokens: async (tokens: TokenPair): Promise<void> => {
123
+ await storage.setItem(keys.ACCESS_TOKEN, tokens.accessToken);
124
+ await storage.setItem(keys.REFRESH_TOKEN, tokens.refreshToken);
125
+ const expiresAt = Date.now() + tokens.expiresIn * 1000;
126
+ await storage.setItem(keys.EXPIRES_AT, expiresAt.toString());
127
+ },
128
+
129
+ /**
130
+ * Update user data
131
+ */
132
+ updateUser: async (user: User): Promise<void> => {
133
+ await storage.setItem(keys.USER, JSON.stringify(user));
134
+ },
135
+
136
+ /**
137
+ * Clear all stored auth data
138
+ */
139
+ clear: async (): Promise<void> => {
140
+ await storage.removeItem(keys.ACCESS_TOKEN);
141
+ await storage.removeItem(keys.REFRESH_TOKEN);
142
+ await storage.removeItem(keys.USER);
143
+ await storage.removeItem(keys.EXPIRES_AT);
144
+ },
145
+
146
+ /**
147
+ * Get all stored auth data
148
+ */
149
+ getAll: async () => {
150
+ const [accessToken, refreshToken, user, expiresAt] = await Promise.all([
151
+ storage.getItem(keys.ACCESS_TOKEN),
152
+ storage.getItem(keys.REFRESH_TOKEN),
153
+ storage.getItem(keys.USER),
154
+ storage.getItem(keys.EXPIRES_AT),
155
+ ]);
156
+
157
+ return {
158
+ accessToken,
159
+ refreshToken,
160
+ user: user ? JSON.parse(user) : null,
161
+ expiresAt: expiresAt ? parseInt(expiresAt, 10) : null,
162
+ };
163
+ },
164
+ };
165
+ }
166
+
167
+ export type TokenStorage = ReturnType<typeof createTokenStorage>;