@pixygon/auth 1.0.0 → 1.1.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.
@@ -6,6 +6,7 @@
6
6
  import { useState, useCallback, type FormEvent } from 'react';
7
7
  import { useAuth } from '../hooks';
8
8
  import type { LoginFormProps, AuthError } from '../types';
9
+ import { DEFAULT_THEME } from '../types';
9
10
 
10
11
  export function LoginForm({
11
12
  onSuccess,
@@ -14,7 +15,9 @@ export function LoginForm({
14
15
  onNavigateForgotPassword,
15
16
  showBranding = true,
16
17
  className = '',
18
+ theme,
17
19
  }: LoginFormProps) {
20
+ const t = { ...DEFAULT_THEME, ...theme };
18
21
  const { login, isLoading, error } = useAuth();
19
22
 
20
23
  const [userName, setUserName] = useState('');
@@ -53,11 +56,11 @@ export function LoginForm({
53
56
  <div className={`pixygon-auth-container ${className}`}>
54
57
  <style>{`
55
58
  .pixygon-auth-container {
56
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
57
- background: #0f0f0f;
58
- color: #ffffff;
59
+ font-family: ${t.fontFamily === 'inherit' ? "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" : t.fontFamily};
60
+ background: ${t.backgroundColor};
61
+ color: ${t.textColor};
59
62
  padding: 2rem;
60
- border-radius: 1rem;
63
+ border-radius: ${t.borderRadius};
61
64
  max-width: 400px;
62
65
  width: 100%;
63
66
  margin: 0 auto;
@@ -83,7 +86,7 @@ export function LoginForm({
83
86
 
84
87
  .pixygon-auth-subtitle {
85
88
  font-size: 0.875rem;
86
- color: #a3a3a3;
89
+ color: ${t.textSecondary};
87
90
  margin: 0;
88
91
  }
89
92
 
@@ -102,31 +105,31 @@ export function LoginForm({
102
105
  .pixygon-auth-label {
103
106
  font-size: 0.875rem;
104
107
  font-weight: 500;
105
- color: #a3a3a3;
108
+ color: ${t.textSecondary};
106
109
  }
107
110
 
108
111
  .pixygon-auth-input {
109
- background: #262626;
112
+ background: ${t.surfaceColor};
110
113
  border: 1px solid #404040;
111
114
  border-radius: 0.5rem;
112
115
  padding: 0.75rem 1rem;
113
116
  font-size: 1rem;
114
- color: #ffffff;
117
+ color: ${t.textColor};
115
118
  outline: none;
116
119
  transition: border-color 0.2s, box-shadow 0.2s;
117
120
  }
118
121
 
119
122
  .pixygon-auth-input:focus {
120
- border-color: #6366f1;
123
+ border-color: ${t.primaryColor};
121
124
  box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
122
125
  }
123
126
 
124
127
  .pixygon-auth-input.error {
125
- border-color: #ef4444;
128
+ border-color: ${t.errorColor};
126
129
  }
127
130
 
128
131
  .pixygon-auth-button {
129
- background: #6366f1;
132
+ background: ${t.primaryColor};
130
133
  color: white;
131
134
  border: none;
132
135
  border-radius: 0.5rem;
@@ -153,7 +156,7 @@ export function LoginForm({
153
156
 
154
157
  .pixygon-auth-error {
155
158
  background: rgba(239, 68, 68, 0.1);
156
- border: 1px solid #ef4444;
159
+ border: 1px solid ${t.errorColor};
157
160
  border-radius: 0.5rem;
158
161
  padding: 0.75rem 1rem;
159
162
  color: #fca5a5;
@@ -161,7 +164,7 @@ export function LoginForm({
161
164
  }
162
165
 
163
166
  .pixygon-auth-link {
164
- color: #6366f1;
167
+ color: ${t.primaryColor};
165
168
  text-decoration: none;
166
169
  font-size: 0.875rem;
167
170
  cursor: pointer;
@@ -180,7 +183,7 @@ export function LoginForm({
180
183
  text-align: center;
181
184
  margin-top: 1.5rem;
182
185
  font-size: 0.875rem;
183
- color: #a3a3a3;
186
+ color: ${t.textSecondary};
184
187
  }
185
188
 
186
189
  .pixygon-auth-branding {
@@ -219,7 +222,7 @@ export function LoginForm({
219
222
  fill="none"
220
223
  xmlns="http://www.w3.org/2000/svg"
221
224
  >
222
- <circle cx="50" cy="50" r="45" fill="#6366f1" />
225
+ <circle cx="50" cy="50" r="45" fill={t.primaryColor} />
223
226
  <path
224
227
  d="M30 45L45 30L70 55L55 70L30 45Z"
225
228
  fill="white"
@@ -5,6 +5,9 @@
5
5
 
6
6
  import { useState, useCallback, useEffect } from 'react';
7
7
  import type { PixygonAuthProps, User, AuthError } from '../types';
8
+ import { DEFAULT_THEME } from '../types';
9
+ import type { AuthTheme } from '../types';
10
+ import { useAuthContext } from '../providers/AuthProvider';
8
11
  import { LoginForm } from './LoginForm';
9
12
  import { RegisterForm } from './RegisterForm';
10
13
  import { VerifyForm } from './VerifyForm';
@@ -22,6 +25,9 @@ export function PixygonAuth({
22
25
  theme = 'dark',
23
26
  className = '',
24
27
  }: PixygonAuthProps) {
28
+ const { config } = useAuthContext();
29
+ const mergedTheme: Required<AuthTheme> = { ...DEFAULT_THEME, ...config.theme };
30
+
25
31
  const [mode, setMode] = useState<AuthMode>(initialMode);
26
32
  const [userName, setUserName] = useState(initialUserName || '');
27
33
 
@@ -125,6 +131,7 @@ export function PixygonAuth({
125
131
  onNavigateRegister={() => handleModeChange('register')}
126
132
  onNavigateForgotPassword={() => handleModeChange('forgot-password')}
127
133
  showBranding={showBranding}
134
+ theme={mergedTheme}
128
135
  />
129
136
  )}
130
137
 
@@ -134,6 +141,7 @@ export function PixygonAuth({
134
141
  onError={handleError}
135
142
  onNavigateLogin={() => handleModeChange('login')}
136
143
  showBranding={showBranding}
144
+ theme={mergedTheme}
137
145
  />
138
146
  )}
139
147
 
@@ -144,6 +152,7 @@ export function PixygonAuth({
144
152
  onError={handleError}
145
153
  onNavigateLogin={() => handleModeChange('login')}
146
154
  showBranding={showBranding}
155
+ theme={mergedTheme}
147
156
  />
148
157
  )}
149
158
 
@@ -153,6 +162,7 @@ export function PixygonAuth({
153
162
  onError={handleError}
154
163
  onNavigateLogin={() => handleModeChange('login')}
155
164
  showBranding={showBranding}
165
+ theme={mergedTheme}
156
166
  />
157
167
  )}
158
168
 
@@ -163,6 +173,7 @@ export function PixygonAuth({
163
173
  onError={handleError}
164
174
  onNavigateLogin={() => handleModeChange('login')}
165
175
  showBranding={showBranding}
176
+ theme={mergedTheme}
166
177
  />
167
178
  )}
168
179
  </div>
@@ -6,6 +6,7 @@
6
6
  import { useState, useCallback, type FormEvent } from 'react';
7
7
  import { useAuth } from '../hooks';
8
8
  import type { RegisterFormProps, AuthError } from '../types';
9
+ import { DEFAULT_THEME } from '../types';
9
10
 
10
11
  export function RegisterForm({
11
12
  onSuccess,
@@ -13,7 +14,9 @@ export function RegisterForm({
13
14
  onNavigateLogin,
14
15
  showBranding = true,
15
16
  className = '',
17
+ theme,
16
18
  }: RegisterFormProps) {
19
+ const t = { ...DEFAULT_THEME, ...theme };
17
20
  const { register, isLoading, error } = useAuth();
18
21
 
19
22
  const [userName, setUserName] = useState('');
@@ -113,11 +116,11 @@ export function RegisterForm({
113
116
  <div className={`pixygon-auth-container ${className}`}>
114
117
  <style>{`
115
118
  .pixygon-auth-container {
116
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
117
- background: #0f0f0f;
118
- color: #ffffff;
119
+ font-family: ${t.fontFamily === 'inherit' ? "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" : t.fontFamily};
120
+ background: ${t.backgroundColor};
121
+ color: ${t.textColor};
119
122
  padding: 2rem;
120
- border-radius: 1rem;
123
+ border-radius: ${t.borderRadius};
121
124
  max-width: 400px;
122
125
  width: 100%;
123
126
  margin: 0 auto;
@@ -143,7 +146,7 @@ export function RegisterForm({
143
146
 
144
147
  .pixygon-auth-subtitle {
145
148
  font-size: 0.875rem;
146
- color: #a3a3a3;
149
+ color: ${t.textSecondary};
147
150
  margin: 0;
148
151
  }
149
152
 
@@ -162,31 +165,31 @@ export function RegisterForm({
162
165
  .pixygon-auth-label {
163
166
  font-size: 0.875rem;
164
167
  font-weight: 500;
165
- color: #a3a3a3;
168
+ color: ${t.textSecondary};
166
169
  }
167
170
 
168
171
  .pixygon-auth-input {
169
- background: #262626;
172
+ background: ${t.surfaceColor};
170
173
  border: 1px solid #404040;
171
174
  border-radius: 0.5rem;
172
175
  padding: 0.75rem 1rem;
173
176
  font-size: 1rem;
174
- color: #ffffff;
177
+ color: ${t.textColor};
175
178
  outline: none;
176
179
  transition: border-color 0.2s, box-shadow 0.2s;
177
180
  }
178
181
 
179
182
  .pixygon-auth-input:focus {
180
- border-color: #6366f1;
183
+ border-color: ${t.primaryColor};
181
184
  box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
182
185
  }
183
186
 
184
187
  .pixygon-auth-input.error {
185
- border-color: #ef4444;
188
+ border-color: ${t.errorColor};
186
189
  }
187
190
 
188
191
  .pixygon-auth-button {
189
- background: #6366f1;
192
+ background: ${t.primaryColor};
190
193
  color: white;
191
194
  border: none;
192
195
  border-radius: 0.5rem;
@@ -213,7 +216,7 @@ export function RegisterForm({
213
216
 
214
217
  .pixygon-auth-error {
215
218
  background: rgba(239, 68, 68, 0.1);
216
- border: 1px solid #ef4444;
219
+ border: 1px solid ${t.errorColor};
217
220
  border-radius: 0.5rem;
218
221
  padding: 0.75rem 1rem;
219
222
  color: #fca5a5;
@@ -221,7 +224,7 @@ export function RegisterForm({
221
224
  }
222
225
 
223
226
  .pixygon-auth-link {
224
- color: #6366f1;
227
+ color: ${t.primaryColor};
225
228
  text-decoration: none;
226
229
  font-size: 0.875rem;
227
230
  cursor: pointer;
@@ -240,7 +243,7 @@ export function RegisterForm({
240
243
  text-align: center;
241
244
  margin-top: 1.5rem;
242
245
  font-size: 0.875rem;
243
- color: #a3a3a3;
246
+ color: ${t.textSecondary};
244
247
  }
245
248
 
246
249
  .pixygon-auth-branding {
@@ -281,11 +284,11 @@ export function RegisterForm({
281
284
  }
282
285
 
283
286
  .pixygon-auth-password-strength-bar.active {
284
- background: #6366f1;
287
+ background: ${t.primaryColor};
285
288
  }
286
289
 
287
290
  .pixygon-auth-password-strength-bar.weak {
288
- background: #ef4444;
291
+ background: ${t.errorColor};
289
292
  }
290
293
 
291
294
  .pixygon-auth-password-strength-bar.fair {
@@ -293,7 +296,7 @@ export function RegisterForm({
293
296
  }
294
297
 
295
298
  .pixygon-auth-password-strength-bar.good {
296
- background: #22c55e;
299
+ background: ${t.successColor};
297
300
  }
298
301
 
299
302
  .pixygon-auth-password-strength-bar.strong {
@@ -314,7 +317,7 @@ export function RegisterForm({
314
317
  fill="none"
315
318
  xmlns="http://www.w3.org/2000/svg"
316
319
  >
317
- <circle cx="50" cy="50" r="45" fill="#6366f1" />
320
+ <circle cx="50" cy="50" r="45" fill={t.primaryColor} />
318
321
  <path
319
322
  d="M30 45L45 30L70 55L55 70L30 45Z"
320
323
  fill="white"
@@ -6,6 +6,7 @@
6
6
  import { useState, useCallback, useRef, useEffect, type FormEvent, type KeyboardEvent, type ClipboardEvent } from 'react';
7
7
  import { useAuth } from '../hooks';
8
8
  import type { VerifyFormProps, AuthError } from '../types';
9
+ import { DEFAULT_THEME } from '../types';
9
10
 
10
11
  export function VerifyForm({
11
12
  userName,
@@ -14,7 +15,9 @@ export function VerifyForm({
14
15
  onNavigateLogin,
15
16
  showBranding = true,
16
17
  className = '',
18
+ theme,
17
19
  }: VerifyFormProps) {
20
+ const t = { ...DEFAULT_THEME, ...theme };
18
21
  const { verify, resendVerification, isLoading, error } = useAuth();
19
22
 
20
23
  const [code, setCode] = useState(['', '', '', '', '', '']);
@@ -135,11 +138,11 @@ export function VerifyForm({
135
138
  <div className={`pixygon-auth-container ${className}`}>
136
139
  <style>{`
137
140
  .pixygon-auth-container {
138
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
139
- background: #0f0f0f;
140
- color: #ffffff;
141
+ font-family: ${t.fontFamily === 'inherit' ? "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" : t.fontFamily};
142
+ background: ${t.backgroundColor};
143
+ color: ${t.textColor};
141
144
  padding: 2rem;
142
- border-radius: 1rem;
145
+ border-radius: ${t.borderRadius};
143
146
  max-width: 400px;
144
147
  width: 100%;
145
148
  margin: 0 auto;
@@ -165,7 +168,7 @@ export function VerifyForm({
165
168
 
166
169
  .pixygon-auth-subtitle {
167
170
  font-size: 0.875rem;
168
- color: #a3a3a3;
171
+ color: ${t.textSecondary};
169
172
  margin: 0;
170
173
  }
171
174
 
@@ -187,25 +190,25 @@ export function VerifyForm({
187
190
  text-align: center;
188
191
  font-size: 1.5rem;
189
192
  font-weight: 600;
190
- background: #262626;
193
+ background: ${t.surfaceColor};
191
194
  border: 1px solid #404040;
192
195
  border-radius: 0.5rem;
193
- color: #ffffff;
196
+ color: ${t.textColor};
194
197
  outline: none;
195
198
  transition: border-color 0.2s, box-shadow 0.2s;
196
199
  }
197
200
 
198
201
  .pixygon-auth-code-input:focus {
199
- border-color: #6366f1;
202
+ border-color: ${t.primaryColor};
200
203
  box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
201
204
  }
202
205
 
203
206
  .pixygon-auth-code-input.error {
204
- border-color: #ef4444;
207
+ border-color: ${t.errorColor};
205
208
  }
206
209
 
207
210
  .pixygon-auth-button {
208
- background: #6366f1;
211
+ background: ${t.primaryColor};
209
212
  color: white;
210
213
  border: none;
211
214
  border-radius: 0.5rem;
@@ -231,7 +234,7 @@ export function VerifyForm({
231
234
 
232
235
  .pixygon-auth-error {
233
236
  background: rgba(239, 68, 68, 0.1);
234
- border: 1px solid #ef4444;
237
+ border: 1px solid ${t.errorColor};
235
238
  border-radius: 0.5rem;
236
239
  padding: 0.75rem 1rem;
237
240
  color: #fca5a5;
@@ -240,7 +243,7 @@ export function VerifyForm({
240
243
 
241
244
  .pixygon-auth-success {
242
245
  background: rgba(34, 197, 94, 0.1);
243
- border: 1px solid #22c55e;
246
+ border: 1px solid ${t.successColor};
244
247
  border-radius: 0.5rem;
245
248
  padding: 0.75rem 1rem;
246
249
  color: #86efac;
@@ -248,7 +251,7 @@ export function VerifyForm({
248
251
  }
249
252
 
250
253
  .pixygon-auth-link {
251
- color: #6366f1;
254
+ color: ${t.primaryColor};
252
255
  text-decoration: none;
253
256
  font-size: 0.875rem;
254
257
  cursor: pointer;
@@ -272,7 +275,7 @@ export function VerifyForm({
272
275
  text-align: center;
273
276
  margin-top: 1.5rem;
274
277
  font-size: 0.875rem;
275
- color: #a3a3a3;
278
+ color: ${t.textSecondary};
276
279
  }
277
280
 
278
281
  .pixygon-auth-branding {
@@ -301,7 +304,7 @@ export function VerifyForm({
301
304
  .pixygon-auth-resend {
302
305
  text-align: center;
303
306
  font-size: 0.875rem;
304
- color: #a3a3a3;
307
+ color: ${t.textSecondary};
305
308
  }
306
309
  `}</style>
307
310
 
@@ -312,7 +315,7 @@ export function VerifyForm({
312
315
  fill="none"
313
316
  xmlns="http://www.w3.org/2000/svg"
314
317
  >
315
- <circle cx="50" cy="50" r="45" fill="#6366f1" />
318
+ <circle cx="50" cy="50" r="45" fill={t.primaryColor} />
316
319
  <path
317
320
  d="M30 45L45 30L70 55L55 70L30 45Z"
318
321
  fill="white"
@@ -28,6 +28,7 @@ export interface UseAuthReturn {
28
28
  resendVerification: ReturnType<typeof useAuthContext>['resendVerification'];
29
29
  forgotPassword: ReturnType<typeof useAuthContext>['forgotPassword'];
30
30
  recoverPassword: ReturnType<typeof useAuthContext>['recoverPassword'];
31
+ changePassword: ReturnType<typeof useAuthContext>['changePassword'];
31
32
 
32
33
  // Utilities
33
34
  hasRole: (role: UserRole | UserRole[]) => boolean;
@@ -57,6 +58,7 @@ export function useAuth(): UseAuthReturn {
57
58
  resendVerification: context.resendVerification,
58
59
  forgotPassword: context.forgotPassword,
59
60
  recoverPassword: context.recoverPassword,
61
+ changePassword: context.changePassword,
60
62
 
61
63
  // Utilities
62
64
  hasRole: context.hasRole,
package/src/index.ts CHANGED
@@ -46,11 +46,17 @@ export type {
46
46
  RefreshTokenResponse,
47
47
  ResendVerificationRequest,
48
48
  ResendVerificationResponse,
49
+ ChangePasswordRequest,
50
+ ChangePasswordResponse,
51
+ LogoutResponse,
49
52
 
50
53
  // Error types
51
54
  AuthError,
52
55
  AuthErrorCode,
53
56
 
57
+ // Theme types
58
+ AuthTheme,
59
+
54
60
  // Config types
55
61
  AuthConfig,
56
62
  AuthStorage,
@@ -64,6 +70,9 @@ export type {
64
70
  PixygonAuthProps,
65
71
  } from './types';
66
72
 
73
+ // Theme
74
+ export { DEFAULT_THEME } from './types';
75
+
67
76
  // Provider
68
77
  export { AuthProvider, AuthContext, useAuthContext } from './providers/AuthProvider';
69
78
  export type { AuthProviderProps } from './providers/AuthProvider';
@@ -31,6 +31,8 @@ import type {
31
31
  RecoverPasswordResponse,
32
32
  ResendVerificationRequest,
33
33
  ResendVerificationResponse,
34
+ ChangePasswordRequest,
35
+ ChangePasswordResponse,
34
36
  } from '../types';
35
37
  import { createTokenStorage } from '../utils/storage';
36
38
  import { createAuthApiClient } from '../api/client';
@@ -368,9 +370,17 @@ export function AuthProvider({ config: userConfig, children }: AuthProviderProps
368
370
  [apiClient]
369
371
  );
370
372
 
373
+ const changePassword = useCallback(
374
+ async (data: ChangePasswordRequest): Promise<ChangePasswordResponse> => {
375
+ return apiClient.changePassword(data);
376
+ },
377
+ [apiClient]
378
+ );
379
+
371
380
  const logout = useCallback(async (): Promise<void> => {
372
381
  log('Logging out...');
373
382
 
383
+ await apiClient.logout();
374
384
  await tokenStorage.clear();
375
385
  apiClient.setAccessToken(null);
376
386
 
@@ -437,6 +447,7 @@ export function AuthProvider({ config: userConfig, children }: AuthProviderProps
437
447
  resendVerification,
438
448
  forgotPassword,
439
449
  recoverPassword,
450
+ changePassword,
440
451
  refreshTokens,
441
452
 
442
453
  // Utilities
@@ -455,6 +466,7 @@ export function AuthProvider({ config: userConfig, children }: AuthProviderProps
455
466
  resendVerification,
456
467
  forgotPassword,
457
468
  recoverPassword,
469
+ changePassword,
458
470
  refreshTokens,
459
471
  getAccessToken,
460
472
  hasRole,
@@ -9,7 +9,7 @@
9
9
 
10
10
  export type UserRole = 'user' | 'creator' | 'admin' | 'superadmin';
11
11
 
12
- export type SubscriptionTier = 'free' | 'basic' | 'pro' | 'family' | 'enterprise';
12
+ export type SubscriptionTier = 'free' | 'plus' | 'family' | 'basic' | 'pro' | 'enterprise';
13
13
 
14
14
  export interface User {
15
15
  _id: string;
@@ -164,6 +164,19 @@ export interface ResendVerificationResponse {
164
164
  message: string;
165
165
  }
166
166
 
167
+ export interface ChangePasswordRequest {
168
+ currentPassword: string;
169
+ newPassword: string;
170
+ }
171
+
172
+ export interface ChangePasswordResponse {
173
+ status: string;
174
+ }
175
+
176
+ export interface LogoutResponse {
177
+ status: string;
178
+ }
179
+
167
180
  // ============================================================================
168
181
  // Error Types
169
182
  // ============================================================================
@@ -190,12 +203,40 @@ export interface AuthError {
190
203
  details?: Record<string, unknown>;
191
204
  }
192
205
 
206
+ // ============================================================================
207
+ // Theme Types
208
+ // ============================================================================
209
+
210
+ export interface AuthTheme {
211
+ primaryColor?: string; // Default: '#6366f1'
212
+ backgroundColor?: string; // Default: '#0f0f0f'
213
+ surfaceColor?: string; // Default: '#262626'
214
+ textColor?: string; // Default: '#ffffff'
215
+ textSecondary?: string; // Default: '#a3a3a3'
216
+ errorColor?: string; // Default: '#ef4444'
217
+ successColor?: string; // Default: '#22c55e'
218
+ borderRadius?: string; // Default: '1rem'
219
+ fontFamily?: string; // Default: inherit
220
+ }
221
+
222
+ export const DEFAULT_THEME: Required<AuthTheme> = {
223
+ primaryColor: '#6366f1',
224
+ backgroundColor: '#0f0f0f',
225
+ surfaceColor: '#262626',
226
+ textColor: '#ffffff',
227
+ textSecondary: '#a3a3a3',
228
+ errorColor: '#ef4444',
229
+ successColor: '#22c55e',
230
+ borderRadius: '1rem',
231
+ fontFamily: 'inherit',
232
+ };
233
+
193
234
  // ============================================================================
194
235
  // Configuration Types
195
236
  // ============================================================================
196
237
 
197
238
  export interface AuthConfig {
198
- /** Base URL for the auth API (e.g., https://pixygon-server.onrender.com/v1) */
239
+ /** Base URL for the auth API (e.g., https://api.pixygon.com/v1) */
199
240
  baseUrl: string;
200
241
 
201
242
  /** App identifier for token storage keys */
@@ -227,6 +268,9 @@ export interface AuthConfig {
227
268
 
228
269
  /** Enable debug logging (default: false) */
229
270
  debug?: boolean;
271
+
272
+ /** Theme customization for branded components */
273
+ theme?: AuthTheme;
230
274
  }
231
275
 
232
276
  export interface AuthStorage {
@@ -248,6 +292,7 @@ export interface AuthContextValue extends AuthState {
248
292
  resendVerification: (data: ResendVerificationRequest) => Promise<ResendVerificationResponse>;
249
293
  forgotPassword: (data: ForgotPasswordRequest) => Promise<ForgotPasswordResponse>;
250
294
  recoverPassword: (data: RecoverPasswordRequest) => Promise<RecoverPasswordResponse>;
295
+ changePassword: (data: ChangePasswordRequest) => Promise<ChangePasswordResponse>;
251
296
  refreshTokens: () => Promise<void>;
252
297
 
253
298
  // Utility
@@ -271,6 +316,7 @@ export interface LoginFormProps {
271
316
  onNavigateForgotPassword?: () => void;
272
317
  showBranding?: boolean;
273
318
  className?: string;
319
+ theme?: AuthTheme;
274
320
  }
275
321
 
276
322
  export interface RegisterFormProps {
@@ -279,6 +325,7 @@ export interface RegisterFormProps {
279
325
  onNavigateLogin?: () => void;
280
326
  showBranding?: boolean;
281
327
  className?: string;
328
+ theme?: AuthTheme;
282
329
  }
283
330
 
284
331
  export interface VerifyFormProps {
@@ -288,6 +335,7 @@ export interface VerifyFormProps {
288
335
  onNavigateLogin?: () => void;
289
336
  showBranding?: boolean;
290
337
  className?: string;
338
+ theme?: AuthTheme;
291
339
  }
292
340
 
293
341
  export interface ForgotPasswordFormProps {
@@ -296,6 +344,7 @@ export interface ForgotPasswordFormProps {
296
344
  onNavigateLogin?: () => void;
297
345
  showBranding?: boolean;
298
346
  className?: string;
347
+ theme?: AuthTheme;
299
348
  }
300
349
 
301
350
  export interface PixygonAuthProps {
@@ -17,15 +17,27 @@ const getKeys = (appId: string) => ({
17
17
  const defaultStorage: AuthStorage = {
18
18
  getItem: (key: string) => {
19
19
  if (typeof window === 'undefined') return null;
20
- return localStorage.getItem(key);
20
+ try {
21
+ return localStorage.getItem(key);
22
+ } catch {
23
+ return null;
24
+ }
21
25
  },
22
26
  setItem: (key: string, value: string) => {
23
27
  if (typeof window === 'undefined') return;
24
- localStorage.setItem(key, value);
28
+ try {
29
+ localStorage.setItem(key, value);
30
+ } catch {
31
+ // Silently fail in private browsing
32
+ }
25
33
  },
26
34
  removeItem: (key: string) => {
27
35
  if (typeof window === 'undefined') return;
28
- localStorage.removeItem(key);
36
+ try {
37
+ localStorage.removeItem(key);
38
+ } catch {
39
+ // Silently fail in private browsing
40
+ }
29
41
  },
30
42
  };
31
43