@cyberia-auth/auth 0.1.0 → 0.1.2

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 CHANGED
@@ -69,6 +69,7 @@ Props:
69
69
  - `apiToken: string` - app API token
70
70
  - `oauthRedirectUri?: string` - redirect URL for Google/Microsoft OAuth
71
71
  - `initialMode?: 'login' | 'register'` - default mode
72
+ - `theme?: 'system' | 'light' | 'dark'` - color mode (`system` by default)
72
73
  - `hideModeSwitch?: boolean` - hide sign-in/sign-up toggle footer
73
74
  - `onAuthSuccess?: (result) => void` - callback with `{ token, user }`
74
75
 
@@ -81,6 +82,7 @@ Props:
81
82
  - `backendUrl: string`
82
83
  - `apiToken: string`
83
84
  - `oauthRedirectUri?: string`
85
+ - `theme?: 'system' | 'light' | 'dark'` - applies to modal + `UserButton` UI
84
86
  - `children: ReactNode`
85
87
  - `storageKey?: string` - localStorage key override (default `cyberia_auth_session`)
86
88
 
package/dist/index.cjs CHANGED
@@ -60,19 +60,29 @@ function CyberiaAuth({
60
60
  apiToken,
61
61
  oauthRedirectUri,
62
62
  initialMode = "register",
63
+ theme = "system",
63
64
  hideModeSwitch = false,
64
65
  onAuthSuccess
65
66
  }) {
66
67
  const backendBaseUrl = resolveBackendBaseUrl(backendUrl);
68
+ const resolvedApiToken = normalizeApiToken(apiToken);
69
+ const resolvedTheme = useResolvedTheme(theme);
70
+ const palette = getThemePalette(resolvedTheme);
67
71
  const [mode, setMode] = (0, import_react.useState)(initialMode);
68
- const [config, setConfig] = (0, import_react.useState)(() => getCachedAppConfig(apiToken));
72
+ const [config, setConfig] = (0, import_react.useState)(() => getCachedAppConfig(resolvedApiToken));
69
73
  const [isBrandingResolved, setIsBrandingResolved] = (0, import_react.useState)(
70
- () => Boolean(getCachedAppConfig(apiToken))
74
+ () => Boolean(getCachedAppConfig(resolvedApiToken))
71
75
  );
72
76
  const [message, setMessage] = (0, import_react.useState)("");
73
77
  const [isLoading, setIsLoading] = (0, import_react.useState)(false);
74
78
  (0, import_react.useEffect)(() => {
75
- const cached = getCachedAppConfig(apiToken);
79
+ if (!resolvedApiToken) {
80
+ setConfig(defaultAppConfig);
81
+ setIsBrandingResolved(true);
82
+ setMessage('Missing apiToken. Pass a valid app token to <CyberiaAuth apiToken="...">.');
83
+ return;
84
+ }
85
+ const cached = getCachedAppConfig(resolvedApiToken);
76
86
  if (cached) {
77
87
  setConfig(cached);
78
88
  setIsBrandingResolved(true);
@@ -80,10 +90,10 @@ function CyberiaAuth({
80
90
  setIsBrandingResolved(false);
81
91
  }
82
92
  void import_axios.default.get(`${backendBaseUrl}/api/public/app/config`, {
83
- params: { apiToken }
93
+ params: { apiToken: resolvedApiToken }
84
94
  }).then((res) => {
85
95
  setConfig(res.data);
86
- cacheAppConfig(apiToken, res.data);
96
+ cacheAppConfig(resolvedApiToken, res.data);
87
97
  }).catch((error) => {
88
98
  const errorMessage = error instanceof Error ? error.message : "Unable to load app config";
89
99
  setMessage(errorMessage);
@@ -93,16 +103,20 @@ function CyberiaAuth({
93
103
  }).finally(() => {
94
104
  setIsBrandingResolved(true);
95
105
  });
96
- }, [apiToken, backendBaseUrl]);
106
+ }, [resolvedApiToken, backendBaseUrl]);
97
107
  const handleSubmit = async (event) => {
98
108
  event.preventDefault();
109
+ if (!resolvedApiToken) {
110
+ setMessage('Missing apiToken. Pass a valid app token to <CyberiaAuth apiToken="...">.');
111
+ return;
112
+ }
99
113
  setIsLoading(true);
100
114
  setMessage("");
101
115
  const form = new FormData(event.currentTarget);
102
116
  try {
103
117
  const endpoint = mode === "register" ? "/api/public/auth/register" : "/api/public/auth/login";
104
118
  const payload = {
105
- apiToken,
119
+ apiToken: resolvedApiToken,
106
120
  email: String(form.get("email")),
107
121
  password: String(form.get("password")),
108
122
  displayName: String(form.get("displayName") ?? "")
@@ -122,12 +136,16 @@ function CyberiaAuth({
122
136
  }
123
137
  };
124
138
  const startOAuth = (provider) => {
139
+ if (!resolvedApiToken) {
140
+ setMessage('Missing apiToken. Pass a valid app token to <CyberiaAuth apiToken="...">.');
141
+ return;
142
+ }
125
143
  if (!oauthRedirectUri) {
126
144
  setMessage("oauthRedirectUri is required for social login");
127
145
  return;
128
146
  }
129
147
  const url = new URL(`${backendBaseUrl}/api/public/oauth/${provider}/start`);
130
- url.searchParams.set("apiToken", apiToken);
148
+ url.searchParams.set("apiToken", resolvedApiToken);
131
149
  url.searchParams.set("redirectUri", oauthRedirectUri);
132
150
  window.location.href = url.toString();
133
151
  };
@@ -140,15 +158,15 @@ function CyberiaAuth({
140
158
  "div",
141
159
  {
142
160
  style: {
143
- border: "1px solid #e5e7eb",
161
+ border: `1px solid ${palette.border}`,
144
162
  borderRadius: 14,
145
163
  padding: 20,
146
- background: "#ffffff",
164
+ background: palette.surface,
147
165
  fontFamily: "Inter, Arial, sans-serif",
148
166
  minHeight: 320,
149
167
  display: "grid",
150
168
  placeItems: "center",
151
- color: "#6b7280"
169
+ color: palette.textMuted
152
170
  },
153
171
  children: "Loading sign-in..."
154
172
  }
@@ -158,15 +176,16 @@ function CyberiaAuth({
158
176
  "div",
159
177
  {
160
178
  style: {
161
- border: "1px solid #e5e7eb",
179
+ border: `1px solid ${palette.border}`,
162
180
  borderRadius: 14,
163
181
  padding: 0,
164
- background: "#ffffff",
182
+ background: palette.surface,
165
183
  overflow: "hidden",
166
184
  fontFamily: "Inter, Arial, sans-serif",
167
185
  width: "100%",
168
186
  maxWidth: 500,
169
- margin: "0 auto"
187
+ margin: "0 auto",
188
+ color: palette.text
170
189
  },
171
190
  children: [
172
191
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 20, display: "grid", gap: 14 }, children: [
@@ -190,15 +209,15 @@ function CyberiaAuth({
190
209
  ),
191
210
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { textAlign: "center" }, children: [
192
211
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { style: { margin: "0 0 6px 0", fontSize: 16 }, children: title }),
193
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { margin: 0, color: "#6b7280", fontSize: 13 }, children: subtitle })
212
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { margin: 0, color: palette.textMuted, fontSize: 13 }, children: subtitle })
194
213
  ] })
195
214
  ] }),
196
215
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }, children: [
197
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", style: socialButtonStyle, onClick: () => startOAuth("google"), children: [
216
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", style: socialButtonStyle(palette), onClick: () => startOAuth("google"), children: [
198
217
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GoogleIcon, {}),
199
218
  "Google"
200
219
  ] }),
201
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", style: socialButtonStyle, onClick: () => startOAuth("microsoft"), children: [
220
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", style: socialButtonStyle(palette), onClick: () => startOAuth("microsoft"), children: [
202
221
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MicrosoftIcon, {}),
203
222
  "Microsoft"
204
223
  ] })
@@ -213,14 +232,14 @@ function CyberiaAuth({
213
232
  gap: 12
214
233
  },
215
234
  children: [
216
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { height: 1, background: "#e5e7eb" } }),
217
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#9ca3af", fontSize: 14 }, children: "or" }),
218
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { height: 1, background: "#e5e7eb" } })
235
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { height: 1, background: palette.border } }),
236
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: palette.textMuted, fontSize: 14 }, children: "or" }),
237
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { height: 1, background: palette.border } })
219
238
  ]
220
239
  }
221
240
  ),
222
241
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit: handleSubmit, style: { display: "grid", gap: 10 }, children: [
223
- mode === "register" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: fieldLabelStyle, children: [
242
+ mode === "register" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: fieldLabelStyle(palette), children: [
224
243
  "Full name",
225
244
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
226
245
  "input",
@@ -228,15 +247,15 @@ function CyberiaAuth({
228
247
  name: "displayName",
229
248
  placeholder: "Enter your full name",
230
249
  required: true,
231
- style: inputStyle
250
+ style: inputStyle(palette)
232
251
  }
233
252
  )
234
253
  ] }),
235
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: fieldLabelStyle, children: [
254
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: fieldLabelStyle(palette), children: [
236
255
  "Email address",
237
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "email", name: "email", placeholder: "Enter your email address", required: true, style: inputStyle })
256
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "email", name: "email", placeholder: "Enter your email address", required: true, style: inputStyle(palette) })
238
257
  ] }),
239
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: fieldLabelStyle, children: [
258
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: fieldLabelStyle(palette), children: [
240
259
  "Password",
241
260
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
242
261
  "input",
@@ -246,7 +265,7 @@ function CyberiaAuth({
246
265
  placeholder: "Enter your password",
247
266
  minLength: 8,
248
267
  required: true,
249
- style: inputStyle
268
+ style: inputStyle(palette)
250
269
  }
251
270
  )
252
271
  ] }),
@@ -274,10 +293,11 @@ function CyberiaAuth({
274
293
  "div",
275
294
  {
276
295
  style: {
277
- borderTop: "1px solid #e5e7eb",
296
+ borderTop: `1px solid ${palette.border}`,
278
297
  padding: 14,
279
298
  textAlign: "center",
280
- background: "#fafafa"
299
+ fontSize: 13,
300
+ background: palette.surfaceAlt
281
301
  },
282
302
  children: [
283
303
  mode === "login" ? "Don't have an account? " : "Already have an account? ",
@@ -289,7 +309,7 @@ function CyberiaAuth({
289
309
  style: {
290
310
  border: "none",
291
311
  background: "transparent",
292
- color: "#111827",
312
+ color: palette.text,
293
313
  textDecoration: "underline",
294
314
  fontWeight: 600,
295
315
  cursor: "pointer",
@@ -310,10 +330,13 @@ function CyberiaAuthProvider({
310
330
  backendUrl,
311
331
  apiToken,
312
332
  oauthRedirectUri,
333
+ theme = "system",
313
334
  children,
314
335
  storageKey = "cyberia_auth_session"
315
336
  }) {
316
337
  const backendBaseUrl = resolveBackendBaseUrl(backendUrl);
338
+ const resolvedApiToken = normalizeApiToken(apiToken);
339
+ const resolvedTheme = useResolvedTheme(theme);
317
340
  const [isLoaded, setIsLoaded] = (0, import_react.useState)(false);
318
341
  const [session, setSession] = (0, import_react.useState)(null);
319
342
  const [modalOpen, setModalOpen] = (0, import_react.useState)(false);
@@ -386,9 +409,10 @@ function CyberiaAuthProvider({
386
409
  setModalOpen(true);
387
410
  },
388
411
  signOut,
389
- setSessionFromAuth
412
+ setSessionFromAuth,
413
+ theme: resolvedTheme
390
414
  }),
391
- [isLoaded, session, setSessionFromAuth]
415
+ [isLoaded, resolvedTheme, session, setSessionFromAuth]
392
416
  );
393
417
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CyberiaAuthContext.Provider, { value, children: [
394
418
  children,
@@ -414,8 +438,9 @@ function CyberiaAuthProvider({
414
438
  CyberiaAuth,
415
439
  {
416
440
  backendUrl: backendBaseUrl,
417
- apiToken,
441
+ apiToken: resolvedApiToken,
418
442
  oauthRedirectUri,
443
+ theme,
419
444
  initialMode: modalMode,
420
445
  hideModeSwitch: false,
421
446
  onAuthSuccess: (result) => {
@@ -474,7 +499,8 @@ function SignOutButton({ children }) {
474
499
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: signOut, children: children ?? "Sign out" });
475
500
  }
476
501
  function UserButton() {
477
- const { user, signOut, openSignIn, openSignUp } = useCyberiaAuth();
502
+ const { user, signOut, openSignIn, openSignUp, theme } = useCyberiaAuth();
503
+ const palette = getThemePalette(theme);
478
504
  const [open, setOpen] = (0, import_react.useState)(false);
479
505
  const [manageOpen, setManageOpen] = (0, import_react.useState)(false);
480
506
  const [tab, setTab] = (0, import_react.useState)("profile");
@@ -505,8 +531,9 @@ function UserButton() {
505
531
  width: 36,
506
532
  height: 36,
507
533
  borderRadius: 999,
508
- border: "1px solid #d1d5db",
509
- background: "#fff",
534
+ border: `1px solid ${palette.border}`,
535
+ background: palette.surface,
536
+ color: palette.text,
510
537
  fontWeight: 700,
511
538
  cursor: "pointer"
512
539
  },
@@ -522,8 +549,8 @@ function UserButton() {
522
549
  right: 0,
523
550
  top: 42,
524
551
  minWidth: 320,
525
- background: "#fff",
526
- border: "1px solid #e5e7eb",
552
+ background: palette.surface,
553
+ border: `1px solid ${palette.border}`,
527
554
  borderRadius: 12,
528
555
  boxShadow: "0 12px 24px rgba(0,0,0,0.08)",
529
556
  padding: 0,
@@ -538,22 +565,22 @@ function UserButton() {
538
565
  width: 44,
539
566
  height: 44,
540
567
  borderRadius: 999,
541
- border: "1px solid #d1d5db",
568
+ border: `1px solid ${palette.border}`,
542
569
  display: "grid",
543
570
  placeItems: "center",
544
571
  fontWeight: 700,
545
- color: "#111827",
546
- background: "#f8fafc"
572
+ color: palette.text,
573
+ background: palette.surfaceAlt
547
574
  },
548
575
  children: initials
549
576
  }
550
577
  ),
551
578
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "grid" }, children: [
552
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { style: { fontSize: 16, color: "#111827", lineHeight: 1.2 }, children: profileName }),
553
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#111827", fontSize: 14 }, children: profileEmail })
579
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { style: { fontSize: 16, color: palette.text, lineHeight: 1.2 }, children: profileName }),
580
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: palette.text, fontSize: 14 }, children: profileEmail })
554
581
  ] })
555
582
  ] }),
556
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", { style: { border: 0, borderTop: "1px solid #e5e7eb", margin: 0 } }),
583
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", { style: { border: 0, borderTop: `1px solid ${palette.border}`, margin: 0 } }),
557
584
  !user && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
558
585
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
559
586
  "button",
@@ -607,9 +634,9 @@ function UserButton() {
607
634
  }
608
635
  )
609
636
  ] }),
610
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { borderTop: "1px solid #e5e7eb", textAlign: "center", padding: "12px 10px", color: "#6b7280" }, children: [
637
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { borderTop: `1px solid ${palette.border}`, textAlign: "center", padding: "12px 10px", color: palette.textMuted, fontSize: 13 }, children: [
611
638
  "Secured by ",
612
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "cyberia auth" })
639
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "Cyberia Auth" })
613
640
  ] })
614
641
  ]
615
642
  }
@@ -634,9 +661,9 @@ function UserButton() {
634
661
  width: "100%",
635
662
  maxWidth: 940,
636
663
  minHeight: 540,
637
- background: "#fff",
664
+ background: palette.surface,
638
665
  borderRadius: 10,
639
- border: "1px solid #e5e7eb",
666
+ border: `1px solid ${palette.border}`,
640
667
  display: "grid",
641
668
  gridTemplateColumns: "220px 1fr",
642
669
  overflow: "hidden"
@@ -647,15 +674,15 @@ function UserButton() {
647
674
  "aside",
648
675
  {
649
676
  style: {
650
- borderRight: "1px solid #e5e7eb",
677
+ borderRight: `1px solid ${palette.border}`,
651
678
  padding: 22,
652
- background: "#fafafa",
679
+ background: palette.surfaceAlt,
653
680
  display: "flex",
654
681
  flexDirection: "column"
655
682
  },
656
683
  children: [
657
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { style: { margin: 0, fontSize: 30, color: "#111827" }, children: "Account" }),
658
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 6, color: "#6b7280", fontSize: 12 }, children: "Manage your account info." }),
684
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { style: { margin: 0, fontSize: 30, color: palette.text }, children: "Account" }),
685
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 6, color: palette.textMuted, fontSize: 12 }, children: "Manage your account info." }),
659
686
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: 20, display: "grid", gap: 8 }, children: [
660
687
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
661
688
  "button",
@@ -664,8 +691,8 @@ function UserButton() {
664
691
  onClick: () => setTab("profile"),
665
692
  style: {
666
693
  ...tabButtonStyle,
667
- background: tab === "profile" ? "#ece8ff" : "transparent",
668
- color: tab === "profile" ? "#6d28d9" : "#4b5563"
694
+ background: tab === "profile" ? "#ffe8e8" : "transparent",
695
+ color: tab === "profile" ? "#d92828" : palette.textMuted
669
696
  },
670
697
  children: "Profile"
671
698
  }
@@ -677,14 +704,14 @@ function UserButton() {
677
704
  onClick: () => setTab("security"),
678
705
  style: {
679
706
  ...tabButtonStyle,
680
- background: tab === "security" ? "#ece8ff" : "transparent",
681
- color: tab === "security" ? "#6d28d9" : "#4b5563"
707
+ background: tab === "security" ? "#ffe8e8" : "transparent",
708
+ color: tab === "security" ? "#d92828" : palette.textMuted
682
709
  },
683
710
  children: "Security"
684
711
  }
685
712
  )
686
713
  ] }),
687
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: "auto", paddingTop: 24, color: "#6b7280", fontSize: 15 }, children: [
714
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: "auto", paddingTop: 24, color: palette.textMuted, fontSize: 15 }, children: [
688
715
  "Secure by ",
689
716
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "Cyberia Auth" })
690
717
  ] })
@@ -693,7 +720,7 @@ function UserButton() {
693
720
  ),
694
721
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("main", { style: { padding: 26 }, children: [
695
722
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
696
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { style: { margin: 0, fontSize: 22, color: "#111827" }, children: tab === "profile" ? "Profile details" : "Security" }),
723
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { style: { margin: 0, fontSize: 22, color: palette.text }, children: tab === "profile" ? "Profile details" : "Security" }),
697
724
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
698
725
  "button",
699
726
  {
@@ -704,10 +731,10 @@ function UserButton() {
704
731
  }
705
732
  )
706
733
  ] }),
707
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", { style: { border: 0, borderTop: "1px solid #e5e7eb", margin: "16px 0 20px" } }),
734
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", { style: { border: 0, borderTop: `1px solid ${palette.border}`, margin: "16px 0 20px" } }),
708
735
  tab === "profile" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "grid", gap: 20 }, children: [
709
736
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: profileRowStyle, children: [
710
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { color: "#374151", fontSize: 14 }, children: "Profile" }),
737
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { color: "#c5c7ca", fontSize: 14 }, children: "Profile" }),
711
738
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
712
739
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: avatarLargeStyle, children: initials }),
713
740
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 16 }, children: profileName })
@@ -715,25 +742,25 @@ function UserButton() {
715
742
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: linkButtonStyle, children: "Update profile" })
716
743
  ] }),
717
744
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: profileRowStyle, children: [
718
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { color: "#374151", fontSize: 14 }, children: "Email addresses" }),
745
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { color: "#c5c7ca", fontSize: 14 }, children: "Email addresses" }),
719
746
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 16 }, children: profileEmail }),
720
747
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#6b7280", fontSize: 14 }, children: "Primary" })
721
748
  ] }),
722
749
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: profileRowStyle, children: [
723
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { color: "#374151", fontSize: 14 }, children: "Connected accounts" }),
750
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { color: "#c5c7ca", fontSize: 14 }, children: "Connected accounts" }),
724
751
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 16 }, children: "Google / Microsoft" }),
725
752
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: linkButtonStyle, children: "Connect account" })
726
753
  ] })
727
754
  ] }),
728
755
  tab === "security" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "grid", gap: 20 }, children: [
729
756
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: profileRowStyle, children: [
730
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { color: "#374151", fontSize: 14 }, children: "Password" }),
757
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { color: "#c5c7ca", fontSize: 14 }, children: "Password" }),
731
758
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {}),
732
759
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: linkButtonStyle, children: "Set password" })
733
760
  ] }),
734
761
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...profileRowStyle, alignItems: "start" }, children: [
735
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { color: "#374151", fontSize: 14 }, children: "Active devices" }),
736
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 14, color: "#374151", maxWidth: 430 }, children: userAgent }),
762
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { color: "#c5c7ca", fontSize: 14 }, children: "Active devices" }),
763
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 14, color: "#c5c7ca", maxWidth: 430 }, children: userAgent }),
737
764
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#6b7280", fontSize: 14 }, children: "This device" })
738
765
  ] }),
739
766
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: profileRowStyle, children: [
@@ -787,25 +814,28 @@ var menuItemButtonStyle = {
787
814
  background: "transparent",
788
815
  cursor: "pointer"
789
816
  };
790
- var inputStyle = {
817
+ var inputStyle = (palette) => ({
791
818
  width: "100%",
792
819
  padding: 11,
793
820
  borderRadius: 9,
794
- border: "1px solid #d1d5db",
821
+ border: `1px solid ${palette.border}`,
822
+ background: palette.surface,
823
+ color: palette.text,
795
824
  fontSize: 13
796
- };
797
- var fieldLabelStyle = {
825
+ });
826
+ var fieldLabelStyle = (palette) => ({
798
827
  display: "grid",
799
828
  gap: 6,
800
829
  fontSize: 14,
801
- color: "#111827",
830
+ color: palette.text,
802
831
  fontWeight: 600
803
- };
804
- var socialButtonStyle = {
805
- border: "1px solid #d1d5db",
832
+ });
833
+ var socialButtonStyle = (palette) => ({
834
+ border: `1px solid ${palette.border}`,
806
835
  borderRadius: 9,
807
836
  padding: "10px 12px",
808
- background: "#ffffff",
837
+ background: palette.surface,
838
+ color: palette.text,
809
839
  cursor: "pointer",
810
840
  fontSize: 16,
811
841
  fontWeight: 300,
@@ -813,7 +843,7 @@ var socialButtonStyle = {
813
843
  alignItems: "center",
814
844
  justifyContent: "center",
815
845
  gap: 8
816
- };
846
+ });
817
847
  function GoogleIcon() {
818
848
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", height: "20", viewBox: "0 0 20 20", width: "20", "aria-hidden": "true", children: [
819
849
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M19.9905 10.1871C19.9905 9.36773 19.9224 8.7698 19.7752 8.14972H10.1992V11.848H15.8201C15.7068 12.7671 15.0948 14.1512 13.7349 15.0813L13.7159 15.2051L16.7436 17.497L16.9534 17.5174C18.8798 15.779 19.9905 13.2211 19.9905 10.1871Z", fill: "#4285F4" }),
@@ -841,6 +871,53 @@ function resolveBackendBaseUrl(backendUrl) {
841
871
  const candidate = backendUrl?.trim() || DEFAULT_BACKEND_URL;
842
872
  return candidate.replace(/\/+$/, "");
843
873
  }
874
+ function normalizeApiToken(apiToken) {
875
+ return apiToken.trim();
876
+ }
877
+ function getThemePalette(theme) {
878
+ if (theme === "dark") {
879
+ return {
880
+ surface: "#445364",
881
+ surfaceAlt: "#1f2937",
882
+ border: "#374151",
883
+ text: "#f9fafb",
884
+ textMuted: "#9ca3af"
885
+ };
886
+ }
887
+ return {
888
+ surface: "#ffffff",
889
+ surfaceAlt: "#fafafa",
890
+ border: "#e5e7eb",
891
+ text: "#111827",
892
+ textMuted: "#6b7280"
893
+ };
894
+ }
895
+ function useResolvedTheme(theme) {
896
+ const [resolvedTheme, setResolvedTheme] = (0, import_react.useState)(() => {
897
+ if (theme === "light" || theme === "dark") {
898
+ return theme;
899
+ }
900
+ if (typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches) {
901
+ return "dark";
902
+ }
903
+ return "light";
904
+ });
905
+ (0, import_react.useEffect)(() => {
906
+ if (theme === "light" || theme === "dark") {
907
+ setResolvedTheme(theme);
908
+ return;
909
+ }
910
+ if (typeof window === "undefined") {
911
+ return;
912
+ }
913
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
914
+ const updateTheme = () => setResolvedTheme(mediaQuery.matches ? "dark" : "light");
915
+ updateTheme();
916
+ mediaQuery.addEventListener("change", updateTheme);
917
+ return () => mediaQuery.removeEventListener("change", updateTheme);
918
+ }, [theme]);
919
+ return resolvedTheme;
920
+ }
844
921
  function getBrandingCacheKey(apiToken) {
845
922
  return `cyberia_auth_branding:${apiToken}`;
846
923
  }
@@ -887,7 +964,7 @@ var tabButtonStyle = {
887
964
  };
888
965
  var profileRowStyle = {
889
966
  display: "grid",
890
- gridTemplateColumns: "220px 1fr auto",
967
+ gridTemplateColumns: "150px 1fr auto",
891
968
  alignItems: "center",
892
969
  gap: 12,
893
970
  paddingBottom: 12,
@@ -907,7 +984,7 @@ var avatarLargeStyle = {
907
984
  var linkButtonStyle = {
908
985
  border: "none",
909
986
  background: "transparent",
910
- color: "#6d28d9",
987
+ color: "#d92828",
911
988
  cursor: "pointer",
912
989
  fontSize: 14
913
990
  };
package/dist/index.d.cts CHANGED
@@ -2,6 +2,8 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode } from 'react';
3
3
 
4
4
  type AuthMode = 'register' | 'login';
5
+ type ThemeMode = 'system' | 'light' | 'dark';
6
+ type ResolvedTheme = 'light' | 'dark';
5
7
  type AuthUser = {
6
8
  id: string;
7
9
  email: string;
@@ -16,10 +18,11 @@ type CyberiaAuthProps = {
16
18
  apiToken: string;
17
19
  oauthRedirectUri?: string;
18
20
  initialMode?: AuthMode;
21
+ theme?: ThemeMode;
19
22
  hideModeSwitch?: boolean;
20
23
  onAuthSuccess?: (data: AuthResult) => void;
21
24
  };
22
- declare function CyberiaAuth({ backendUrl, apiToken, oauthRedirectUri, initialMode, hideModeSwitch, onAuthSuccess, }: CyberiaAuthProps): react_jsx_runtime.JSX.Element;
25
+ declare function CyberiaAuth({ backendUrl, apiToken, oauthRedirectUri, initialMode, theme, hideModeSwitch, onAuthSuccess, }: CyberiaAuthProps): react_jsx_runtime.JSX.Element;
23
26
  type CyberiaAuthContextValue = {
24
27
  isLoaded: boolean;
25
28
  isSignedIn: boolean;
@@ -29,15 +32,17 @@ type CyberiaAuthContextValue = {
29
32
  openSignUp: () => void;
30
33
  signOut: () => void;
31
34
  setSessionFromAuth: (result: AuthResult) => void;
35
+ theme: ResolvedTheme;
32
36
  };
33
37
  type CyberiaAuthProviderProps = {
34
38
  backendUrl?: string;
35
39
  apiToken: string;
36
40
  oauthRedirectUri?: string;
41
+ theme?: ThemeMode;
37
42
  children: ReactNode;
38
43
  storageKey?: string;
39
44
  };
40
- declare function CyberiaAuthProvider({ backendUrl, apiToken, oauthRedirectUri, children, storageKey, }: CyberiaAuthProviderProps): react_jsx_runtime.JSX.Element;
45
+ declare function CyberiaAuthProvider({ backendUrl, apiToken, oauthRedirectUri, theme, children, storageKey, }: CyberiaAuthProviderProps): react_jsx_runtime.JSX.Element;
41
46
  declare function useCyberiaAuth(): CyberiaAuthContextValue;
42
47
  declare function Show({ when, children }: {
43
48
  when: 'signed-in' | 'signed-out';
package/dist/index.d.ts CHANGED
@@ -2,6 +2,8 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode } from 'react';
3
3
 
4
4
  type AuthMode = 'register' | 'login';
5
+ type ThemeMode = 'system' | 'light' | 'dark';
6
+ type ResolvedTheme = 'light' | 'dark';
5
7
  type AuthUser = {
6
8
  id: string;
7
9
  email: string;
@@ -16,10 +18,11 @@ type CyberiaAuthProps = {
16
18
  apiToken: string;
17
19
  oauthRedirectUri?: string;
18
20
  initialMode?: AuthMode;
21
+ theme?: ThemeMode;
19
22
  hideModeSwitch?: boolean;
20
23
  onAuthSuccess?: (data: AuthResult) => void;
21
24
  };
22
- declare function CyberiaAuth({ backendUrl, apiToken, oauthRedirectUri, initialMode, hideModeSwitch, onAuthSuccess, }: CyberiaAuthProps): react_jsx_runtime.JSX.Element;
25
+ declare function CyberiaAuth({ backendUrl, apiToken, oauthRedirectUri, initialMode, theme, hideModeSwitch, onAuthSuccess, }: CyberiaAuthProps): react_jsx_runtime.JSX.Element;
23
26
  type CyberiaAuthContextValue = {
24
27
  isLoaded: boolean;
25
28
  isSignedIn: boolean;
@@ -29,15 +32,17 @@ type CyberiaAuthContextValue = {
29
32
  openSignUp: () => void;
30
33
  signOut: () => void;
31
34
  setSessionFromAuth: (result: AuthResult) => void;
35
+ theme: ResolvedTheme;
32
36
  };
33
37
  type CyberiaAuthProviderProps = {
34
38
  backendUrl?: string;
35
39
  apiToken: string;
36
40
  oauthRedirectUri?: string;
41
+ theme?: ThemeMode;
37
42
  children: ReactNode;
38
43
  storageKey?: string;
39
44
  };
40
- declare function CyberiaAuthProvider({ backendUrl, apiToken, oauthRedirectUri, children, storageKey, }: CyberiaAuthProviderProps): react_jsx_runtime.JSX.Element;
45
+ declare function CyberiaAuthProvider({ backendUrl, apiToken, oauthRedirectUri, theme, children, storageKey, }: CyberiaAuthProviderProps): react_jsx_runtime.JSX.Element;
41
46
  declare function useCyberiaAuth(): CyberiaAuthContextValue;
42
47
  declare function Show({ when, children }: {
43
48
  when: 'signed-in' | 'signed-out';
package/dist/index.js CHANGED
@@ -21,19 +21,29 @@ function CyberiaAuth({
21
21
  apiToken,
22
22
  oauthRedirectUri,
23
23
  initialMode = "register",
24
+ theme = "system",
24
25
  hideModeSwitch = false,
25
26
  onAuthSuccess
26
27
  }) {
27
28
  const backendBaseUrl = resolveBackendBaseUrl(backendUrl);
29
+ const resolvedApiToken = normalizeApiToken(apiToken);
30
+ const resolvedTheme = useResolvedTheme(theme);
31
+ const palette = getThemePalette(resolvedTheme);
28
32
  const [mode, setMode] = useState(initialMode);
29
- const [config, setConfig] = useState(() => getCachedAppConfig(apiToken));
33
+ const [config, setConfig] = useState(() => getCachedAppConfig(resolvedApiToken));
30
34
  const [isBrandingResolved, setIsBrandingResolved] = useState(
31
- () => Boolean(getCachedAppConfig(apiToken))
35
+ () => Boolean(getCachedAppConfig(resolvedApiToken))
32
36
  );
33
37
  const [message, setMessage] = useState("");
34
38
  const [isLoading, setIsLoading] = useState(false);
35
39
  useEffect(() => {
36
- const cached = getCachedAppConfig(apiToken);
40
+ if (!resolvedApiToken) {
41
+ setConfig(defaultAppConfig);
42
+ setIsBrandingResolved(true);
43
+ setMessage('Missing apiToken. Pass a valid app token to <CyberiaAuth apiToken="...">.');
44
+ return;
45
+ }
46
+ const cached = getCachedAppConfig(resolvedApiToken);
37
47
  if (cached) {
38
48
  setConfig(cached);
39
49
  setIsBrandingResolved(true);
@@ -41,10 +51,10 @@ function CyberiaAuth({
41
51
  setIsBrandingResolved(false);
42
52
  }
43
53
  void axios.get(`${backendBaseUrl}/api/public/app/config`, {
44
- params: { apiToken }
54
+ params: { apiToken: resolvedApiToken }
45
55
  }).then((res) => {
46
56
  setConfig(res.data);
47
- cacheAppConfig(apiToken, res.data);
57
+ cacheAppConfig(resolvedApiToken, res.data);
48
58
  }).catch((error) => {
49
59
  const errorMessage = error instanceof Error ? error.message : "Unable to load app config";
50
60
  setMessage(errorMessage);
@@ -54,16 +64,20 @@ function CyberiaAuth({
54
64
  }).finally(() => {
55
65
  setIsBrandingResolved(true);
56
66
  });
57
- }, [apiToken, backendBaseUrl]);
67
+ }, [resolvedApiToken, backendBaseUrl]);
58
68
  const handleSubmit = async (event) => {
59
69
  event.preventDefault();
70
+ if (!resolvedApiToken) {
71
+ setMessage('Missing apiToken. Pass a valid app token to <CyberiaAuth apiToken="...">.');
72
+ return;
73
+ }
60
74
  setIsLoading(true);
61
75
  setMessage("");
62
76
  const form = new FormData(event.currentTarget);
63
77
  try {
64
78
  const endpoint = mode === "register" ? "/api/public/auth/register" : "/api/public/auth/login";
65
79
  const payload = {
66
- apiToken,
80
+ apiToken: resolvedApiToken,
67
81
  email: String(form.get("email")),
68
82
  password: String(form.get("password")),
69
83
  displayName: String(form.get("displayName") ?? "")
@@ -83,12 +97,16 @@ function CyberiaAuth({
83
97
  }
84
98
  };
85
99
  const startOAuth = (provider) => {
100
+ if (!resolvedApiToken) {
101
+ setMessage('Missing apiToken. Pass a valid app token to <CyberiaAuth apiToken="...">.');
102
+ return;
103
+ }
86
104
  if (!oauthRedirectUri) {
87
105
  setMessage("oauthRedirectUri is required for social login");
88
106
  return;
89
107
  }
90
108
  const url = new URL(`${backendBaseUrl}/api/public/oauth/${provider}/start`);
91
- url.searchParams.set("apiToken", apiToken);
109
+ url.searchParams.set("apiToken", resolvedApiToken);
92
110
  url.searchParams.set("redirectUri", oauthRedirectUri);
93
111
  window.location.href = url.toString();
94
112
  };
@@ -101,15 +119,15 @@ function CyberiaAuth({
101
119
  "div",
102
120
  {
103
121
  style: {
104
- border: "1px solid #e5e7eb",
122
+ border: `1px solid ${palette.border}`,
105
123
  borderRadius: 14,
106
124
  padding: 20,
107
- background: "#ffffff",
125
+ background: palette.surface,
108
126
  fontFamily: "Inter, Arial, sans-serif",
109
127
  minHeight: 320,
110
128
  display: "grid",
111
129
  placeItems: "center",
112
- color: "#6b7280"
130
+ color: palette.textMuted
113
131
  },
114
132
  children: "Loading sign-in..."
115
133
  }
@@ -119,15 +137,16 @@ function CyberiaAuth({
119
137
  "div",
120
138
  {
121
139
  style: {
122
- border: "1px solid #e5e7eb",
140
+ border: `1px solid ${palette.border}`,
123
141
  borderRadius: 14,
124
142
  padding: 0,
125
- background: "#ffffff",
143
+ background: palette.surface,
126
144
  overflow: "hidden",
127
145
  fontFamily: "Inter, Arial, sans-serif",
128
146
  width: "100%",
129
147
  maxWidth: 500,
130
- margin: "0 auto"
148
+ margin: "0 auto",
149
+ color: palette.text
131
150
  },
132
151
  children: [
133
152
  /* @__PURE__ */ jsxs("div", { style: { padding: 20, display: "grid", gap: 14 }, children: [
@@ -151,15 +170,15 @@ function CyberiaAuth({
151
170
  ),
152
171
  /* @__PURE__ */ jsxs("div", { style: { textAlign: "center" }, children: [
153
172
  /* @__PURE__ */ jsx("h3", { style: { margin: "0 0 6px 0", fontSize: 16 }, children: title }),
154
- /* @__PURE__ */ jsx("p", { style: { margin: 0, color: "#6b7280", fontSize: 13 }, children: subtitle })
173
+ /* @__PURE__ */ jsx("p", { style: { margin: 0, color: palette.textMuted, fontSize: 13 }, children: subtitle })
155
174
  ] })
156
175
  ] }),
157
176
  /* @__PURE__ */ jsxs("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }, children: [
158
- /* @__PURE__ */ jsxs("button", { type: "button", style: socialButtonStyle, onClick: () => startOAuth("google"), children: [
177
+ /* @__PURE__ */ jsxs("button", { type: "button", style: socialButtonStyle(palette), onClick: () => startOAuth("google"), children: [
159
178
  /* @__PURE__ */ jsx(GoogleIcon, {}),
160
179
  "Google"
161
180
  ] }),
162
- /* @__PURE__ */ jsxs("button", { type: "button", style: socialButtonStyle, onClick: () => startOAuth("microsoft"), children: [
181
+ /* @__PURE__ */ jsxs("button", { type: "button", style: socialButtonStyle(palette), onClick: () => startOAuth("microsoft"), children: [
163
182
  /* @__PURE__ */ jsx(MicrosoftIcon, {}),
164
183
  "Microsoft"
165
184
  ] })
@@ -174,14 +193,14 @@ function CyberiaAuth({
174
193
  gap: 12
175
194
  },
176
195
  children: [
177
- /* @__PURE__ */ jsx("div", { style: { height: 1, background: "#e5e7eb" } }),
178
- /* @__PURE__ */ jsx("span", { style: { color: "#9ca3af", fontSize: 14 }, children: "or" }),
179
- /* @__PURE__ */ jsx("div", { style: { height: 1, background: "#e5e7eb" } })
196
+ /* @__PURE__ */ jsx("div", { style: { height: 1, background: palette.border } }),
197
+ /* @__PURE__ */ jsx("span", { style: { color: palette.textMuted, fontSize: 14 }, children: "or" }),
198
+ /* @__PURE__ */ jsx("div", { style: { height: 1, background: palette.border } })
180
199
  ]
181
200
  }
182
201
  ),
183
202
  /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, style: { display: "grid", gap: 10 }, children: [
184
- mode === "register" && /* @__PURE__ */ jsxs("label", { style: fieldLabelStyle, children: [
203
+ mode === "register" && /* @__PURE__ */ jsxs("label", { style: fieldLabelStyle(palette), children: [
185
204
  "Full name",
186
205
  /* @__PURE__ */ jsx(
187
206
  "input",
@@ -189,15 +208,15 @@ function CyberiaAuth({
189
208
  name: "displayName",
190
209
  placeholder: "Enter your full name",
191
210
  required: true,
192
- style: inputStyle
211
+ style: inputStyle(palette)
193
212
  }
194
213
  )
195
214
  ] }),
196
- /* @__PURE__ */ jsxs("label", { style: fieldLabelStyle, children: [
215
+ /* @__PURE__ */ jsxs("label", { style: fieldLabelStyle(palette), children: [
197
216
  "Email address",
198
- /* @__PURE__ */ jsx("input", { type: "email", name: "email", placeholder: "Enter your email address", required: true, style: inputStyle })
217
+ /* @__PURE__ */ jsx("input", { type: "email", name: "email", placeholder: "Enter your email address", required: true, style: inputStyle(palette) })
199
218
  ] }),
200
- /* @__PURE__ */ jsxs("label", { style: fieldLabelStyle, children: [
219
+ /* @__PURE__ */ jsxs("label", { style: fieldLabelStyle(palette), children: [
201
220
  "Password",
202
221
  /* @__PURE__ */ jsx(
203
222
  "input",
@@ -207,7 +226,7 @@ function CyberiaAuth({
207
226
  placeholder: "Enter your password",
208
227
  minLength: 8,
209
228
  required: true,
210
- style: inputStyle
229
+ style: inputStyle(palette)
211
230
  }
212
231
  )
213
232
  ] }),
@@ -235,10 +254,11 @@ function CyberiaAuth({
235
254
  "div",
236
255
  {
237
256
  style: {
238
- borderTop: "1px solid #e5e7eb",
257
+ borderTop: `1px solid ${palette.border}`,
239
258
  padding: 14,
240
259
  textAlign: "center",
241
- background: "#fafafa"
260
+ fontSize: 13,
261
+ background: palette.surfaceAlt
242
262
  },
243
263
  children: [
244
264
  mode === "login" ? "Don't have an account? " : "Already have an account? ",
@@ -250,7 +270,7 @@ function CyberiaAuth({
250
270
  style: {
251
271
  border: "none",
252
272
  background: "transparent",
253
- color: "#111827",
273
+ color: palette.text,
254
274
  textDecoration: "underline",
255
275
  fontWeight: 600,
256
276
  cursor: "pointer",
@@ -271,10 +291,13 @@ function CyberiaAuthProvider({
271
291
  backendUrl,
272
292
  apiToken,
273
293
  oauthRedirectUri,
294
+ theme = "system",
274
295
  children,
275
296
  storageKey = "cyberia_auth_session"
276
297
  }) {
277
298
  const backendBaseUrl = resolveBackendBaseUrl(backendUrl);
299
+ const resolvedApiToken = normalizeApiToken(apiToken);
300
+ const resolvedTheme = useResolvedTheme(theme);
278
301
  const [isLoaded, setIsLoaded] = useState(false);
279
302
  const [session, setSession] = useState(null);
280
303
  const [modalOpen, setModalOpen] = useState(false);
@@ -347,9 +370,10 @@ function CyberiaAuthProvider({
347
370
  setModalOpen(true);
348
371
  },
349
372
  signOut,
350
- setSessionFromAuth
373
+ setSessionFromAuth,
374
+ theme: resolvedTheme
351
375
  }),
352
- [isLoaded, session, setSessionFromAuth]
376
+ [isLoaded, resolvedTheme, session, setSessionFromAuth]
353
377
  );
354
378
  return /* @__PURE__ */ jsxs(CyberiaAuthContext.Provider, { value, children: [
355
379
  children,
@@ -375,8 +399,9 @@ function CyberiaAuthProvider({
375
399
  CyberiaAuth,
376
400
  {
377
401
  backendUrl: backendBaseUrl,
378
- apiToken,
402
+ apiToken: resolvedApiToken,
379
403
  oauthRedirectUri,
404
+ theme,
380
405
  initialMode: modalMode,
381
406
  hideModeSwitch: false,
382
407
  onAuthSuccess: (result) => {
@@ -435,7 +460,8 @@ function SignOutButton({ children }) {
435
460
  return /* @__PURE__ */ jsx("button", { type: "button", onClick: signOut, children: children ?? "Sign out" });
436
461
  }
437
462
  function UserButton() {
438
- const { user, signOut, openSignIn, openSignUp } = useCyberiaAuth();
463
+ const { user, signOut, openSignIn, openSignUp, theme } = useCyberiaAuth();
464
+ const palette = getThemePalette(theme);
439
465
  const [open, setOpen] = useState(false);
440
466
  const [manageOpen, setManageOpen] = useState(false);
441
467
  const [tab, setTab] = useState("profile");
@@ -466,8 +492,9 @@ function UserButton() {
466
492
  width: 36,
467
493
  height: 36,
468
494
  borderRadius: 999,
469
- border: "1px solid #d1d5db",
470
- background: "#fff",
495
+ border: `1px solid ${palette.border}`,
496
+ background: palette.surface,
497
+ color: palette.text,
471
498
  fontWeight: 700,
472
499
  cursor: "pointer"
473
500
  },
@@ -483,8 +510,8 @@ function UserButton() {
483
510
  right: 0,
484
511
  top: 42,
485
512
  minWidth: 320,
486
- background: "#fff",
487
- border: "1px solid #e5e7eb",
513
+ background: palette.surface,
514
+ border: `1px solid ${palette.border}`,
488
515
  borderRadius: 12,
489
516
  boxShadow: "0 12px 24px rgba(0,0,0,0.08)",
490
517
  padding: 0,
@@ -499,22 +526,22 @@ function UserButton() {
499
526
  width: 44,
500
527
  height: 44,
501
528
  borderRadius: 999,
502
- border: "1px solid #d1d5db",
529
+ border: `1px solid ${palette.border}`,
503
530
  display: "grid",
504
531
  placeItems: "center",
505
532
  fontWeight: 700,
506
- color: "#111827",
507
- background: "#f8fafc"
533
+ color: palette.text,
534
+ background: palette.surfaceAlt
508
535
  },
509
536
  children: initials
510
537
  }
511
538
  ),
512
539
  /* @__PURE__ */ jsxs("div", { style: { display: "grid" }, children: [
513
- /* @__PURE__ */ jsx("strong", { style: { fontSize: 16, color: "#111827", lineHeight: 1.2 }, children: profileName }),
514
- /* @__PURE__ */ jsx("span", { style: { color: "#111827", fontSize: 14 }, children: profileEmail })
540
+ /* @__PURE__ */ jsx("strong", { style: { fontSize: 16, color: palette.text, lineHeight: 1.2 }, children: profileName }),
541
+ /* @__PURE__ */ jsx("span", { style: { color: palette.text, fontSize: 14 }, children: profileEmail })
515
542
  ] })
516
543
  ] }),
517
- /* @__PURE__ */ jsx("hr", { style: { border: 0, borderTop: "1px solid #e5e7eb", margin: 0 } }),
544
+ /* @__PURE__ */ jsx("hr", { style: { border: 0, borderTop: `1px solid ${palette.border}`, margin: 0 } }),
518
545
  !user && /* @__PURE__ */ jsxs(Fragment, { children: [
519
546
  /* @__PURE__ */ jsx(
520
547
  "button",
@@ -568,9 +595,9 @@ function UserButton() {
568
595
  }
569
596
  )
570
597
  ] }),
571
- /* @__PURE__ */ jsxs("div", { style: { borderTop: "1px solid #e5e7eb", textAlign: "center", padding: "12px 10px", color: "#6b7280" }, children: [
598
+ /* @__PURE__ */ jsxs("div", { style: { borderTop: `1px solid ${palette.border}`, textAlign: "center", padding: "12px 10px", color: palette.textMuted, fontSize: 13 }, children: [
572
599
  "Secured by ",
573
- /* @__PURE__ */ jsx("strong", { children: "cyberia auth" })
600
+ /* @__PURE__ */ jsx("strong", { children: "Cyberia Auth" })
574
601
  ] })
575
602
  ]
576
603
  }
@@ -595,9 +622,9 @@ function UserButton() {
595
622
  width: "100%",
596
623
  maxWidth: 940,
597
624
  minHeight: 540,
598
- background: "#fff",
625
+ background: palette.surface,
599
626
  borderRadius: 10,
600
- border: "1px solid #e5e7eb",
627
+ border: `1px solid ${palette.border}`,
601
628
  display: "grid",
602
629
  gridTemplateColumns: "220px 1fr",
603
630
  overflow: "hidden"
@@ -608,15 +635,15 @@ function UserButton() {
608
635
  "aside",
609
636
  {
610
637
  style: {
611
- borderRight: "1px solid #e5e7eb",
638
+ borderRight: `1px solid ${palette.border}`,
612
639
  padding: 22,
613
- background: "#fafafa",
640
+ background: palette.surfaceAlt,
614
641
  display: "flex",
615
642
  flexDirection: "column"
616
643
  },
617
644
  children: [
618
- /* @__PURE__ */ jsx("h2", { style: { margin: 0, fontSize: 30, color: "#111827" }, children: "Account" }),
619
- /* @__PURE__ */ jsx("p", { style: { marginTop: 6, color: "#6b7280", fontSize: 12 }, children: "Manage your account info." }),
645
+ /* @__PURE__ */ jsx("h2", { style: { margin: 0, fontSize: 30, color: palette.text }, children: "Account" }),
646
+ /* @__PURE__ */ jsx("p", { style: { marginTop: 6, color: palette.textMuted, fontSize: 12 }, children: "Manage your account info." }),
620
647
  /* @__PURE__ */ jsxs("div", { style: { marginTop: 20, display: "grid", gap: 8 }, children: [
621
648
  /* @__PURE__ */ jsx(
622
649
  "button",
@@ -625,8 +652,8 @@ function UserButton() {
625
652
  onClick: () => setTab("profile"),
626
653
  style: {
627
654
  ...tabButtonStyle,
628
- background: tab === "profile" ? "#ece8ff" : "transparent",
629
- color: tab === "profile" ? "#6d28d9" : "#4b5563"
655
+ background: tab === "profile" ? "#ffe8e8" : "transparent",
656
+ color: tab === "profile" ? "#d92828" : palette.textMuted
630
657
  },
631
658
  children: "Profile"
632
659
  }
@@ -638,14 +665,14 @@ function UserButton() {
638
665
  onClick: () => setTab("security"),
639
666
  style: {
640
667
  ...tabButtonStyle,
641
- background: tab === "security" ? "#ece8ff" : "transparent",
642
- color: tab === "security" ? "#6d28d9" : "#4b5563"
668
+ background: tab === "security" ? "#ffe8e8" : "transparent",
669
+ color: tab === "security" ? "#d92828" : palette.textMuted
643
670
  },
644
671
  children: "Security"
645
672
  }
646
673
  )
647
674
  ] }),
648
- /* @__PURE__ */ jsxs("div", { style: { marginTop: "auto", paddingTop: 24, color: "#6b7280", fontSize: 15 }, children: [
675
+ /* @__PURE__ */ jsxs("div", { style: { marginTop: "auto", paddingTop: 24, color: palette.textMuted, fontSize: 15 }, children: [
649
676
  "Secure by ",
650
677
  /* @__PURE__ */ jsx("strong", { children: "Cyberia Auth" })
651
678
  ] })
@@ -654,7 +681,7 @@ function UserButton() {
654
681
  ),
655
682
  /* @__PURE__ */ jsxs("main", { style: { padding: 26 }, children: [
656
683
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
657
- /* @__PURE__ */ jsx("h3", { style: { margin: 0, fontSize: 22, color: "#111827" }, children: tab === "profile" ? "Profile details" : "Security" }),
684
+ /* @__PURE__ */ jsx("h3", { style: { margin: 0, fontSize: 22, color: palette.text }, children: tab === "profile" ? "Profile details" : "Security" }),
658
685
  /* @__PURE__ */ jsx(
659
686
  "button",
660
687
  {
@@ -665,10 +692,10 @@ function UserButton() {
665
692
  }
666
693
  )
667
694
  ] }),
668
- /* @__PURE__ */ jsx("hr", { style: { border: 0, borderTop: "1px solid #e5e7eb", margin: "16px 0 20px" } }),
695
+ /* @__PURE__ */ jsx("hr", { style: { border: 0, borderTop: `1px solid ${palette.border}`, margin: "16px 0 20px" } }),
669
696
  tab === "profile" && /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: 20 }, children: [
670
697
  /* @__PURE__ */ jsxs("div", { style: profileRowStyle, children: [
671
- /* @__PURE__ */ jsx("div", { style: { color: "#374151", fontSize: 14 }, children: "Profile" }),
698
+ /* @__PURE__ */ jsx("div", { style: { color: "#c5c7ca", fontSize: 14 }, children: "Profile" }),
672
699
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
673
700
  /* @__PURE__ */ jsx("div", { style: avatarLargeStyle, children: initials }),
674
701
  /* @__PURE__ */ jsx("div", { style: { fontSize: 16 }, children: profileName })
@@ -676,25 +703,25 @@ function UserButton() {
676
703
  /* @__PURE__ */ jsx("button", { type: "button", style: linkButtonStyle, children: "Update profile" })
677
704
  ] }),
678
705
  /* @__PURE__ */ jsxs("div", { style: profileRowStyle, children: [
679
- /* @__PURE__ */ jsx("div", { style: { color: "#374151", fontSize: 14 }, children: "Email addresses" }),
706
+ /* @__PURE__ */ jsx("div", { style: { color: "#c5c7ca", fontSize: 14 }, children: "Email addresses" }),
680
707
  /* @__PURE__ */ jsx("div", { style: { fontSize: 16 }, children: profileEmail }),
681
708
  /* @__PURE__ */ jsx("span", { style: { color: "#6b7280", fontSize: 14 }, children: "Primary" })
682
709
  ] }),
683
710
  /* @__PURE__ */ jsxs("div", { style: profileRowStyle, children: [
684
- /* @__PURE__ */ jsx("div", { style: { color: "#374151", fontSize: 14 }, children: "Connected accounts" }),
711
+ /* @__PURE__ */ jsx("div", { style: { color: "#c5c7ca", fontSize: 14 }, children: "Connected accounts" }),
685
712
  /* @__PURE__ */ jsx("div", { style: { fontSize: 16 }, children: "Google / Microsoft" }),
686
713
  /* @__PURE__ */ jsx("button", { type: "button", style: linkButtonStyle, children: "Connect account" })
687
714
  ] })
688
715
  ] }),
689
716
  tab === "security" && /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: 20 }, children: [
690
717
  /* @__PURE__ */ jsxs("div", { style: profileRowStyle, children: [
691
- /* @__PURE__ */ jsx("div", { style: { color: "#374151", fontSize: 14 }, children: "Password" }),
718
+ /* @__PURE__ */ jsx("div", { style: { color: "#c5c7ca", fontSize: 14 }, children: "Password" }),
692
719
  /* @__PURE__ */ jsx("div", {}),
693
720
  /* @__PURE__ */ jsx("button", { type: "button", style: linkButtonStyle, children: "Set password" })
694
721
  ] }),
695
722
  /* @__PURE__ */ jsxs("div", { style: { ...profileRowStyle, alignItems: "start" }, children: [
696
- /* @__PURE__ */ jsx("div", { style: { color: "#374151", fontSize: 14 }, children: "Active devices" }),
697
- /* @__PURE__ */ jsx("div", { style: { fontSize: 14, color: "#374151", maxWidth: 430 }, children: userAgent }),
723
+ /* @__PURE__ */ jsx("div", { style: { color: "#c5c7ca", fontSize: 14 }, children: "Active devices" }),
724
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 14, color: "#c5c7ca", maxWidth: 430 }, children: userAgent }),
698
725
  /* @__PURE__ */ jsx("span", { style: { color: "#6b7280", fontSize: 14 }, children: "This device" })
699
726
  ] }),
700
727
  /* @__PURE__ */ jsxs("div", { style: profileRowStyle, children: [
@@ -748,25 +775,28 @@ var menuItemButtonStyle = {
748
775
  background: "transparent",
749
776
  cursor: "pointer"
750
777
  };
751
- var inputStyle = {
778
+ var inputStyle = (palette) => ({
752
779
  width: "100%",
753
780
  padding: 11,
754
781
  borderRadius: 9,
755
- border: "1px solid #d1d5db",
782
+ border: `1px solid ${palette.border}`,
783
+ background: palette.surface,
784
+ color: palette.text,
756
785
  fontSize: 13
757
- };
758
- var fieldLabelStyle = {
786
+ });
787
+ var fieldLabelStyle = (palette) => ({
759
788
  display: "grid",
760
789
  gap: 6,
761
790
  fontSize: 14,
762
- color: "#111827",
791
+ color: palette.text,
763
792
  fontWeight: 600
764
- };
765
- var socialButtonStyle = {
766
- border: "1px solid #d1d5db",
793
+ });
794
+ var socialButtonStyle = (palette) => ({
795
+ border: `1px solid ${palette.border}`,
767
796
  borderRadius: 9,
768
797
  padding: "10px 12px",
769
- background: "#ffffff",
798
+ background: palette.surface,
799
+ color: palette.text,
770
800
  cursor: "pointer",
771
801
  fontSize: 16,
772
802
  fontWeight: 300,
@@ -774,7 +804,7 @@ var socialButtonStyle = {
774
804
  alignItems: "center",
775
805
  justifyContent: "center",
776
806
  gap: 8
777
- };
807
+ });
778
808
  function GoogleIcon() {
779
809
  return /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", height: "20", viewBox: "0 0 20 20", width: "20", "aria-hidden": "true", children: [
780
810
  /* @__PURE__ */ jsx("path", { d: "M19.9905 10.1871C19.9905 9.36773 19.9224 8.7698 19.7752 8.14972H10.1992V11.848H15.8201C15.7068 12.7671 15.0948 14.1512 13.7349 15.0813L13.7159 15.2051L16.7436 17.497L16.9534 17.5174C18.8798 15.779 19.9905 13.2211 19.9905 10.1871Z", fill: "#4285F4" }),
@@ -802,6 +832,53 @@ function resolveBackendBaseUrl(backendUrl) {
802
832
  const candidate = backendUrl?.trim() || DEFAULT_BACKEND_URL;
803
833
  return candidate.replace(/\/+$/, "");
804
834
  }
835
+ function normalizeApiToken(apiToken) {
836
+ return apiToken.trim();
837
+ }
838
+ function getThemePalette(theme) {
839
+ if (theme === "dark") {
840
+ return {
841
+ surface: "#445364",
842
+ surfaceAlt: "#1f2937",
843
+ border: "#374151",
844
+ text: "#f9fafb",
845
+ textMuted: "#9ca3af"
846
+ };
847
+ }
848
+ return {
849
+ surface: "#ffffff",
850
+ surfaceAlt: "#fafafa",
851
+ border: "#e5e7eb",
852
+ text: "#111827",
853
+ textMuted: "#6b7280"
854
+ };
855
+ }
856
+ function useResolvedTheme(theme) {
857
+ const [resolvedTheme, setResolvedTheme] = useState(() => {
858
+ if (theme === "light" || theme === "dark") {
859
+ return theme;
860
+ }
861
+ if (typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches) {
862
+ return "dark";
863
+ }
864
+ return "light";
865
+ });
866
+ useEffect(() => {
867
+ if (theme === "light" || theme === "dark") {
868
+ setResolvedTheme(theme);
869
+ return;
870
+ }
871
+ if (typeof window === "undefined") {
872
+ return;
873
+ }
874
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
875
+ const updateTheme = () => setResolvedTheme(mediaQuery.matches ? "dark" : "light");
876
+ updateTheme();
877
+ mediaQuery.addEventListener("change", updateTheme);
878
+ return () => mediaQuery.removeEventListener("change", updateTheme);
879
+ }, [theme]);
880
+ return resolvedTheme;
881
+ }
805
882
  function getBrandingCacheKey(apiToken) {
806
883
  return `cyberia_auth_branding:${apiToken}`;
807
884
  }
@@ -848,7 +925,7 @@ var tabButtonStyle = {
848
925
  };
849
926
  var profileRowStyle = {
850
927
  display: "grid",
851
- gridTemplateColumns: "220px 1fr auto",
928
+ gridTemplateColumns: "150px 1fr auto",
852
929
  alignItems: "center",
853
930
  gap: 12,
854
931
  paddingBottom: 12,
@@ -868,7 +945,7 @@ var avatarLargeStyle = {
868
945
  var linkButtonStyle = {
869
946
  border: "none",
870
947
  background: "transparent",
871
- color: "#6d28d9",
948
+ color: "#d92828",
872
949
  cursor: "pointer",
873
950
  fontSize: 14
874
951
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyberia-auth/auth",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Cyberia Auth React components and provider",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",