@authon/react 0.2.1 → 0.3.1
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.ko.md +95 -0
- package/README.md +173 -90
- package/dist/index.cjs +2009 -157
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +174 -9
- package/dist/index.d.ts +174 -9
- package/dist/index.js +1996 -157
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
// src/AuthonProvider.tsx
|
|
2
4
|
import { createContext, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
5
|
import { Authon } from "@authon/js";
|
|
@@ -6,6 +8,7 @@ var AuthonContext = createContext(null);
|
|
|
6
8
|
function AuthonProvider({ publishableKey, children, config }) {
|
|
7
9
|
const [user, setUser] = useState(null);
|
|
8
10
|
const [isLoading, setIsLoading] = useState(true);
|
|
11
|
+
const [activeOrganization, setActiveOrganization] = useState(null);
|
|
9
12
|
const clientRef = useRef(null);
|
|
10
13
|
useEffect(() => {
|
|
11
14
|
const client = new Authon(publishableKey, config);
|
|
@@ -33,6 +36,7 @@ function AuthonProvider({ publishableKey, children, config }) {
|
|
|
33
36
|
const signOut = useCallback(async () => {
|
|
34
37
|
await clientRef.current?.signOut();
|
|
35
38
|
setUser(null);
|
|
39
|
+
setActiveOrganization(null);
|
|
36
40
|
}, []);
|
|
37
41
|
const openSignIn = useCallback(async () => {
|
|
38
42
|
await clientRef.current?.openSignIn();
|
|
@@ -48,13 +52,15 @@ function AuthonProvider({ publishableKey, children, config }) {
|
|
|
48
52
|
isSignedIn: !!user,
|
|
49
53
|
isLoading,
|
|
50
54
|
user,
|
|
55
|
+
activeOrganization,
|
|
56
|
+
setActiveOrganization,
|
|
51
57
|
signOut,
|
|
52
58
|
openSignIn,
|
|
53
59
|
openSignUp,
|
|
54
60
|
getToken,
|
|
55
61
|
client: clientRef.current
|
|
56
62
|
}),
|
|
57
|
-
[user, isLoading, signOut, openSignIn, openSignUp, getToken]
|
|
63
|
+
[user, isLoading, activeOrganization, signOut, openSignIn, openSignUp, getToken]
|
|
58
64
|
);
|
|
59
65
|
return /* @__PURE__ */ jsx(AuthonContext.Provider, { value, children });
|
|
60
66
|
}
|
|
@@ -75,201 +81,1328 @@ function useUser() {
|
|
|
75
81
|
return { user, isLoading };
|
|
76
82
|
}
|
|
77
83
|
|
|
78
|
-
// src/SignIn.tsx
|
|
79
|
-
import {
|
|
80
|
-
import {
|
|
81
|
-
|
|
84
|
+
// src/components/SignIn.tsx
|
|
85
|
+
import { useState as useState5 } from "react";
|
|
86
|
+
import { PROVIDER_COLORS, PROVIDER_DISPLAY_NAMES } from "@authon/shared";
|
|
87
|
+
import { getProviderButtonConfig } from "@authon/js";
|
|
88
|
+
|
|
89
|
+
// src/hooks/useBranding.ts
|
|
90
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
91
|
+
import { DEFAULT_BRANDING } from "@authon/shared";
|
|
92
|
+
var cache = /* @__PURE__ */ new Map();
|
|
93
|
+
function useBranding() {
|
|
82
94
|
const { client } = useAuthon();
|
|
83
|
-
const
|
|
95
|
+
const [state, setState] = useState2(() => {
|
|
96
|
+
const key = client?.publishableKey;
|
|
97
|
+
return cache.get(key ?? "") ?? { branding: DEFAULT_BRANDING, providers: [], isLoaded: false };
|
|
98
|
+
});
|
|
99
|
+
const fetchedRef = useRef2(false);
|
|
100
|
+
const fetchBranding = useCallback2(async () => {
|
|
101
|
+
if (!client || fetchedRef.current) return;
|
|
102
|
+
const key = client.publishableKey;
|
|
103
|
+
const cached = cache.get(key);
|
|
104
|
+
if (cached) {
|
|
105
|
+
setState(cached);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
fetchedRef.current = true;
|
|
109
|
+
try {
|
|
110
|
+
const providers = await client.getProviders();
|
|
111
|
+
const apiUrl = client.config?.apiUrl ?? "https://api.authon.dev";
|
|
112
|
+
const res = await fetch(`${apiUrl}/v1/auth/branding`, {
|
|
113
|
+
headers: { "x-api-key": key },
|
|
114
|
+
credentials: "include"
|
|
115
|
+
});
|
|
116
|
+
let branding = DEFAULT_BRANDING;
|
|
117
|
+
if (res.ok) {
|
|
118
|
+
const data = await res.json();
|
|
119
|
+
branding = { ...DEFAULT_BRANDING, ...data };
|
|
120
|
+
}
|
|
121
|
+
const next = { branding, providers, isLoaded: true };
|
|
122
|
+
cache.set(key, next);
|
|
123
|
+
setState(next);
|
|
124
|
+
} catch {
|
|
125
|
+
const fallback = { branding: DEFAULT_BRANDING, providers: [], isLoaded: true };
|
|
126
|
+
setState(fallback);
|
|
127
|
+
}
|
|
128
|
+
}, [client]);
|
|
84
129
|
useEffect2(() => {
|
|
85
|
-
|
|
86
|
-
|
|
130
|
+
fetchBranding();
|
|
131
|
+
}, [fetchBranding]);
|
|
132
|
+
return state;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/components/shared/ThemeProvider.tsx
|
|
136
|
+
import { createContext as createContext2, useContext as useContext2, useMemo as useMemo2 } from "react";
|
|
137
|
+
import { DEFAULT_BRANDING as DEFAULT_BRANDING2 } from "@authon/shared";
|
|
138
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
139
|
+
var ThemeContext = createContext2(null);
|
|
140
|
+
function useTheme() {
|
|
141
|
+
const ctx = useContext2(ThemeContext);
|
|
142
|
+
if (!ctx) {
|
|
143
|
+
return resolveTheme(DEFAULT_BRANDING2, false);
|
|
144
|
+
}
|
|
145
|
+
return ctx;
|
|
146
|
+
}
|
|
147
|
+
function resolveTheme(branding, _dark) {
|
|
148
|
+
const radius = branding.borderRadius ?? DEFAULT_BRANDING2.borderRadius ?? 12;
|
|
149
|
+
return {
|
|
150
|
+
primaryStart: branding.primaryColorStart ?? DEFAULT_BRANDING2.primaryColorStart ?? "#7c3aed",
|
|
151
|
+
primaryEnd: branding.primaryColorEnd ?? DEFAULT_BRANDING2.primaryColorEnd ?? "#4f46e5",
|
|
152
|
+
bg: branding.lightBg ?? DEFAULT_BRANDING2.lightBg ?? "#ffffff",
|
|
153
|
+
text: branding.lightText ?? DEFAULT_BRANDING2.lightText ?? "#111827",
|
|
154
|
+
textMuted: "#6b7280",
|
|
155
|
+
border: "#e5e7eb",
|
|
156
|
+
borderRadius: `${radius}px`,
|
|
157
|
+
fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
158
|
+
inputStyle: "outline"
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function ThemeProvider({ branding, children, overrides, style, className }) {
|
|
162
|
+
const merged = useMemo2(() => ({ ...branding, ...overrides }), [branding, overrides]);
|
|
163
|
+
const theme = useMemo2(() => resolveTheme(merged, false), [merged]);
|
|
164
|
+
const cssVars = {
|
|
165
|
+
"--authon-primary-start": theme.primaryStart,
|
|
166
|
+
"--authon-primary-end": theme.primaryEnd,
|
|
167
|
+
"--authon-bg": theme.bg,
|
|
168
|
+
"--authon-text": theme.text,
|
|
169
|
+
"--authon-text-muted": theme.textMuted,
|
|
170
|
+
"--authon-border": theme.border,
|
|
171
|
+
"--authon-radius": theme.borderRadius,
|
|
172
|
+
"--authon-font": theme.fontFamily
|
|
173
|
+
};
|
|
174
|
+
return /* @__PURE__ */ jsx2(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ jsx2(
|
|
175
|
+
"div",
|
|
176
|
+
{
|
|
177
|
+
className,
|
|
178
|
+
style: {
|
|
179
|
+
fontFamily: theme.fontFamily,
|
|
180
|
+
color: theme.text,
|
|
181
|
+
...cssVars,
|
|
182
|
+
...style
|
|
183
|
+
},
|
|
184
|
+
children
|
|
87
185
|
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
186
|
+
) });
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/components/shared/Input.tsx
|
|
190
|
+
import { useState as useState3 } from "react";
|
|
191
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
192
|
+
function Input({
|
|
193
|
+
label,
|
|
194
|
+
error,
|
|
195
|
+
hint,
|
|
196
|
+
inputStyle,
|
|
197
|
+
onChange,
|
|
198
|
+
rightElement,
|
|
199
|
+
style: userStyle,
|
|
200
|
+
...rest
|
|
201
|
+
}) {
|
|
202
|
+
const theme = useTheme();
|
|
203
|
+
const [focused, setFocused] = useState3(false);
|
|
204
|
+
const resolvedStyle = inputStyle ?? theme.inputStyle;
|
|
205
|
+
const wrapperStyle = {
|
|
206
|
+
display: "flex",
|
|
207
|
+
flexDirection: "column",
|
|
208
|
+
gap: 6,
|
|
209
|
+
width: "100%"
|
|
210
|
+
};
|
|
211
|
+
const labelStyle = {
|
|
212
|
+
fontSize: 13,
|
|
213
|
+
fontWeight: 500,
|
|
214
|
+
color: error ? "#ef4444" : theme.text
|
|
215
|
+
};
|
|
216
|
+
const inputContainerStyle = {
|
|
217
|
+
position: "relative",
|
|
218
|
+
display: "flex",
|
|
219
|
+
alignItems: "center"
|
|
220
|
+
};
|
|
221
|
+
const baseInputStyle = {
|
|
222
|
+
width: "100%",
|
|
223
|
+
height: 44,
|
|
224
|
+
paddingLeft: 14,
|
|
225
|
+
paddingRight: rightElement ? 44 : 14,
|
|
226
|
+
borderRadius: theme.borderRadius,
|
|
227
|
+
fontFamily: theme.fontFamily,
|
|
228
|
+
fontSize: 15,
|
|
229
|
+
color: theme.text,
|
|
230
|
+
outline: "none",
|
|
231
|
+
transition: "border-color 0.15s, box-shadow 0.15s, background 0.15s",
|
|
232
|
+
boxSizing: "border-box",
|
|
233
|
+
...userStyle
|
|
234
|
+
};
|
|
235
|
+
let inputVariantStyle = {};
|
|
236
|
+
if (resolvedStyle === "filled") {
|
|
237
|
+
inputVariantStyle = {
|
|
238
|
+
background: focused ? `${theme.primaryStart}0d` : "#f3f4f6",
|
|
239
|
+
border: `1.5px solid ${error ? "#ef4444" : focused ? theme.primaryStart : "transparent"}`,
|
|
240
|
+
boxShadow: focused && !error ? `0 0 0 3px ${theme.primaryStart}22` : "none"
|
|
241
|
+
};
|
|
242
|
+
} else {
|
|
243
|
+
inputVariantStyle = {
|
|
244
|
+
background: theme.bg,
|
|
245
|
+
border: `1.5px solid ${error ? "#ef4444" : focused ? theme.primaryStart : theme.border}`,
|
|
246
|
+
boxShadow: focused && !error ? `0 0 0 3px ${theme.primaryStart}22` : "none"
|
|
247
|
+
};
|
|
91
248
|
}
|
|
92
|
-
|
|
249
|
+
const rightStyle = {
|
|
250
|
+
position: "absolute",
|
|
251
|
+
right: 12,
|
|
252
|
+
display: "flex",
|
|
253
|
+
alignItems: "center",
|
|
254
|
+
color: theme.textMuted
|
|
255
|
+
};
|
|
256
|
+
const hintStyle = {
|
|
257
|
+
fontSize: 12,
|
|
258
|
+
color: error ? "#ef4444" : theme.textMuted
|
|
259
|
+
};
|
|
260
|
+
return /* @__PURE__ */ jsxs("div", { style: wrapperStyle, children: [
|
|
261
|
+
label && /* @__PURE__ */ jsx3("label", { style: labelStyle, children: label }),
|
|
262
|
+
/* @__PURE__ */ jsxs("div", { style: inputContainerStyle, children: [
|
|
263
|
+
/* @__PURE__ */ jsx3(
|
|
264
|
+
"input",
|
|
265
|
+
{
|
|
266
|
+
...rest,
|
|
267
|
+
style: { ...baseInputStyle, ...inputVariantStyle },
|
|
268
|
+
onFocus: (e) => {
|
|
269
|
+
setFocused(true);
|
|
270
|
+
rest.onFocus?.(e);
|
|
271
|
+
},
|
|
272
|
+
onBlur: (e) => {
|
|
273
|
+
setFocused(false);
|
|
274
|
+
rest.onBlur?.(e);
|
|
275
|
+
},
|
|
276
|
+
onChange: (e) => onChange?.(e.target.value)
|
|
277
|
+
}
|
|
278
|
+
),
|
|
279
|
+
rightElement && /* @__PURE__ */ jsx3("div", { style: rightStyle, children: rightElement })
|
|
280
|
+
] }),
|
|
281
|
+
(error || hint) && /* @__PURE__ */ jsx3("span", { style: hintStyle, children: error ?? hint })
|
|
282
|
+
] });
|
|
93
283
|
}
|
|
94
284
|
|
|
95
|
-
// src/
|
|
96
|
-
import {
|
|
97
|
-
import { jsx as
|
|
98
|
-
|
|
285
|
+
// src/components/shared/Button.tsx
|
|
286
|
+
import { useState as useState4 } from "react";
|
|
287
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
288
|
+
var SPINNER_STYLE = {
|
|
289
|
+
display: "inline-block",
|
|
290
|
+
width: 16,
|
|
291
|
+
height: 16,
|
|
292
|
+
border: "2px solid currentColor",
|
|
293
|
+
borderTopColor: "transparent",
|
|
294
|
+
borderRadius: "50%",
|
|
295
|
+
animation: "authon-spin 0.6s linear infinite",
|
|
296
|
+
flexShrink: 0
|
|
297
|
+
};
|
|
298
|
+
function Button({
|
|
299
|
+
variant = "primary",
|
|
300
|
+
size = "md",
|
|
301
|
+
fullWidth = false,
|
|
302
|
+
loading = false,
|
|
303
|
+
disabled = false,
|
|
304
|
+
children,
|
|
305
|
+
onClick,
|
|
306
|
+
type = "button",
|
|
307
|
+
style: userStyle
|
|
308
|
+
}) {
|
|
309
|
+
const theme = useTheme();
|
|
310
|
+
const [hovered, setHovered] = useState4(false);
|
|
311
|
+
const sizeMap = {
|
|
312
|
+
sm: { height: 36, paddingLeft: 12, paddingRight: 12, fontSize: 13 },
|
|
313
|
+
md: { height: 44, paddingLeft: 16, paddingRight: 16, fontSize: 15 },
|
|
314
|
+
lg: { height: 52, paddingLeft: 20, paddingRight: 20, fontSize: 16 }
|
|
315
|
+
};
|
|
316
|
+
const base = {
|
|
317
|
+
display: "inline-flex",
|
|
318
|
+
alignItems: "center",
|
|
319
|
+
justifyContent: "center",
|
|
320
|
+
gap: 8,
|
|
321
|
+
borderRadius: theme.borderRadius,
|
|
322
|
+
fontFamily: theme.fontFamily,
|
|
323
|
+
fontWeight: 600,
|
|
324
|
+
border: "none",
|
|
325
|
+
cursor: disabled || loading ? "not-allowed" : "pointer",
|
|
326
|
+
transition: "opacity 0.15s, transform 0.1s",
|
|
327
|
+
width: fullWidth ? "100%" : void 0,
|
|
328
|
+
opacity: disabled ? 0.55 : hovered && !disabled && !loading ? 0.88 : 1,
|
|
329
|
+
transform: hovered && !disabled && !loading ? "translateY(-1px)" : void 0,
|
|
330
|
+
userSelect: "none",
|
|
331
|
+
...sizeMap[size]
|
|
332
|
+
};
|
|
333
|
+
let variantStyle = {};
|
|
334
|
+
switch (variant) {
|
|
335
|
+
case "primary":
|
|
336
|
+
variantStyle = {
|
|
337
|
+
background: `linear-gradient(135deg, ${theme.primaryStart}, ${theme.primaryEnd})`,
|
|
338
|
+
color: "#ffffff",
|
|
339
|
+
boxShadow: hovered ? `0 4px 16px ${theme.primaryStart}55` : "0 2px 8px rgba(0,0,0,0.1)"
|
|
340
|
+
};
|
|
341
|
+
break;
|
|
342
|
+
case "secondary":
|
|
343
|
+
variantStyle = {
|
|
344
|
+
background: `${theme.primaryStart}18`,
|
|
345
|
+
color: theme.primaryStart
|
|
346
|
+
};
|
|
347
|
+
break;
|
|
348
|
+
case "outline":
|
|
349
|
+
variantStyle = {
|
|
350
|
+
background: "transparent",
|
|
351
|
+
color: theme.text,
|
|
352
|
+
border: `1.5px solid ${theme.border}`
|
|
353
|
+
};
|
|
354
|
+
break;
|
|
355
|
+
case "ghost":
|
|
356
|
+
variantStyle = {
|
|
357
|
+
background: "transparent",
|
|
358
|
+
color: theme.textMuted
|
|
359
|
+
};
|
|
360
|
+
break;
|
|
361
|
+
case "social":
|
|
362
|
+
variantStyle = {
|
|
363
|
+
background: theme.bg,
|
|
364
|
+
color: theme.text,
|
|
365
|
+
border: `1.5px solid ${theme.border}`
|
|
366
|
+
};
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
370
|
+
/* @__PURE__ */ jsx4("style", { children: `@keyframes authon-spin { to { transform: rotate(360deg); } }` }),
|
|
371
|
+
/* @__PURE__ */ jsx4(
|
|
372
|
+
"button",
|
|
373
|
+
{
|
|
374
|
+
type,
|
|
375
|
+
disabled: disabled || loading,
|
|
376
|
+
onClick,
|
|
377
|
+
onMouseEnter: () => setHovered(true),
|
|
378
|
+
onMouseLeave: () => setHovered(false),
|
|
379
|
+
style: { ...base, ...variantStyle, ...userStyle },
|
|
380
|
+
children: loading ? /* @__PURE__ */ jsx4("span", { style: SPINNER_STYLE }) : children
|
|
381
|
+
}
|
|
382
|
+
)
|
|
383
|
+
] });
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// src/components/shared/Divider.tsx
|
|
387
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
388
|
+
function Divider({ label = "Or continue with" }) {
|
|
389
|
+
const theme = useTheme();
|
|
390
|
+
const containerStyle = {
|
|
391
|
+
display: "flex",
|
|
392
|
+
alignItems: "center",
|
|
393
|
+
gap: 12,
|
|
394
|
+
margin: "4px 0"
|
|
395
|
+
};
|
|
396
|
+
const lineStyle = {
|
|
397
|
+
flex: 1,
|
|
398
|
+
height: 1,
|
|
399
|
+
background: theme.border
|
|
400
|
+
};
|
|
401
|
+
const textStyle = {
|
|
402
|
+
fontSize: 13,
|
|
403
|
+
color: theme.textMuted,
|
|
404
|
+
whiteSpace: "nowrap",
|
|
405
|
+
fontWeight: 400
|
|
406
|
+
};
|
|
407
|
+
return /* @__PURE__ */ jsxs3("div", { style: containerStyle, children: [
|
|
408
|
+
/* @__PURE__ */ jsx5("div", { style: lineStyle }),
|
|
409
|
+
/* @__PURE__ */ jsx5("span", { style: textStyle, children: label }),
|
|
410
|
+
/* @__PURE__ */ jsx5("div", { style: lineStyle })
|
|
411
|
+
] });
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// src/components/SignIn.tsx
|
|
415
|
+
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
416
|
+
function SignInCard({ afterSignInUrl, onSignIn, onNavigateSignUp }) {
|
|
417
|
+
const theme = useTheme();
|
|
99
418
|
const { client } = useAuthon();
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
419
|
+
const { branding, providers, isLoaded } = useBranding();
|
|
420
|
+
const [email, setEmail] = useState5("");
|
|
421
|
+
const [password, setPassword] = useState5("");
|
|
422
|
+
const [showPassword, setShowPassword] = useState5(false);
|
|
423
|
+
const [loading, setLoading] = useState5(false);
|
|
424
|
+
const [oauthLoading, setOauthLoading] = useState5(null);
|
|
425
|
+
const [error, setError] = useState5("");
|
|
426
|
+
const [fieldErrors, setFieldErrors] = useState5({});
|
|
427
|
+
const validate = () => {
|
|
428
|
+
const errs = {};
|
|
429
|
+
if (!email.trim()) errs.email = "Email is required";
|
|
430
|
+
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) errs.email = "Invalid email address";
|
|
431
|
+
if (!password) errs.password = "Password is required";
|
|
432
|
+
setFieldErrors(errs);
|
|
433
|
+
return Object.keys(errs).length === 0;
|
|
434
|
+
};
|
|
435
|
+
const handleSubmit = async () => {
|
|
436
|
+
if (!validate() || !client) return;
|
|
437
|
+
setLoading(true);
|
|
438
|
+
setError("");
|
|
439
|
+
try {
|
|
440
|
+
await client.signInWithEmail(email, password);
|
|
441
|
+
if (afterSignInUrl) window.location.assign(afterSignInUrl);
|
|
442
|
+
onSignIn?.();
|
|
443
|
+
} catch (e) {
|
|
444
|
+
setError(e?.message ?? "Sign in failed");
|
|
445
|
+
} finally {
|
|
446
|
+
setLoading(false);
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
const handleOAuth = async (provider) => {
|
|
450
|
+
if (!client) return;
|
|
451
|
+
setOauthLoading(provider);
|
|
452
|
+
setError("");
|
|
453
|
+
try {
|
|
454
|
+
await client.signInWithOAuth(provider);
|
|
455
|
+
} catch (e) {
|
|
456
|
+
setError(e?.message ?? "OAuth sign in failed");
|
|
457
|
+
} finally {
|
|
458
|
+
setOauthLoading(null);
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
const cardStyle = {
|
|
462
|
+
width: "100%",
|
|
463
|
+
maxWidth: 440,
|
|
464
|
+
background: theme.bg,
|
|
465
|
+
borderRadius: `calc(${theme.borderRadius} + 4px)`,
|
|
466
|
+
boxShadow: "0 4px 32px rgba(0,0,0,0.10)",
|
|
467
|
+
padding: "40px 36px 32px",
|
|
468
|
+
boxSizing: "border-box",
|
|
469
|
+
display: "flex",
|
|
470
|
+
flexDirection: "column",
|
|
471
|
+
gap: 20,
|
|
472
|
+
fontFamily: theme.fontFamily
|
|
473
|
+
};
|
|
474
|
+
const logoStyle = {
|
|
475
|
+
display: "flex",
|
|
476
|
+
flexDirection: "column",
|
|
477
|
+
alignItems: "center",
|
|
478
|
+
gap: 10
|
|
479
|
+
};
|
|
480
|
+
const titleStyle = {
|
|
481
|
+
fontSize: 24,
|
|
482
|
+
fontWeight: 700,
|
|
483
|
+
color: theme.text,
|
|
484
|
+
textAlign: "center",
|
|
485
|
+
letterSpacing: "-0.3px"
|
|
486
|
+
};
|
|
487
|
+
const subtitleStyle = {
|
|
488
|
+
fontSize: 14,
|
|
489
|
+
color: theme.textMuted,
|
|
490
|
+
textAlign: "center",
|
|
491
|
+
marginTop: -12
|
|
492
|
+
};
|
|
493
|
+
const errorBoxStyle = {
|
|
494
|
+
padding: "10px 14px",
|
|
495
|
+
borderRadius: theme.borderRadius,
|
|
496
|
+
background: "#fef2f2",
|
|
497
|
+
border: "1px solid #fecaca",
|
|
498
|
+
color: "#dc2626",
|
|
499
|
+
fontSize: 13
|
|
500
|
+
};
|
|
501
|
+
const forgotStyle = {
|
|
502
|
+
textAlign: "right",
|
|
503
|
+
marginTop: -12
|
|
504
|
+
};
|
|
505
|
+
const linkStyle = {
|
|
506
|
+
fontSize: 13,
|
|
507
|
+
color: theme.primaryStart,
|
|
508
|
+
background: "none",
|
|
509
|
+
border: "none",
|
|
510
|
+
cursor: "pointer",
|
|
511
|
+
padding: 0,
|
|
512
|
+
fontFamily: theme.fontFamily,
|
|
513
|
+
textDecoration: "none"
|
|
514
|
+
};
|
|
515
|
+
const footerStyle = {
|
|
516
|
+
textAlign: "center",
|
|
517
|
+
fontSize: 14,
|
|
518
|
+
color: theme.textMuted,
|
|
519
|
+
paddingTop: 4
|
|
520
|
+
};
|
|
521
|
+
const eyeIconPath = showPassword ? "M17.94 17.94A10.07 10.07 0 0112 20c-7 0-11-8-11-8a18.45 18.45 0 015.06-5.94M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19m-6.72-1.07a3 3 0 11-4.24-4.24 M1 1l22 22" : "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z M12 9a3 3 0 100 6 3 3 0 000-6z";
|
|
522
|
+
const showEmailPw = branding.showEmailPassword !== false;
|
|
523
|
+
const showDivider = branding.showDivider !== false && providers.length > 0 && showEmailPw;
|
|
524
|
+
const providersToShow = providers.filter(
|
|
525
|
+
(p) => !(branding.hiddenProviders ?? []).includes(p)
|
|
526
|
+
);
|
|
527
|
+
if (!isLoaded) {
|
|
528
|
+
return /* @__PURE__ */ jsx6("div", { style: { ...cardStyle, alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx6(
|
|
529
|
+
"span",
|
|
530
|
+
{
|
|
531
|
+
style: {
|
|
532
|
+
width: 28,
|
|
533
|
+
height: 28,
|
|
534
|
+
border: `3px solid ${theme.primaryStart}33`,
|
|
535
|
+
borderTopColor: theme.primaryStart,
|
|
536
|
+
borderRadius: "50%",
|
|
537
|
+
display: "inline-block",
|
|
538
|
+
animation: "authon-spin 0.7s linear infinite"
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
) });
|
|
542
|
+
}
|
|
543
|
+
return /* @__PURE__ */ jsxs4("div", { style: cardStyle, children: [
|
|
544
|
+
/* @__PURE__ */ jsxs4("div", { style: logoStyle, children: [
|
|
545
|
+
branding.logoDataUrl && /* @__PURE__ */ jsx6("img", { src: branding.logoDataUrl, alt: branding.brandName ?? "Logo", style: { height: 40, objectFit: "contain" } }),
|
|
546
|
+
/* @__PURE__ */ jsx6("h1", { style: titleStyle, children: "Sign in" }),
|
|
547
|
+
branding.brandName && /* @__PURE__ */ jsxs4("p", { style: subtitleStyle, children: [
|
|
548
|
+
"to ",
|
|
549
|
+
branding.brandName
|
|
550
|
+
] })
|
|
551
|
+
] }),
|
|
552
|
+
error && /* @__PURE__ */ jsx6("div", { style: errorBoxStyle, children: error }),
|
|
553
|
+
providersToShow.length > 0 && /* @__PURE__ */ jsx6("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: providersToShow.length <= 3 ? providersToShow.map((provider) => /* @__PURE__ */ jsx6(
|
|
554
|
+
OAuthButton,
|
|
555
|
+
{
|
|
556
|
+
provider,
|
|
557
|
+
loading: oauthLoading === provider,
|
|
558
|
+
disabled: !!oauthLoading || loading,
|
|
559
|
+
onClick: () => handleOAuth(provider),
|
|
560
|
+
borderRadius: theme.borderRadius
|
|
561
|
+
},
|
|
562
|
+
provider
|
|
563
|
+
)) : /* @__PURE__ */ jsx6("div", { style: { display: "flex", flexWrap: "wrap", gap: 10, justifyContent: "center" }, children: providersToShow.map((provider) => /* @__PURE__ */ jsx6(
|
|
564
|
+
CompactOAuthButton,
|
|
565
|
+
{
|
|
566
|
+
provider,
|
|
567
|
+
loading: oauthLoading === provider,
|
|
568
|
+
disabled: !!oauthLoading || loading,
|
|
569
|
+
onClick: () => handleOAuth(provider),
|
|
570
|
+
borderRadius: theme.borderRadius
|
|
571
|
+
},
|
|
572
|
+
provider
|
|
573
|
+
)) }) }),
|
|
574
|
+
showDivider && /* @__PURE__ */ jsx6(Divider, {}),
|
|
575
|
+
showEmailPw && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
576
|
+
/* @__PURE__ */ jsx6(
|
|
577
|
+
Input,
|
|
578
|
+
{
|
|
579
|
+
label: "Email",
|
|
580
|
+
type: "email",
|
|
581
|
+
placeholder: "you@example.com",
|
|
582
|
+
value: email,
|
|
583
|
+
onChange: setEmail,
|
|
584
|
+
error: fieldErrors.email,
|
|
585
|
+
autoComplete: "email"
|
|
586
|
+
}
|
|
587
|
+
),
|
|
588
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
|
|
589
|
+
/* @__PURE__ */ jsx6(
|
|
590
|
+
Input,
|
|
591
|
+
{
|
|
592
|
+
label: "Password",
|
|
593
|
+
type: showPassword ? "text" : "password",
|
|
594
|
+
placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
|
|
595
|
+
value: password,
|
|
596
|
+
onChange: setPassword,
|
|
597
|
+
error: fieldErrors.password,
|
|
598
|
+
autoComplete: "current-password",
|
|
599
|
+
rightElement: /* @__PURE__ */ jsx6(
|
|
600
|
+
"button",
|
|
601
|
+
{
|
|
602
|
+
type: "button",
|
|
603
|
+
onClick: () => setShowPassword((v) => !v),
|
|
604
|
+
style: { background: "none", border: "none", cursor: "pointer", padding: 0, display: "flex", color: theme.textMuted },
|
|
605
|
+
"aria-label": showPassword ? "Hide password" : "Show password",
|
|
606
|
+
children: /* @__PURE__ */ jsx6("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx6("path", { d: eyeIconPath }) })
|
|
607
|
+
}
|
|
608
|
+
)
|
|
609
|
+
}
|
|
610
|
+
),
|
|
611
|
+
/* @__PURE__ */ jsx6("div", { style: forgotStyle, children: /* @__PURE__ */ jsx6("button", { type: "button", style: linkStyle, children: "Forgot password?" }) })
|
|
612
|
+
] }),
|
|
613
|
+
/* @__PURE__ */ jsx6(
|
|
614
|
+
Button,
|
|
615
|
+
{
|
|
616
|
+
variant: "primary",
|
|
617
|
+
fullWidth: true,
|
|
618
|
+
loading,
|
|
619
|
+
disabled: !!oauthLoading,
|
|
620
|
+
onClick: handleSubmit,
|
|
621
|
+
children: "Sign in"
|
|
622
|
+
}
|
|
623
|
+
)
|
|
624
|
+
] }),
|
|
625
|
+
/* @__PURE__ */ jsxs4("div", { style: footerStyle, children: [
|
|
626
|
+
"Don't have an account?",
|
|
627
|
+
" ",
|
|
628
|
+
/* @__PURE__ */ jsx6(
|
|
629
|
+
"button",
|
|
630
|
+
{
|
|
631
|
+
type: "button",
|
|
632
|
+
style: linkStyle,
|
|
633
|
+
onClick: onNavigateSignUp,
|
|
634
|
+
children: "Sign up"
|
|
635
|
+
}
|
|
636
|
+
)
|
|
637
|
+
] }),
|
|
638
|
+
branding.showSecuredBy !== false && /* @__PURE__ */ jsx6(SecuredByAuthon, { primaryStart: theme.primaryStart, textMuted: theme.textMuted }),
|
|
639
|
+
branding.termsUrl || branding.privacyUrl ? /* @__PURE__ */ jsxs4("div", { style: { textAlign: "center", fontSize: 11, color: theme.textMuted, marginTop: -8 }, children: [
|
|
640
|
+
branding.termsUrl && /* @__PURE__ */ jsx6("a", { href: branding.termsUrl, target: "_blank", rel: "noopener noreferrer", style: { color: theme.textMuted }, children: "Terms" }),
|
|
641
|
+
branding.termsUrl && branding.privacyUrl && " \xB7 ",
|
|
642
|
+
branding.privacyUrl && /* @__PURE__ */ jsx6("a", { href: branding.privacyUrl, target: "_blank", rel: "noopener noreferrer", style: { color: theme.textMuted }, children: "Privacy" })
|
|
643
|
+
] }) : null
|
|
644
|
+
] });
|
|
645
|
+
}
|
|
646
|
+
function OAuthButton({ provider, loading, disabled, onClick, borderRadius }) {
|
|
647
|
+
const [hovered, setHovered] = useState5(false);
|
|
648
|
+
const colors = PROVIDER_COLORS[provider] ?? { bg: "#333", text: "#fff" };
|
|
649
|
+
const name = PROVIDER_DISPLAY_NAMES[provider] ?? provider;
|
|
650
|
+
const config = getProviderButtonConfig(provider);
|
|
651
|
+
const needsBorder = colors.bg.toLowerCase() === "#ffffff";
|
|
652
|
+
const style = {
|
|
653
|
+
display: "flex",
|
|
654
|
+
alignItems: "center",
|
|
655
|
+
gap: 10,
|
|
656
|
+
width: "100%",
|
|
657
|
+
height: 44,
|
|
658
|
+
paddingLeft: 16,
|
|
659
|
+
paddingRight: 16,
|
|
660
|
+
borderRadius,
|
|
661
|
+
background: colors.bg,
|
|
662
|
+
color: colors.text,
|
|
663
|
+
border: needsBorder ? "1.5px solid #dadce0" : "none",
|
|
664
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
665
|
+
fontSize: 15,
|
|
666
|
+
fontWeight: 600,
|
|
667
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
668
|
+
justifyContent: "center",
|
|
669
|
+
opacity: disabled ? 0.6 : hovered ? 0.88 : 1,
|
|
670
|
+
transition: "opacity 0.15s",
|
|
671
|
+
boxSizing: "border-box"
|
|
672
|
+
};
|
|
673
|
+
return /* @__PURE__ */ jsx6(
|
|
674
|
+
"button",
|
|
675
|
+
{
|
|
676
|
+
type: "button",
|
|
677
|
+
style,
|
|
678
|
+
onClick,
|
|
679
|
+
disabled,
|
|
680
|
+
onMouseEnter: () => setHovered(true),
|
|
681
|
+
onMouseLeave: () => setHovered(false),
|
|
682
|
+
"aria-label": `Continue with ${name}`,
|
|
683
|
+
children: loading ? /* @__PURE__ */ jsx6("span", { style: { width: 18, height: 18, border: `2px solid ${colors.text}`, borderTopColor: "transparent", borderRadius: "50%", display: "inline-block", animation: "authon-spin 0.6s linear infinite" } }) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
684
|
+
/* @__PURE__ */ jsx6("span", { style: { display: "flex", alignItems: "center" }, dangerouslySetInnerHTML: { __html: config.iconSvg } }),
|
|
685
|
+
/* @__PURE__ */ jsxs4("span", { children: [
|
|
686
|
+
"Continue with ",
|
|
687
|
+
name
|
|
688
|
+
] })
|
|
689
|
+
] })
|
|
690
|
+
}
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
function CompactOAuthButton({ provider, loading, disabled, onClick, borderRadius }) {
|
|
694
|
+
const [hovered, setHovered] = useState5(false);
|
|
695
|
+
const colors = PROVIDER_COLORS[provider] ?? { bg: "#333", text: "#fff" };
|
|
696
|
+
const name = PROVIDER_DISPLAY_NAMES[provider] ?? provider;
|
|
697
|
+
const config = getProviderButtonConfig(provider);
|
|
698
|
+
const needsBorder = colors.bg.toLowerCase() === "#ffffff";
|
|
699
|
+
const style = {
|
|
700
|
+
display: "flex",
|
|
701
|
+
alignItems: "center",
|
|
702
|
+
justifyContent: "center",
|
|
703
|
+
width: 48,
|
|
704
|
+
height: 48,
|
|
705
|
+
borderRadius,
|
|
706
|
+
background: colors.bg,
|
|
707
|
+
border: needsBorder ? "1.5px solid #dadce0" : "none",
|
|
708
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
709
|
+
opacity: disabled ? 0.6 : hovered ? 0.85 : 1,
|
|
710
|
+
transition: "opacity 0.15s",
|
|
711
|
+
padding: 0
|
|
712
|
+
};
|
|
713
|
+
return /* @__PURE__ */ jsx6(
|
|
714
|
+
"button",
|
|
715
|
+
{
|
|
716
|
+
type: "button",
|
|
717
|
+
style,
|
|
718
|
+
onClick,
|
|
719
|
+
disabled,
|
|
720
|
+
onMouseEnter: () => setHovered(true),
|
|
721
|
+
onMouseLeave: () => setHovered(false),
|
|
722
|
+
"aria-label": `Continue with ${name}`,
|
|
723
|
+
children: loading ? /* @__PURE__ */ jsx6("span", { style: { width: 18, height: 18, border: `2px solid ${colors.text}`, borderTopColor: "transparent", borderRadius: "50%", display: "inline-block", animation: "authon-spin 0.6s linear infinite" } }) : /* @__PURE__ */ jsx6("span", { style: { display: "flex" }, dangerouslySetInnerHTML: { __html: config.iconSvg } })
|
|
724
|
+
}
|
|
725
|
+
);
|
|
726
|
+
}
|
|
727
|
+
function SecuredByAuthon({ primaryStart, textMuted }) {
|
|
728
|
+
return /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", gap: 5, marginTop: -8 }, children: [
|
|
729
|
+
/* @__PURE__ */ jsxs4("svg", { width: "12", height: "14", viewBox: "0 0 12 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
730
|
+
/* @__PURE__ */ jsx6("path", { d: "M6 0L0.5 2.5V6.5C0.5 9.7 2.9 12.7 6 13.5C9.1 12.7 11.5 9.7 11.5 6.5V2.5L6 0Z", fill: primaryStart, opacity: "0.85" }),
|
|
731
|
+
/* @__PURE__ */ jsx6("path", { d: "M4 7L5.5 8.5L8.5 5.5", stroke: "white", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
732
|
+
] }),
|
|
733
|
+
/* @__PURE__ */ jsxs4("span", { style: { fontSize: 11, color: textMuted }, children: [
|
|
734
|
+
"Secured by",
|
|
735
|
+
" ",
|
|
736
|
+
/* @__PURE__ */ jsx6("a", { href: "https://authon.dev", target: "_blank", rel: "noopener noreferrer", style: { color: primaryStart, textDecoration: "none", fontWeight: 600 }, children: "Authon" })
|
|
737
|
+
] })
|
|
738
|
+
] });
|
|
739
|
+
}
|
|
740
|
+
function SignIn({ appearance, afterSignInUrl, onSignIn, onNavigateSignUp }) {
|
|
741
|
+
const { branding, isLoaded } = useBranding();
|
|
742
|
+
const effectiveBranding = isLoaded ? { ...branding, ...appearance?.variables ?? {} } : branding;
|
|
743
|
+
return /* @__PURE__ */ jsxs4(ThemeProvider, { branding: effectiveBranding, overrides: appearance?.variables, style: { display: "flex", justifyContent: "center" }, children: [
|
|
744
|
+
/* @__PURE__ */ jsx6("style", { children: `@keyframes authon-spin { to { transform: rotate(360deg); } }` }),
|
|
745
|
+
/* @__PURE__ */ jsx6(
|
|
746
|
+
SignInCard,
|
|
747
|
+
{
|
|
748
|
+
afterSignInUrl,
|
|
749
|
+
onSignIn,
|
|
750
|
+
onNavigateSignUp
|
|
751
|
+
}
|
|
752
|
+
)
|
|
753
|
+
] });
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// src/components/SignUp.tsx
|
|
757
|
+
import { useState as useState6 } from "react";
|
|
758
|
+
import { PROVIDER_COLORS as PROVIDER_COLORS2, PROVIDER_DISPLAY_NAMES as PROVIDER_DISPLAY_NAMES2 } from "@authon/shared";
|
|
759
|
+
import { getProviderButtonConfig as getProviderButtonConfig2 } from "@authon/js";
|
|
760
|
+
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
761
|
+
function SignUpCard({ afterSignUpUrl, onSignUp, onNavigateSignIn }) {
|
|
762
|
+
const theme = useTheme();
|
|
763
|
+
const { client } = useAuthon();
|
|
764
|
+
const { branding, providers, isLoaded } = useBranding();
|
|
765
|
+
const [displayName, setDisplayName] = useState6("");
|
|
766
|
+
const [email, setEmail] = useState6("");
|
|
767
|
+
const [password, setPassword] = useState6("");
|
|
768
|
+
const [confirmPassword, setConfirmPassword] = useState6("");
|
|
769
|
+
const [showPassword, setShowPassword] = useState6(false);
|
|
770
|
+
const [loading, setLoading] = useState6(false);
|
|
771
|
+
const [oauthLoading, setOauthLoading] = useState6(null);
|
|
772
|
+
const [error, setError] = useState6("");
|
|
773
|
+
const [fieldErrors, setFieldErrors] = useState6({});
|
|
774
|
+
const validate = () => {
|
|
775
|
+
const errs = {};
|
|
776
|
+
if (!email.trim()) errs.email = "Email is required";
|
|
777
|
+
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) errs.email = "Invalid email address";
|
|
778
|
+
if (!password) errs.password = "Password is required";
|
|
779
|
+
else if (password.length < 8) errs.password = "Password must be at least 8 characters";
|
|
780
|
+
if (confirmPassword !== password) errs.confirmPassword = "Passwords do not match";
|
|
781
|
+
setFieldErrors(errs);
|
|
782
|
+
return Object.keys(errs).length === 0;
|
|
783
|
+
};
|
|
784
|
+
const handleSubmit = async () => {
|
|
785
|
+
if (!validate() || !client) return;
|
|
786
|
+
setLoading(true);
|
|
787
|
+
setError("");
|
|
788
|
+
try {
|
|
789
|
+
await client.signUpWithEmail(email, password, displayName ? { displayName } : void 0);
|
|
790
|
+
if (afterSignUpUrl) window.location.assign(afterSignUpUrl);
|
|
791
|
+
onSignUp?.();
|
|
792
|
+
} catch (e) {
|
|
793
|
+
setError(e?.message ?? "Sign up failed");
|
|
794
|
+
} finally {
|
|
795
|
+
setLoading(false);
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
const handleOAuth = async (provider) => {
|
|
799
|
+
if (!client) return;
|
|
800
|
+
setOauthLoading(provider);
|
|
801
|
+
setError("");
|
|
802
|
+
try {
|
|
803
|
+
await client.signInWithOAuth(provider);
|
|
804
|
+
} catch (e) {
|
|
805
|
+
setError(e?.message ?? "OAuth sign in failed");
|
|
806
|
+
} finally {
|
|
807
|
+
setOauthLoading(null);
|
|
103
808
|
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
809
|
+
};
|
|
810
|
+
const cardStyle = {
|
|
811
|
+
width: "100%",
|
|
812
|
+
maxWidth: 440,
|
|
813
|
+
background: theme.bg,
|
|
814
|
+
borderRadius: `calc(${theme.borderRadius} + 4px)`,
|
|
815
|
+
boxShadow: "0 4px 32px rgba(0,0,0,0.10)",
|
|
816
|
+
padding: "40px 36px 32px",
|
|
817
|
+
boxSizing: "border-box",
|
|
818
|
+
display: "flex",
|
|
819
|
+
flexDirection: "column",
|
|
820
|
+
gap: 20,
|
|
821
|
+
fontFamily: theme.fontFamily
|
|
822
|
+
};
|
|
823
|
+
const logoStyle = {
|
|
824
|
+
display: "flex",
|
|
825
|
+
flexDirection: "column",
|
|
826
|
+
alignItems: "center",
|
|
827
|
+
gap: 10
|
|
828
|
+
};
|
|
829
|
+
const titleStyle = {
|
|
830
|
+
fontSize: 24,
|
|
831
|
+
fontWeight: 700,
|
|
832
|
+
color: theme.text,
|
|
833
|
+
textAlign: "center",
|
|
834
|
+
letterSpacing: "-0.3px"
|
|
835
|
+
};
|
|
836
|
+
const subtitleStyle = {
|
|
837
|
+
fontSize: 14,
|
|
838
|
+
color: theme.textMuted,
|
|
839
|
+
textAlign: "center",
|
|
840
|
+
marginTop: -12
|
|
841
|
+
};
|
|
842
|
+
const errorBoxStyle = {
|
|
843
|
+
padding: "10px 14px",
|
|
844
|
+
borderRadius: theme.borderRadius,
|
|
845
|
+
background: "#fef2f2",
|
|
846
|
+
border: "1px solid #fecaca",
|
|
847
|
+
color: "#dc2626",
|
|
848
|
+
fontSize: 13
|
|
849
|
+
};
|
|
850
|
+
const linkStyle = {
|
|
851
|
+
fontSize: 13,
|
|
852
|
+
color: theme.primaryStart,
|
|
853
|
+
background: "none",
|
|
854
|
+
border: "none",
|
|
855
|
+
cursor: "pointer",
|
|
856
|
+
padding: 0,
|
|
857
|
+
fontFamily: theme.fontFamily,
|
|
858
|
+
textDecoration: "none"
|
|
859
|
+
};
|
|
860
|
+
const footerStyle = {
|
|
861
|
+
textAlign: "center",
|
|
862
|
+
fontSize: 14,
|
|
863
|
+
color: theme.textMuted,
|
|
864
|
+
paddingTop: 4
|
|
865
|
+
};
|
|
866
|
+
const showEmailPw = branding.showEmailPassword !== false;
|
|
867
|
+
const showDivider = branding.showDivider !== false && providers.length > 0 && showEmailPw;
|
|
868
|
+
const providersToShow = providers.filter(
|
|
869
|
+
(p) => !(branding.hiddenProviders ?? []).includes(p)
|
|
870
|
+
);
|
|
871
|
+
if (!isLoaded) {
|
|
872
|
+
return /* @__PURE__ */ jsx7("div", { style: { ...cardStyle, alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx7(
|
|
873
|
+
"span",
|
|
874
|
+
{
|
|
875
|
+
style: {
|
|
876
|
+
width: 28,
|
|
877
|
+
height: 28,
|
|
878
|
+
border: `3px solid ${theme.primaryStart}33`,
|
|
879
|
+
borderTopColor: theme.primaryStart,
|
|
880
|
+
borderRadius: "50%",
|
|
881
|
+
display: "inline-block",
|
|
882
|
+
animation: "authon-spin 0.7s linear infinite"
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
) });
|
|
107
886
|
}
|
|
108
|
-
|
|
887
|
+
const eyeIconPath = showPassword ? "M17.94 17.94A10.07 10.07 0 0112 20c-7 0-11-8-11-8a18.45 18.45 0 015.06-5.94M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19m-6.72-1.07a3 3 0 11-4.24-4.24 M1 1l22 22" : "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z M12 9a3 3 0 100 6 3 3 0 000-6z";
|
|
888
|
+
return /* @__PURE__ */ jsxs5("div", { style: cardStyle, children: [
|
|
889
|
+
/* @__PURE__ */ jsxs5("div", { style: logoStyle, children: [
|
|
890
|
+
branding.logoDataUrl && /* @__PURE__ */ jsx7("img", { src: branding.logoDataUrl, alt: branding.brandName ?? "Logo", style: { height: 40, objectFit: "contain" } }),
|
|
891
|
+
/* @__PURE__ */ jsx7("h1", { style: titleStyle, children: "Create account" }),
|
|
892
|
+
branding.brandName && /* @__PURE__ */ jsxs5("p", { style: subtitleStyle, children: [
|
|
893
|
+
"Join ",
|
|
894
|
+
branding.brandName
|
|
895
|
+
] })
|
|
896
|
+
] }),
|
|
897
|
+
error && /* @__PURE__ */ jsx7("div", { style: errorBoxStyle, children: error }),
|
|
898
|
+
providersToShow.length > 0 && /* @__PURE__ */ jsx7("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: providersToShow.length <= 3 ? providersToShow.map((provider) => /* @__PURE__ */ jsx7(
|
|
899
|
+
OAuthButtonFull,
|
|
900
|
+
{
|
|
901
|
+
provider,
|
|
902
|
+
loading: oauthLoading === provider,
|
|
903
|
+
disabled: !!oauthLoading || loading,
|
|
904
|
+
onClick: () => handleOAuth(provider),
|
|
905
|
+
borderRadius: theme.borderRadius
|
|
906
|
+
},
|
|
907
|
+
provider
|
|
908
|
+
)) : /* @__PURE__ */ jsx7("div", { style: { display: "flex", flexWrap: "wrap", gap: 10, justifyContent: "center" }, children: providersToShow.map((provider) => /* @__PURE__ */ jsx7(
|
|
909
|
+
CompactOAuthBtn,
|
|
910
|
+
{
|
|
911
|
+
provider,
|
|
912
|
+
loading: oauthLoading === provider,
|
|
913
|
+
disabled: !!oauthLoading || loading,
|
|
914
|
+
onClick: () => handleOAuth(provider),
|
|
915
|
+
borderRadius: theme.borderRadius
|
|
916
|
+
},
|
|
917
|
+
provider
|
|
918
|
+
)) }) }),
|
|
919
|
+
showDivider && /* @__PURE__ */ jsx7(Divider, {}),
|
|
920
|
+
showEmailPw && /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
921
|
+
/* @__PURE__ */ jsx7(
|
|
922
|
+
Input,
|
|
923
|
+
{
|
|
924
|
+
label: "Display name",
|
|
925
|
+
type: "text",
|
|
926
|
+
placeholder: "Your name",
|
|
927
|
+
value: displayName,
|
|
928
|
+
onChange: setDisplayName,
|
|
929
|
+
error: fieldErrors.displayName,
|
|
930
|
+
autoComplete: "name"
|
|
931
|
+
}
|
|
932
|
+
),
|
|
933
|
+
/* @__PURE__ */ jsx7(
|
|
934
|
+
Input,
|
|
935
|
+
{
|
|
936
|
+
label: "Email",
|
|
937
|
+
type: "email",
|
|
938
|
+
placeholder: "you@example.com",
|
|
939
|
+
value: email,
|
|
940
|
+
onChange: setEmail,
|
|
941
|
+
error: fieldErrors.email,
|
|
942
|
+
autoComplete: "email"
|
|
943
|
+
}
|
|
944
|
+
),
|
|
945
|
+
/* @__PURE__ */ jsx7(
|
|
946
|
+
Input,
|
|
947
|
+
{
|
|
948
|
+
label: "Password",
|
|
949
|
+
type: showPassword ? "text" : "password",
|
|
950
|
+
placeholder: "Minimum 8 characters",
|
|
951
|
+
value: password,
|
|
952
|
+
onChange: setPassword,
|
|
953
|
+
error: fieldErrors.password,
|
|
954
|
+
autoComplete: "new-password",
|
|
955
|
+
rightElement: /* @__PURE__ */ jsx7(
|
|
956
|
+
"button",
|
|
957
|
+
{
|
|
958
|
+
type: "button",
|
|
959
|
+
onClick: () => setShowPassword((v) => !v),
|
|
960
|
+
style: { background: "none", border: "none", cursor: "pointer", padding: 0, display: "flex", color: theme.textMuted },
|
|
961
|
+
"aria-label": showPassword ? "Hide password" : "Show password",
|
|
962
|
+
children: /* @__PURE__ */ jsx7("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: eyeIconPath }) })
|
|
963
|
+
}
|
|
964
|
+
)
|
|
965
|
+
}
|
|
966
|
+
),
|
|
967
|
+
/* @__PURE__ */ jsx7(
|
|
968
|
+
Input,
|
|
969
|
+
{
|
|
970
|
+
label: "Confirm password",
|
|
971
|
+
type: showPassword ? "text" : "password",
|
|
972
|
+
placeholder: "Repeat password",
|
|
973
|
+
value: confirmPassword,
|
|
974
|
+
onChange: setConfirmPassword,
|
|
975
|
+
error: fieldErrors.confirmPassword,
|
|
976
|
+
autoComplete: "new-password"
|
|
977
|
+
}
|
|
978
|
+
),
|
|
979
|
+
/* @__PURE__ */ jsx7(
|
|
980
|
+
Button,
|
|
981
|
+
{
|
|
982
|
+
variant: "primary",
|
|
983
|
+
fullWidth: true,
|
|
984
|
+
loading,
|
|
985
|
+
disabled: !!oauthLoading,
|
|
986
|
+
onClick: handleSubmit,
|
|
987
|
+
children: "Create account"
|
|
988
|
+
}
|
|
989
|
+
)
|
|
990
|
+
] }),
|
|
991
|
+
/* @__PURE__ */ jsxs5("div", { style: footerStyle, children: [
|
|
992
|
+
"Already have an account?",
|
|
993
|
+
" ",
|
|
994
|
+
/* @__PURE__ */ jsx7("button", { type: "button", style: linkStyle, onClick: onNavigateSignIn, children: "Sign in" })
|
|
995
|
+
] }),
|
|
996
|
+
branding.showSecuredBy !== false && /* @__PURE__ */ jsx7(SecuredByAuthon2, { primaryStart: theme.primaryStart, textMuted: theme.textMuted }),
|
|
997
|
+
branding.termsUrl || branding.privacyUrl ? /* @__PURE__ */ jsxs5("div", { style: { textAlign: "center", fontSize: 11, color: theme.textMuted, marginTop: -8 }, children: [
|
|
998
|
+
"By creating an account you agree to our",
|
|
999
|
+
" ",
|
|
1000
|
+
branding.termsUrl && /* @__PURE__ */ jsx7("a", { href: branding.termsUrl, target: "_blank", rel: "noopener noreferrer", style: { color: theme.primaryStart }, children: "Terms" }),
|
|
1001
|
+
branding.termsUrl && branding.privacyUrl && " and ",
|
|
1002
|
+
branding.privacyUrl && /* @__PURE__ */ jsx7("a", { href: branding.privacyUrl, target: "_blank", rel: "noopener noreferrer", style: { color: theme.primaryStart }, children: "Privacy Policy" })
|
|
1003
|
+
] }) : null
|
|
1004
|
+
] });
|
|
1005
|
+
}
|
|
1006
|
+
function OAuthButtonFull({ provider, loading, disabled, onClick, borderRadius }) {
|
|
1007
|
+
const [hovered, setHovered] = useState6(false);
|
|
1008
|
+
const colors = PROVIDER_COLORS2[provider] ?? { bg: "#333", text: "#fff" };
|
|
1009
|
+
const name = PROVIDER_DISPLAY_NAMES2[provider] ?? provider;
|
|
1010
|
+
const config = getProviderButtonConfig2(provider);
|
|
1011
|
+
const needsBorder = colors.bg.toLowerCase() === "#ffffff";
|
|
1012
|
+
const style = {
|
|
1013
|
+
display: "flex",
|
|
1014
|
+
alignItems: "center",
|
|
1015
|
+
gap: 10,
|
|
1016
|
+
width: "100%",
|
|
1017
|
+
height: 44,
|
|
1018
|
+
paddingLeft: 16,
|
|
1019
|
+
paddingRight: 16,
|
|
1020
|
+
borderRadius,
|
|
1021
|
+
background: colors.bg,
|
|
1022
|
+
color: colors.text,
|
|
1023
|
+
border: needsBorder ? "1.5px solid #dadce0" : "none",
|
|
1024
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
1025
|
+
fontSize: 15,
|
|
1026
|
+
fontWeight: 600,
|
|
1027
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
1028
|
+
justifyContent: "center",
|
|
1029
|
+
opacity: disabled ? 0.6 : hovered ? 0.88 : 1,
|
|
1030
|
+
transition: "opacity 0.15s",
|
|
1031
|
+
boxSizing: "border-box"
|
|
1032
|
+
};
|
|
1033
|
+
return /* @__PURE__ */ jsx7(
|
|
1034
|
+
"button",
|
|
1035
|
+
{
|
|
1036
|
+
type: "button",
|
|
1037
|
+
style,
|
|
1038
|
+
onClick,
|
|
1039
|
+
disabled,
|
|
1040
|
+
onMouseEnter: () => setHovered(true),
|
|
1041
|
+
onMouseLeave: () => setHovered(false),
|
|
1042
|
+
"aria-label": `Continue with ${name}`,
|
|
1043
|
+
children: loading ? /* @__PURE__ */ jsx7("span", { style: { width: 18, height: 18, border: `2px solid ${colors.text}`, borderTopColor: "transparent", borderRadius: "50%", display: "inline-block", animation: "authon-spin 0.6s linear infinite" } }) : /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
1044
|
+
/* @__PURE__ */ jsx7("span", { style: { display: "flex", alignItems: "center" }, dangerouslySetInnerHTML: { __html: config.iconSvg } }),
|
|
1045
|
+
/* @__PURE__ */ jsxs5("span", { children: [
|
|
1046
|
+
"Continue with ",
|
|
1047
|
+
name
|
|
1048
|
+
] })
|
|
1049
|
+
] })
|
|
1050
|
+
}
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
function CompactOAuthBtn({ provider, loading, disabled, onClick, borderRadius }) {
|
|
1054
|
+
const [hovered, setHovered] = useState6(false);
|
|
1055
|
+
const colors = PROVIDER_COLORS2[provider] ?? { bg: "#333", text: "#fff" };
|
|
1056
|
+
const name = PROVIDER_DISPLAY_NAMES2[provider] ?? provider;
|
|
1057
|
+
const config = getProviderButtonConfig2(provider);
|
|
1058
|
+
const needsBorder = colors.bg.toLowerCase() === "#ffffff";
|
|
1059
|
+
const style = {
|
|
1060
|
+
display: "flex",
|
|
1061
|
+
alignItems: "center",
|
|
1062
|
+
justifyContent: "center",
|
|
1063
|
+
width: 48,
|
|
1064
|
+
height: 48,
|
|
1065
|
+
borderRadius,
|
|
1066
|
+
background: colors.bg,
|
|
1067
|
+
border: needsBorder ? "1.5px solid #dadce0" : "none",
|
|
1068
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
1069
|
+
opacity: disabled ? 0.6 : hovered ? 0.85 : 1,
|
|
1070
|
+
transition: "opacity 0.15s",
|
|
1071
|
+
padding: 0
|
|
1072
|
+
};
|
|
1073
|
+
return /* @__PURE__ */ jsx7(
|
|
1074
|
+
"button",
|
|
1075
|
+
{
|
|
1076
|
+
type: "button",
|
|
1077
|
+
style,
|
|
1078
|
+
onClick,
|
|
1079
|
+
disabled,
|
|
1080
|
+
onMouseEnter: () => setHovered(true),
|
|
1081
|
+
onMouseLeave: () => setHovered(false),
|
|
1082
|
+
"aria-label": `Continue with ${name}`,
|
|
1083
|
+
children: loading ? /* @__PURE__ */ jsx7("span", { style: { width: 18, height: 18, border: `2px solid ${colors.text}`, borderTopColor: "transparent", borderRadius: "50%", display: "inline-block", animation: "authon-spin 0.6s linear infinite" } }) : /* @__PURE__ */ jsx7("span", { style: { display: "flex" }, dangerouslySetInnerHTML: { __html: config.iconSvg } })
|
|
1084
|
+
}
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
function SecuredByAuthon2({ primaryStart, textMuted }) {
|
|
1088
|
+
return /* @__PURE__ */ jsxs5("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", gap: 5, marginTop: -8 }, children: [
|
|
1089
|
+
/* @__PURE__ */ jsxs5("svg", { width: "12", height: "14", viewBox: "0 0 12 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
1090
|
+
/* @__PURE__ */ jsx7("path", { d: "M6 0L0.5 2.5V6.5C0.5 9.7 2.9 12.7 6 13.5C9.1 12.7 11.5 9.7 11.5 6.5V2.5L6 0Z", fill: primaryStart, opacity: "0.85" }),
|
|
1091
|
+
/* @__PURE__ */ jsx7("path", { d: "M4 7L5.5 8.5L8.5 5.5", stroke: "white", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
1092
|
+
] }),
|
|
1093
|
+
/* @__PURE__ */ jsxs5("span", { style: { fontSize: 11, color: textMuted }, children: [
|
|
1094
|
+
"Secured by",
|
|
1095
|
+
" ",
|
|
1096
|
+
/* @__PURE__ */ jsx7("a", { href: "https://authon.dev", target: "_blank", rel: "noopener noreferrer", style: { color: primaryStart, textDecoration: "none", fontWeight: 600 }, children: "Authon" })
|
|
1097
|
+
] })
|
|
1098
|
+
] });
|
|
1099
|
+
}
|
|
1100
|
+
function SignUp({ appearance, afterSignUpUrl, onSignUp, onNavigateSignIn }) {
|
|
1101
|
+
const { branding, isLoaded } = useBranding();
|
|
1102
|
+
const effectiveBranding = isLoaded ? { ...branding, ...appearance?.variables ?? {} } : branding;
|
|
1103
|
+
return /* @__PURE__ */ jsxs5(ThemeProvider, { branding: effectiveBranding, overrides: appearance?.variables, style: { display: "flex", justifyContent: "center" }, children: [
|
|
1104
|
+
/* @__PURE__ */ jsx7("style", { children: `@keyframes authon-spin { to { transform: rotate(360deg); } }` }),
|
|
1105
|
+
/* @__PURE__ */ jsx7(
|
|
1106
|
+
SignUpCard,
|
|
1107
|
+
{
|
|
1108
|
+
afterSignUpUrl,
|
|
1109
|
+
onSignUp,
|
|
1110
|
+
onNavigateSignIn
|
|
1111
|
+
}
|
|
1112
|
+
)
|
|
1113
|
+
] });
|
|
109
1114
|
}
|
|
110
1115
|
|
|
111
|
-
// src/UserButton.tsx
|
|
112
|
-
import { useCallback as
|
|
113
|
-
import { jsx as
|
|
114
|
-
function
|
|
115
|
-
|
|
116
|
-
|
|
1116
|
+
// src/components/UserButton.tsx
|
|
1117
|
+
import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState7 } from "react";
|
|
1118
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1119
|
+
function UserAvatar({
|
|
1120
|
+
avatarUrl,
|
|
1121
|
+
displayName,
|
|
1122
|
+
email,
|
|
1123
|
+
size,
|
|
1124
|
+
primaryStart,
|
|
1125
|
+
primaryEnd
|
|
1126
|
+
}) {
|
|
1127
|
+
const initials = displayName ? displayName.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2) : (email?.[0] ?? "?").toUpperCase();
|
|
1128
|
+
const avatarStyle = {
|
|
1129
|
+
width: size,
|
|
1130
|
+
height: size,
|
|
1131
|
+
borderRadius: "50%",
|
|
1132
|
+
overflow: "hidden",
|
|
1133
|
+
display: "flex",
|
|
1134
|
+
alignItems: "center",
|
|
1135
|
+
justifyContent: "center",
|
|
1136
|
+
background: avatarUrl ? "transparent" : `linear-gradient(135deg, ${primaryStart}, ${primaryEnd})`,
|
|
1137
|
+
color: "#fff",
|
|
1138
|
+
fontSize: size * 0.38,
|
|
1139
|
+
fontWeight: 700,
|
|
1140
|
+
userSelect: "none"
|
|
1141
|
+
};
|
|
1142
|
+
return /* @__PURE__ */ jsx8("div", { style: avatarStyle, children: avatarUrl ? /* @__PURE__ */ jsx8("img", { src: avatarUrl, alt: displayName ?? "avatar", style: { width: "100%", height: "100%", objectFit: "cover" } }) : initials });
|
|
1143
|
+
}
|
|
1144
|
+
function UserButtonInner({ afterSignOutUrl, userProfileUrl }) {
|
|
1145
|
+
const theme = useTheme();
|
|
1146
|
+
const { user, signOut, openSignIn, isSignedIn, activeOrganization, client } = useAuthon();
|
|
1147
|
+
const [open, setOpen] = useState7(false);
|
|
117
1148
|
const dropdownRef = useRef3(null);
|
|
118
|
-
const handleClickOutside =
|
|
1149
|
+
const handleClickOutside = useCallback3((e) => {
|
|
119
1150
|
if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
|
|
120
1151
|
setOpen(false);
|
|
121
1152
|
}
|
|
122
1153
|
}, []);
|
|
123
|
-
|
|
124
|
-
if (open)
|
|
125
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
126
|
-
}
|
|
1154
|
+
useEffect3(() => {
|
|
1155
|
+
if (open) document.addEventListener("mousedown", handleClickOutside);
|
|
127
1156
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
128
1157
|
}, [open, handleClickOutside]);
|
|
129
1158
|
if (!isSignedIn) {
|
|
130
|
-
return /* @__PURE__ */
|
|
1159
|
+
return /* @__PURE__ */ jsx8(
|
|
131
1160
|
"button",
|
|
132
1161
|
{
|
|
1162
|
+
type: "button",
|
|
133
1163
|
onClick: () => openSignIn(),
|
|
134
1164
|
style: {
|
|
135
|
-
padding: "8px
|
|
136
|
-
borderRadius:
|
|
1165
|
+
padding: "8px 18px",
|
|
1166
|
+
borderRadius: theme.borderRadius,
|
|
137
1167
|
border: "none",
|
|
138
|
-
background:
|
|
1168
|
+
background: `linear-gradient(135deg, ${theme.primaryStart}, ${theme.primaryEnd})`,
|
|
139
1169
|
color: "#fff",
|
|
140
1170
|
cursor: "pointer",
|
|
141
|
-
fontSize:
|
|
142
|
-
fontWeight: 600
|
|
1171
|
+
fontSize: 14,
|
|
1172
|
+
fontWeight: 600,
|
|
1173
|
+
fontFamily: theme.fontFamily
|
|
143
1174
|
},
|
|
144
1175
|
children: "Sign In"
|
|
145
1176
|
}
|
|
146
1177
|
);
|
|
147
1178
|
}
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
1179
|
+
const triggerStyle = {
|
|
1180
|
+
width: 38,
|
|
1181
|
+
height: 38,
|
|
1182
|
+
borderRadius: "50%",
|
|
1183
|
+
border: `2px solid ${theme.primaryStart}`,
|
|
1184
|
+
background: "none",
|
|
1185
|
+
cursor: "pointer",
|
|
1186
|
+
padding: 1,
|
|
1187
|
+
overflow: "hidden",
|
|
1188
|
+
display: "flex",
|
|
1189
|
+
alignItems: "center",
|
|
1190
|
+
justifyContent: "center"
|
|
1191
|
+
};
|
|
1192
|
+
const dropdownStyle = {
|
|
1193
|
+
position: "absolute",
|
|
1194
|
+
right: 0,
|
|
1195
|
+
top: 46,
|
|
1196
|
+
minWidth: 240,
|
|
1197
|
+
background: theme.bg,
|
|
1198
|
+
border: `1px solid ${theme.border}`,
|
|
1199
|
+
borderRadius: theme.borderRadius,
|
|
1200
|
+
boxShadow: "0 8px 32px rgba(0,0,0,0.13)",
|
|
1201
|
+
zIndex: 9999,
|
|
1202
|
+
overflow: "hidden",
|
|
1203
|
+
fontFamily: theme.fontFamily
|
|
1204
|
+
};
|
|
1205
|
+
const headerStyle = {
|
|
1206
|
+
padding: "14px 16px",
|
|
1207
|
+
borderBottom: `1px solid ${theme.border}`,
|
|
1208
|
+
display: "flex",
|
|
1209
|
+
alignItems: "center",
|
|
1210
|
+
gap: 12
|
|
1211
|
+
};
|
|
1212
|
+
const menuItemStyle = (danger) => ({
|
|
1213
|
+
display: "flex",
|
|
1214
|
+
alignItems: "center",
|
|
1215
|
+
gap: 10,
|
|
1216
|
+
width: "100%",
|
|
1217
|
+
padding: "10px 16px",
|
|
1218
|
+
textAlign: "left",
|
|
1219
|
+
background: "none",
|
|
1220
|
+
border: "none",
|
|
1221
|
+
cursor: "pointer",
|
|
1222
|
+
fontSize: 14,
|
|
1223
|
+
color: danger ? "#ef4444" : theme.text,
|
|
1224
|
+
fontWeight: 500,
|
|
1225
|
+
fontFamily: theme.fontFamily,
|
|
1226
|
+
boxSizing: "border-box"
|
|
1227
|
+
});
|
|
1228
|
+
return /* @__PURE__ */ jsxs6("div", { ref: dropdownRef, style: { position: "relative", display: "inline-block" }, children: [
|
|
1229
|
+
/* @__PURE__ */ jsx8("button", { type: "button", style: triggerStyle, onClick: () => setOpen((v) => !v), "aria-label": "User menu", children: /* @__PURE__ */ jsx8(
|
|
1230
|
+
UserAvatar,
|
|
152
1231
|
{
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
justifyContent: "center",
|
|
166
|
-
color: "#fff",
|
|
167
|
-
fontSize: "13px",
|
|
168
|
-
fontWeight: 700
|
|
169
|
-
},
|
|
170
|
-
children: user?.avatarUrl ? /* @__PURE__ */ jsx4(
|
|
171
|
-
"img",
|
|
1232
|
+
avatarUrl: user?.avatarUrl,
|
|
1233
|
+
displayName: user?.displayName,
|
|
1234
|
+
email: user?.email,
|
|
1235
|
+
size: 32,
|
|
1236
|
+
primaryStart: theme.primaryStart,
|
|
1237
|
+
primaryEnd: theme.primaryEnd
|
|
1238
|
+
}
|
|
1239
|
+
) }),
|
|
1240
|
+
open && /* @__PURE__ */ jsxs6("div", { style: dropdownStyle, children: [
|
|
1241
|
+
/* @__PURE__ */ jsxs6("div", { style: headerStyle, children: [
|
|
1242
|
+
/* @__PURE__ */ jsx8(
|
|
1243
|
+
UserAvatar,
|
|
172
1244
|
{
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
1245
|
+
avatarUrl: user?.avatarUrl,
|
|
1246
|
+
displayName: user?.displayName,
|
|
1247
|
+
email: user?.email,
|
|
1248
|
+
size: 40,
|
|
1249
|
+
primaryStart: theme.primaryStart,
|
|
1250
|
+
primaryEnd: theme.primaryEnd
|
|
176
1251
|
}
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
color: "#ef4444",
|
|
226
|
-
fontWeight: 500
|
|
227
|
-
},
|
|
228
|
-
onMouseEnter: (e) => {
|
|
229
|
-
e.currentTarget.style.background = "#fef2f2";
|
|
230
|
-
},
|
|
231
|
-
onMouseLeave: (e) => {
|
|
232
|
-
e.currentTarget.style.background = "none";
|
|
233
|
-
},
|
|
234
|
-
children: "Sign out"
|
|
235
|
-
}
|
|
236
|
-
)
|
|
237
|
-
]
|
|
238
|
-
}
|
|
239
|
-
)
|
|
1252
|
+
),
|
|
1253
|
+
/* @__PURE__ */ jsxs6("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
1254
|
+
user?.displayName && /* @__PURE__ */ jsx8("div", { style: { fontSize: 14, fontWeight: 600, color: theme.text, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: user.displayName }),
|
|
1255
|
+
user?.email && /* @__PURE__ */ jsx8("div", { style: { fontSize: 12, color: theme.textMuted, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", marginTop: 1 }, children: user.email })
|
|
1256
|
+
] })
|
|
1257
|
+
] }),
|
|
1258
|
+
activeOrganization && /* @__PURE__ */ jsx8(
|
|
1259
|
+
OrgSwitcherRow,
|
|
1260
|
+
{
|
|
1261
|
+
org: activeOrganization,
|
|
1262
|
+
primaryStart: theme.primaryStart,
|
|
1263
|
+
theme,
|
|
1264
|
+
client
|
|
1265
|
+
}
|
|
1266
|
+
),
|
|
1267
|
+
userProfileUrl && /* @__PURE__ */ jsx8(
|
|
1268
|
+
MenuButton,
|
|
1269
|
+
{
|
|
1270
|
+
style: menuItemStyle(),
|
|
1271
|
+
onClick: () => {
|
|
1272
|
+
setOpen(false);
|
|
1273
|
+
window.location.assign(userProfileUrl);
|
|
1274
|
+
},
|
|
1275
|
+
icon: /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1276
|
+
/* @__PURE__ */ jsx8("path", { d: "M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2" }),
|
|
1277
|
+
/* @__PURE__ */ jsx8("circle", { cx: "12", cy: "7", r: "4" })
|
|
1278
|
+
] }),
|
|
1279
|
+
children: "Manage account"
|
|
1280
|
+
}
|
|
1281
|
+
),
|
|
1282
|
+
/* @__PURE__ */ jsx8("div", { style: { borderTop: `1px solid ${theme.border}` }, children: /* @__PURE__ */ jsx8(
|
|
1283
|
+
MenuButton,
|
|
1284
|
+
{
|
|
1285
|
+
style: menuItemStyle(true),
|
|
1286
|
+
onClick: async () => {
|
|
1287
|
+
setOpen(false);
|
|
1288
|
+
await signOut();
|
|
1289
|
+
if (afterSignOutUrl) window.location.assign(afterSignOutUrl);
|
|
1290
|
+
},
|
|
1291
|
+
icon: /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1292
|
+
/* @__PURE__ */ jsx8("path", { d: "M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4" }),
|
|
1293
|
+
/* @__PURE__ */ jsx8("polyline", { points: "16 17 21 12 16 7" }),
|
|
1294
|
+
/* @__PURE__ */ jsx8("line", { x1: "21", y1: "12", x2: "9", y2: "12" })
|
|
1295
|
+
] }),
|
|
1296
|
+
children: "Sign out"
|
|
1297
|
+
}
|
|
1298
|
+
) })
|
|
1299
|
+
] })
|
|
240
1300
|
] });
|
|
241
1301
|
}
|
|
1302
|
+
function MenuButton({
|
|
1303
|
+
style: baseStyle2,
|
|
1304
|
+
onClick,
|
|
1305
|
+
icon,
|
|
1306
|
+
children
|
|
1307
|
+
}) {
|
|
1308
|
+
const [hovered, setHovered] = useState7(false);
|
|
1309
|
+
return /* @__PURE__ */ jsxs6(
|
|
1310
|
+
"button",
|
|
1311
|
+
{
|
|
1312
|
+
type: "button",
|
|
1313
|
+
style: {
|
|
1314
|
+
...baseStyle2,
|
|
1315
|
+
background: hovered ? baseStyle2.color === "#ef4444" ? "#fef2f2" : "#f9fafb" : "none"
|
|
1316
|
+
},
|
|
1317
|
+
onClick,
|
|
1318
|
+
onMouseEnter: () => setHovered(true),
|
|
1319
|
+
onMouseLeave: () => setHovered(false),
|
|
1320
|
+
children: [
|
|
1321
|
+
icon,
|
|
1322
|
+
children
|
|
1323
|
+
]
|
|
1324
|
+
}
|
|
1325
|
+
);
|
|
1326
|
+
}
|
|
1327
|
+
function OrgSwitcherRow({
|
|
1328
|
+
org,
|
|
1329
|
+
primaryStart,
|
|
1330
|
+
theme,
|
|
1331
|
+
client
|
|
1332
|
+
}) {
|
|
1333
|
+
return /* @__PURE__ */ jsxs6(
|
|
1334
|
+
"div",
|
|
1335
|
+
{
|
|
1336
|
+
style: {
|
|
1337
|
+
padding: "8px 16px",
|
|
1338
|
+
borderBottom: `1px solid ${theme.border}`,
|
|
1339
|
+
display: "flex",
|
|
1340
|
+
alignItems: "center",
|
|
1341
|
+
gap: 10
|
|
1342
|
+
},
|
|
1343
|
+
children: [
|
|
1344
|
+
org.logoUrl ? /* @__PURE__ */ jsx8("img", { src: org.logoUrl, alt: org.name, style: { width: 24, height: 24, borderRadius: 6, objectFit: "cover" } }) : /* @__PURE__ */ jsx8(
|
|
1345
|
+
"div",
|
|
1346
|
+
{
|
|
1347
|
+
style: {
|
|
1348
|
+
width: 24,
|
|
1349
|
+
height: 24,
|
|
1350
|
+
borderRadius: 6,
|
|
1351
|
+
background: `${primaryStart}22`,
|
|
1352
|
+
color: primaryStart,
|
|
1353
|
+
display: "flex",
|
|
1354
|
+
alignItems: "center",
|
|
1355
|
+
justifyContent: "center",
|
|
1356
|
+
fontSize: 11,
|
|
1357
|
+
fontWeight: 700
|
|
1358
|
+
},
|
|
1359
|
+
children: org.name[0]?.toUpperCase()
|
|
1360
|
+
}
|
|
1361
|
+
),
|
|
1362
|
+
/* @__PURE__ */ jsxs6("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
1363
|
+
/* @__PURE__ */ jsx8("div", { style: { fontSize: 12, color: theme.textMuted }, children: "Organization" }),
|
|
1364
|
+
/* @__PURE__ */ jsx8("div", { style: { fontSize: 13, fontWeight: 600, color: theme.text, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: org.name })
|
|
1365
|
+
] })
|
|
1366
|
+
]
|
|
1367
|
+
}
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1370
|
+
function UserButton({ appearance, afterSignOutUrl, userProfileUrl }) {
|
|
1371
|
+
const { branding } = useBranding();
|
|
1372
|
+
const effectiveBranding = { ...branding, ...appearance?.variables ?? {} };
|
|
1373
|
+
return /* @__PURE__ */ jsx8(ThemeProvider, { branding: effectiveBranding, style: { display: "inline-block" }, children: /* @__PURE__ */ jsx8(UserButtonInner, { afterSignOutUrl, userProfileUrl }) });
|
|
1374
|
+
}
|
|
242
1375
|
|
|
243
1376
|
// src/SignedIn.tsx
|
|
244
|
-
import { Fragment, jsx as
|
|
1377
|
+
import { Fragment as Fragment4, jsx as jsx9 } from "react/jsx-runtime";
|
|
245
1378
|
function SignedIn({ children }) {
|
|
246
1379
|
const { isSignedIn, isLoading } = useAuthon();
|
|
247
1380
|
if (isLoading || !isSignedIn) return null;
|
|
248
|
-
return /* @__PURE__ */
|
|
1381
|
+
return /* @__PURE__ */ jsx9(Fragment4, { children });
|
|
249
1382
|
}
|
|
250
1383
|
|
|
251
1384
|
// src/SignedOut.tsx
|
|
252
|
-
import { Fragment as
|
|
1385
|
+
import { Fragment as Fragment5, jsx as jsx10 } from "react/jsx-runtime";
|
|
253
1386
|
function SignedOut({ children }) {
|
|
254
1387
|
const { isSignedIn, isLoading } = useAuthon();
|
|
255
1388
|
if (isLoading || isSignedIn) return null;
|
|
256
|
-
return /* @__PURE__ */
|
|
1389
|
+
return /* @__PURE__ */ jsx10(Fragment5, { children });
|
|
257
1390
|
}
|
|
258
1391
|
|
|
259
1392
|
// src/Protect.tsx
|
|
260
|
-
import { Fragment as
|
|
1393
|
+
import { Fragment as Fragment6, jsx as jsx11 } from "react/jsx-runtime";
|
|
261
1394
|
function Protect({ children, fallback = null, condition }) {
|
|
262
1395
|
const { isSignedIn, isLoading, user } = useAuthon();
|
|
263
1396
|
if (isLoading) return null;
|
|
264
|
-
if (!isSignedIn || !user) return /* @__PURE__ */
|
|
265
|
-
if (condition && !condition(user)) return /* @__PURE__ */
|
|
266
|
-
return /* @__PURE__ */
|
|
1397
|
+
if (!isSignedIn || !user) return /* @__PURE__ */ jsx11(Fragment6, { children: fallback });
|
|
1398
|
+
if (condition && !condition(user)) return /* @__PURE__ */ jsx11(Fragment6, { children: fallback });
|
|
1399
|
+
return /* @__PURE__ */ jsx11(Fragment6, { children });
|
|
267
1400
|
}
|
|
268
1401
|
|
|
269
1402
|
// src/SocialButton.tsx
|
|
270
|
-
import { PROVIDER_COLORS, PROVIDER_DISPLAY_NAMES } from "@authon/shared";
|
|
271
|
-
import { getProviderButtonConfig } from "@authon/js";
|
|
272
|
-
import { Fragment as
|
|
1403
|
+
import { PROVIDER_COLORS as PROVIDER_COLORS3, PROVIDER_DISPLAY_NAMES as PROVIDER_DISPLAY_NAMES3 } from "@authon/shared";
|
|
1404
|
+
import { getProviderButtonConfig as getProviderButtonConfig3 } from "@authon/js";
|
|
1405
|
+
import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
273
1406
|
var baseStyle = {
|
|
274
1407
|
display: "flex",
|
|
275
1408
|
alignItems: "center",
|
|
@@ -306,16 +1439,16 @@ function SocialButton({
|
|
|
306
1439
|
height = 48,
|
|
307
1440
|
size = 48
|
|
308
1441
|
}) {
|
|
309
|
-
const colors =
|
|
310
|
-
const displayName =
|
|
1442
|
+
const colors = PROVIDER_COLORS3[provider] || { bg: "#333", text: "#fff" };
|
|
1443
|
+
const displayName = PROVIDER_DISPLAY_NAMES3[provider] || provider;
|
|
311
1444
|
const buttonLabel = label ?? `Continue with ${displayName}`;
|
|
312
1445
|
const needsBorder = colors.bg.toLowerCase() === "#ffffff";
|
|
313
1446
|
const resolvedIconSize = iconSize ?? (compact ? 24 : 20);
|
|
314
|
-
const config =
|
|
1447
|
+
const config = getProviderButtonConfig3(provider);
|
|
315
1448
|
const iconSvg = config.iconSvg.replace(/width="\d+"/, `width="${resolvedIconSize}"`).replace(/height="\d+"/, `height="${resolvedIconSize}"`);
|
|
316
1449
|
const borderProps = needsBorder ? { border: "1px solid #dadce0" } : {};
|
|
317
1450
|
if (compact) {
|
|
318
|
-
return /* @__PURE__ */
|
|
1451
|
+
return /* @__PURE__ */ jsx12(
|
|
319
1452
|
"button",
|
|
320
1453
|
{
|
|
321
1454
|
className,
|
|
@@ -331,7 +1464,7 @@ function SocialButton({
|
|
|
331
1464
|
onClick: () => onClick(provider),
|
|
332
1465
|
disabled: disabled || loading,
|
|
333
1466
|
"aria-label": `Sign in with ${displayName}`,
|
|
334
|
-
children: loading ? /* @__PURE__ */
|
|
1467
|
+
children: loading ? /* @__PURE__ */ jsx12(
|
|
335
1468
|
"span",
|
|
336
1469
|
{
|
|
337
1470
|
style: {
|
|
@@ -344,7 +1477,7 @@ function SocialButton({
|
|
|
344
1477
|
animation: "authon-spin 0.6s linear infinite"
|
|
345
1478
|
}
|
|
346
1479
|
}
|
|
347
|
-
) : /* @__PURE__ */
|
|
1480
|
+
) : /* @__PURE__ */ jsx12(
|
|
348
1481
|
"span",
|
|
349
1482
|
{
|
|
350
1483
|
style: { display: "flex", alignItems: "center", flexShrink: 0 },
|
|
@@ -354,7 +1487,7 @@ function SocialButton({
|
|
|
354
1487
|
}
|
|
355
1488
|
);
|
|
356
1489
|
}
|
|
357
|
-
return /* @__PURE__ */
|
|
1490
|
+
return /* @__PURE__ */ jsx12(
|
|
358
1491
|
"button",
|
|
359
1492
|
{
|
|
360
1493
|
className,
|
|
@@ -370,7 +1503,7 @@ function SocialButton({
|
|
|
370
1503
|
onClick: () => onClick(provider),
|
|
371
1504
|
disabled: disabled || loading,
|
|
372
1505
|
"aria-label": `Sign in with ${displayName}`,
|
|
373
|
-
children: loading ? /* @__PURE__ */
|
|
1506
|
+
children: loading ? /* @__PURE__ */ jsx12(
|
|
374
1507
|
"span",
|
|
375
1508
|
{
|
|
376
1509
|
style: {
|
|
@@ -383,23 +1516,23 @@ function SocialButton({
|
|
|
383
1516
|
animation: "authon-spin 0.6s linear infinite"
|
|
384
1517
|
}
|
|
385
1518
|
}
|
|
386
|
-
) : /* @__PURE__ */
|
|
387
|
-
/* @__PURE__ */
|
|
1519
|
+
) : /* @__PURE__ */ jsxs7(Fragment7, { children: [
|
|
1520
|
+
/* @__PURE__ */ jsx12(
|
|
388
1521
|
"span",
|
|
389
1522
|
{
|
|
390
1523
|
style: { display: "flex", alignItems: "center", flexShrink: 0 },
|
|
391
1524
|
dangerouslySetInnerHTML: { __html: iconSvg }
|
|
392
1525
|
}
|
|
393
1526
|
),
|
|
394
|
-
/* @__PURE__ */
|
|
1527
|
+
/* @__PURE__ */ jsx12("span", { style: { fontSize: 15, fontWeight: 600, whiteSpace: "nowrap" }, children: buttonLabel })
|
|
395
1528
|
] })
|
|
396
1529
|
}
|
|
397
1530
|
);
|
|
398
1531
|
}
|
|
399
1532
|
|
|
400
1533
|
// src/SocialButtons.tsx
|
|
401
|
-
import { useState as
|
|
402
|
-
import { jsx as
|
|
1534
|
+
import { useState as useState8, useEffect as useEffect4 } from "react";
|
|
1535
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
403
1536
|
function SocialButtons({
|
|
404
1537
|
onSuccess,
|
|
405
1538
|
onError,
|
|
@@ -411,9 +1544,9 @@ function SocialButtons({
|
|
|
411
1544
|
buttonProps
|
|
412
1545
|
}) {
|
|
413
1546
|
const { client } = useAuthon();
|
|
414
|
-
const [providers, setProviders] =
|
|
415
|
-
const [loadingProvider, setLoadingProvider] =
|
|
416
|
-
|
|
1547
|
+
const [providers, setProviders] = useState8([]);
|
|
1548
|
+
const [loadingProvider, setLoadingProvider] = useState8(null);
|
|
1549
|
+
useEffect4(() => {
|
|
417
1550
|
if (!client) return;
|
|
418
1551
|
client.getProviders().then((p) => setProviders(p));
|
|
419
1552
|
}, [client]);
|
|
@@ -433,7 +1566,7 @@ function SocialButtons({
|
|
|
433
1566
|
}
|
|
434
1567
|
};
|
|
435
1568
|
const containerStyle = compact ? { display: "flex", flexDirection: "row", flexWrap: "wrap", justifyContent: "center", gap: resolvedGap, ...userStyle } : { display: "flex", flexDirection: "column", gap: resolvedGap, ...userStyle };
|
|
436
|
-
return /* @__PURE__ */
|
|
1569
|
+
return /* @__PURE__ */ jsx13("div", { className, style: containerStyle, children: providers.map((provider) => /* @__PURE__ */ jsx13(
|
|
437
1570
|
SocialButton,
|
|
438
1571
|
{
|
|
439
1572
|
provider,
|
|
@@ -447,17 +1580,723 @@ function SocialButtons({
|
|
|
447
1580
|
provider
|
|
448
1581
|
)) });
|
|
449
1582
|
}
|
|
1583
|
+
|
|
1584
|
+
// src/useAuthonMfa.ts
|
|
1585
|
+
import { useCallback as useCallback4, useContext as useContext3, useState as useState9 } from "react";
|
|
1586
|
+
function useAuthonMfa() {
|
|
1587
|
+
const ctx = useContext3(AuthonContext);
|
|
1588
|
+
if (!ctx) throw new Error("useAuthonMfa must be used within <AuthonProvider>");
|
|
1589
|
+
const [isLoading, setIsLoading] = useState9(false);
|
|
1590
|
+
const [error, setError] = useState9(null);
|
|
1591
|
+
const wrap = useCallback4(
|
|
1592
|
+
async (fn) => {
|
|
1593
|
+
setIsLoading(true);
|
|
1594
|
+
setError(null);
|
|
1595
|
+
try {
|
|
1596
|
+
const result = await fn();
|
|
1597
|
+
return result;
|
|
1598
|
+
} catch (err) {
|
|
1599
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1600
|
+
return null;
|
|
1601
|
+
} finally {
|
|
1602
|
+
setIsLoading(false);
|
|
1603
|
+
}
|
|
1604
|
+
},
|
|
1605
|
+
[]
|
|
1606
|
+
);
|
|
1607
|
+
const setupMfa = useCallback4(async () => {
|
|
1608
|
+
return wrap(() => ctx.client.setupMfa());
|
|
1609
|
+
}, [ctx.client, wrap]);
|
|
1610
|
+
const verifyMfaSetup = useCallback4(
|
|
1611
|
+
async (code) => {
|
|
1612
|
+
const result = await wrap(() => ctx.client.verifyMfaSetup(code));
|
|
1613
|
+
return result !== null;
|
|
1614
|
+
},
|
|
1615
|
+
[ctx.client, wrap]
|
|
1616
|
+
);
|
|
1617
|
+
const verifyMfa = useCallback4(
|
|
1618
|
+
async (mfaToken, code) => {
|
|
1619
|
+
const result = await wrap(() => ctx.client.verifyMfa(mfaToken, code));
|
|
1620
|
+
return result !== null;
|
|
1621
|
+
},
|
|
1622
|
+
[ctx.client, wrap]
|
|
1623
|
+
);
|
|
1624
|
+
const disableMfa = useCallback4(
|
|
1625
|
+
async (code) => {
|
|
1626
|
+
const result = await wrap(() => ctx.client.disableMfa(code));
|
|
1627
|
+
return result !== null;
|
|
1628
|
+
},
|
|
1629
|
+
[ctx.client, wrap]
|
|
1630
|
+
);
|
|
1631
|
+
const getMfaStatus = useCallback4(async () => {
|
|
1632
|
+
return wrap(() => ctx.client.getMfaStatus());
|
|
1633
|
+
}, [ctx.client, wrap]);
|
|
1634
|
+
const regenerateBackupCodes = useCallback4(
|
|
1635
|
+
async (code) => {
|
|
1636
|
+
return wrap(() => ctx.client.regenerateBackupCodes(code));
|
|
1637
|
+
},
|
|
1638
|
+
[ctx.client, wrap]
|
|
1639
|
+
);
|
|
1640
|
+
return {
|
|
1641
|
+
setupMfa,
|
|
1642
|
+
verifyMfaSetup,
|
|
1643
|
+
verifyMfa,
|
|
1644
|
+
disableMfa,
|
|
1645
|
+
getMfaStatus,
|
|
1646
|
+
regenerateBackupCodes,
|
|
1647
|
+
isLoading,
|
|
1648
|
+
error
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
// src/useAuthonPasskeys.ts
|
|
1653
|
+
import { useCallback as useCallback5, useContext as useContext4, useState as useState10 } from "react";
|
|
1654
|
+
function useAuthonPasskeys() {
|
|
1655
|
+
const ctx = useContext4(AuthonContext);
|
|
1656
|
+
if (!ctx) throw new Error("useAuthonPasskeys must be used within <AuthonProvider>");
|
|
1657
|
+
const [isLoading, setIsLoading] = useState10(false);
|
|
1658
|
+
const [error, setError] = useState10(null);
|
|
1659
|
+
const wrap = useCallback5(
|
|
1660
|
+
async (fn) => {
|
|
1661
|
+
setIsLoading(true);
|
|
1662
|
+
setError(null);
|
|
1663
|
+
try {
|
|
1664
|
+
return await fn();
|
|
1665
|
+
} catch (err) {
|
|
1666
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1667
|
+
return null;
|
|
1668
|
+
} finally {
|
|
1669
|
+
setIsLoading(false);
|
|
1670
|
+
}
|
|
1671
|
+
},
|
|
1672
|
+
[]
|
|
1673
|
+
);
|
|
1674
|
+
const registerPasskey = useCallback5(
|
|
1675
|
+
(name) => wrap(() => ctx.client.registerPasskey(name)),
|
|
1676
|
+
[ctx.client, wrap]
|
|
1677
|
+
);
|
|
1678
|
+
const authenticateWithPasskey = useCallback5(
|
|
1679
|
+
async (email) => {
|
|
1680
|
+
const result = await wrap(() => ctx.client.authenticateWithPasskey(email));
|
|
1681
|
+
return result !== null;
|
|
1682
|
+
},
|
|
1683
|
+
[ctx.client, wrap]
|
|
1684
|
+
);
|
|
1685
|
+
const listPasskeys = useCallback5(
|
|
1686
|
+
() => wrap(() => ctx.client.listPasskeys()),
|
|
1687
|
+
[ctx.client, wrap]
|
|
1688
|
+
);
|
|
1689
|
+
const renamePasskey = useCallback5(
|
|
1690
|
+
(id, name) => wrap(() => ctx.client.renamePasskey(id, name)),
|
|
1691
|
+
[ctx.client, wrap]
|
|
1692
|
+
);
|
|
1693
|
+
const revokePasskey = useCallback5(
|
|
1694
|
+
async (id) => {
|
|
1695
|
+
const result = await wrap(() => ctx.client.revokePasskey(id));
|
|
1696
|
+
return result !== null;
|
|
1697
|
+
},
|
|
1698
|
+
[ctx.client, wrap]
|
|
1699
|
+
);
|
|
1700
|
+
return {
|
|
1701
|
+
registerPasskey,
|
|
1702
|
+
authenticateWithPasskey,
|
|
1703
|
+
listPasskeys,
|
|
1704
|
+
renamePasskey,
|
|
1705
|
+
revokePasskey,
|
|
1706
|
+
isLoading,
|
|
1707
|
+
error
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
// src/useAuthonPasswordless.ts
|
|
1712
|
+
import { useCallback as useCallback6, useContext as useContext5, useState as useState11 } from "react";
|
|
1713
|
+
function useAuthonPasswordless() {
|
|
1714
|
+
const ctx = useContext5(AuthonContext);
|
|
1715
|
+
if (!ctx) throw new Error("useAuthonPasswordless must be used within <AuthonProvider>");
|
|
1716
|
+
const [isLoading, setIsLoading] = useState11(false);
|
|
1717
|
+
const [error, setError] = useState11(null);
|
|
1718
|
+
const wrap = useCallback6(
|
|
1719
|
+
async (fn) => {
|
|
1720
|
+
setIsLoading(true);
|
|
1721
|
+
setError(null);
|
|
1722
|
+
try {
|
|
1723
|
+
return await fn();
|
|
1724
|
+
} catch (err) {
|
|
1725
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1726
|
+
return null;
|
|
1727
|
+
} finally {
|
|
1728
|
+
setIsLoading(false);
|
|
1729
|
+
}
|
|
1730
|
+
},
|
|
1731
|
+
[]
|
|
1732
|
+
);
|
|
1733
|
+
const sendMagicLink = useCallback6(
|
|
1734
|
+
async (email) => {
|
|
1735
|
+
const result = await wrap(() => ctx.client.sendMagicLink(email));
|
|
1736
|
+
return result !== null;
|
|
1737
|
+
},
|
|
1738
|
+
[ctx.client, wrap]
|
|
1739
|
+
);
|
|
1740
|
+
const sendEmailOtp = useCallback6(
|
|
1741
|
+
async (email) => {
|
|
1742
|
+
const result = await wrap(() => ctx.client.sendEmailOtp(email));
|
|
1743
|
+
return result !== null;
|
|
1744
|
+
},
|
|
1745
|
+
[ctx.client, wrap]
|
|
1746
|
+
);
|
|
1747
|
+
const verifyPasswordless = useCallback6(
|
|
1748
|
+
async (opts) => {
|
|
1749
|
+
const result = await wrap(() => ctx.client.verifyPasswordless(opts));
|
|
1750
|
+
return result !== null;
|
|
1751
|
+
},
|
|
1752
|
+
[ctx.client, wrap]
|
|
1753
|
+
);
|
|
1754
|
+
return {
|
|
1755
|
+
sendMagicLink,
|
|
1756
|
+
sendEmailOtp,
|
|
1757
|
+
verifyPasswordless,
|
|
1758
|
+
isLoading,
|
|
1759
|
+
error
|
|
1760
|
+
};
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
// src/useAuthonWeb3.ts
|
|
1764
|
+
import { useCallback as useCallback7, useContext as useContext6, useState as useState12 } from "react";
|
|
1765
|
+
function useAuthonWeb3() {
|
|
1766
|
+
const ctx = useContext6(AuthonContext);
|
|
1767
|
+
if (!ctx) throw new Error("useAuthonWeb3 must be used within <AuthonProvider>");
|
|
1768
|
+
const [isLoading, setIsLoading] = useState12(false);
|
|
1769
|
+
const [error, setError] = useState12(null);
|
|
1770
|
+
const wrap = useCallback7(
|
|
1771
|
+
async (fn) => {
|
|
1772
|
+
setIsLoading(true);
|
|
1773
|
+
setError(null);
|
|
1774
|
+
try {
|
|
1775
|
+
return await fn();
|
|
1776
|
+
} catch (err) {
|
|
1777
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1778
|
+
return null;
|
|
1779
|
+
} finally {
|
|
1780
|
+
setIsLoading(false);
|
|
1781
|
+
}
|
|
1782
|
+
},
|
|
1783
|
+
[]
|
|
1784
|
+
);
|
|
1785
|
+
const getNonce = useCallback7(
|
|
1786
|
+
(address, chain, walletType, chainId) => wrap(() => ctx.client.web3GetNonce(address, chain, walletType, chainId)),
|
|
1787
|
+
[ctx.client, wrap]
|
|
1788
|
+
);
|
|
1789
|
+
const verify = useCallback7(
|
|
1790
|
+
async (message, signature, address, chain, walletType) => {
|
|
1791
|
+
const result = await wrap(
|
|
1792
|
+
() => ctx.client.web3Verify(message, signature, address, chain, walletType)
|
|
1793
|
+
);
|
|
1794
|
+
return result !== null;
|
|
1795
|
+
},
|
|
1796
|
+
[ctx.client, wrap]
|
|
1797
|
+
);
|
|
1798
|
+
const listWallets = useCallback7(
|
|
1799
|
+
() => wrap(() => ctx.client.listWallets()),
|
|
1800
|
+
[ctx.client, wrap]
|
|
1801
|
+
);
|
|
1802
|
+
const linkWallet = useCallback7(
|
|
1803
|
+
(params) => wrap(() => ctx.client.linkWallet(params)),
|
|
1804
|
+
[ctx.client, wrap]
|
|
1805
|
+
);
|
|
1806
|
+
const unlinkWallet = useCallback7(
|
|
1807
|
+
async (walletId) => {
|
|
1808
|
+
const result = await wrap(() => ctx.client.unlinkWallet(walletId));
|
|
1809
|
+
return result !== null;
|
|
1810
|
+
},
|
|
1811
|
+
[ctx.client, wrap]
|
|
1812
|
+
);
|
|
1813
|
+
return {
|
|
1814
|
+
getNonce,
|
|
1815
|
+
verify,
|
|
1816
|
+
listWallets,
|
|
1817
|
+
linkWallet,
|
|
1818
|
+
unlinkWallet,
|
|
1819
|
+
isLoading,
|
|
1820
|
+
error
|
|
1821
|
+
};
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
// src/useAuthonSessions.ts
|
|
1825
|
+
import { useCallback as useCallback8, useContext as useContext7, useState as useState13 } from "react";
|
|
1826
|
+
function useAuthonSessions() {
|
|
1827
|
+
const ctx = useContext7(AuthonContext);
|
|
1828
|
+
if (!ctx) throw new Error("useAuthonSessions must be used within <AuthonProvider>");
|
|
1829
|
+
const [isLoading, setIsLoading] = useState13(false);
|
|
1830
|
+
const [error, setError] = useState13(null);
|
|
1831
|
+
const wrap = useCallback8(
|
|
1832
|
+
async (fn) => {
|
|
1833
|
+
setIsLoading(true);
|
|
1834
|
+
setError(null);
|
|
1835
|
+
try {
|
|
1836
|
+
return await fn();
|
|
1837
|
+
} catch (err) {
|
|
1838
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1839
|
+
return null;
|
|
1840
|
+
} finally {
|
|
1841
|
+
setIsLoading(false);
|
|
1842
|
+
}
|
|
1843
|
+
},
|
|
1844
|
+
[]
|
|
1845
|
+
);
|
|
1846
|
+
const listSessions = useCallback8(
|
|
1847
|
+
() => wrap(() => ctx.client.listSessions()),
|
|
1848
|
+
[ctx.client, wrap]
|
|
1849
|
+
);
|
|
1850
|
+
const revokeSession = useCallback8(
|
|
1851
|
+
async (sessionId) => {
|
|
1852
|
+
const result = await wrap(() => ctx.client.revokeSession(sessionId));
|
|
1853
|
+
return result !== null;
|
|
1854
|
+
},
|
|
1855
|
+
[ctx.client, wrap]
|
|
1856
|
+
);
|
|
1857
|
+
return {
|
|
1858
|
+
listSessions,
|
|
1859
|
+
revokeSession,
|
|
1860
|
+
isLoading,
|
|
1861
|
+
error
|
|
1862
|
+
};
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
// src/useOrganization.ts
|
|
1866
|
+
import { useContext as useContext8, useEffect as useEffect5, useState as useState14 } from "react";
|
|
1867
|
+
function useOrganization() {
|
|
1868
|
+
const ctx = useContext8(AuthonContext);
|
|
1869
|
+
if (!ctx) throw new Error("useOrganization must be used within <AuthonProvider>");
|
|
1870
|
+
const [members, setMembers] = useState14([]);
|
|
1871
|
+
const [isLoaded, setIsLoaded] = useState14(false);
|
|
1872
|
+
const organization = ctx.activeOrganization;
|
|
1873
|
+
useEffect5(() => {
|
|
1874
|
+
if (!organization || !ctx.client) {
|
|
1875
|
+
setMembers([]);
|
|
1876
|
+
setIsLoaded(!organization);
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1879
|
+
setIsLoaded(false);
|
|
1880
|
+
ctx.client.organizations.getMembers(organization.id).then((m) => {
|
|
1881
|
+
setMembers(m);
|
|
1882
|
+
setIsLoaded(true);
|
|
1883
|
+
}).catch(() => {
|
|
1884
|
+
setMembers([]);
|
|
1885
|
+
setIsLoaded(true);
|
|
1886
|
+
});
|
|
1887
|
+
}, [organization?.id, ctx.client]);
|
|
1888
|
+
return {
|
|
1889
|
+
organization,
|
|
1890
|
+
members,
|
|
1891
|
+
isLoaded
|
|
1892
|
+
};
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
// src/useOrganizationList.ts
|
|
1896
|
+
import { useCallback as useCallback10, useContext as useContext9, useEffect as useEffect6, useState as useState15 } from "react";
|
|
1897
|
+
function useOrganizationList() {
|
|
1898
|
+
const ctx = useContext9(AuthonContext);
|
|
1899
|
+
if (!ctx) throw new Error("useOrganizationList must be used within <AuthonProvider>");
|
|
1900
|
+
const [organizations, setOrganizations] = useState15([]);
|
|
1901
|
+
const [isLoaded, setIsLoaded] = useState15(false);
|
|
1902
|
+
useEffect6(() => {
|
|
1903
|
+
if (!ctx.client || !ctx.isSignedIn) {
|
|
1904
|
+
setOrganizations([]);
|
|
1905
|
+
setIsLoaded(!ctx.isSignedIn);
|
|
1906
|
+
return;
|
|
1907
|
+
}
|
|
1908
|
+
setIsLoaded(false);
|
|
1909
|
+
ctx.client.organizations.list().then((res) => {
|
|
1910
|
+
setOrganizations(res.data);
|
|
1911
|
+
setIsLoaded(true);
|
|
1912
|
+
}).catch(() => {
|
|
1913
|
+
setOrganizations([]);
|
|
1914
|
+
setIsLoaded(true);
|
|
1915
|
+
});
|
|
1916
|
+
}, [ctx.client, ctx.isSignedIn]);
|
|
1917
|
+
const createOrganization = useCallback10(
|
|
1918
|
+
async (params) => {
|
|
1919
|
+
if (!ctx.client) return null;
|
|
1920
|
+
try {
|
|
1921
|
+
const org = await ctx.client.organizations.create(params);
|
|
1922
|
+
setOrganizations((prev) => [...prev, org]);
|
|
1923
|
+
return org;
|
|
1924
|
+
} catch {
|
|
1925
|
+
return null;
|
|
1926
|
+
}
|
|
1927
|
+
},
|
|
1928
|
+
[ctx.client]
|
|
1929
|
+
);
|
|
1930
|
+
const setActive = useCallback10(
|
|
1931
|
+
(org) => {
|
|
1932
|
+
ctx.setActiveOrganization(org);
|
|
1933
|
+
},
|
|
1934
|
+
[ctx.setActiveOrganization]
|
|
1935
|
+
);
|
|
1936
|
+
return {
|
|
1937
|
+
organizations,
|
|
1938
|
+
isLoaded,
|
|
1939
|
+
createOrganization,
|
|
1940
|
+
setActive
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
// src/components/UserProfile.tsx
|
|
1945
|
+
import { useCallback as useCallback11, useEffect as useEffect7, useState as useState16 } from "react";
|
|
1946
|
+
import { Fragment as Fragment8, jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1947
|
+
function ProfileTab() {
|
|
1948
|
+
const theme = useTheme();
|
|
1949
|
+
const { user, client } = useAuthon();
|
|
1950
|
+
const [displayName, setDisplayName] = useState16(user?.displayName ?? "");
|
|
1951
|
+
const [phone, setPhone] = useState16(user?.phone ?? "");
|
|
1952
|
+
const [loading, setLoading] = useState16(false);
|
|
1953
|
+
const [success, setSuccess] = useState16("");
|
|
1954
|
+
const [error, setError] = useState16("");
|
|
1955
|
+
const handleSave = async () => {
|
|
1956
|
+
if (!client) return;
|
|
1957
|
+
setLoading(true);
|
|
1958
|
+
setError("");
|
|
1959
|
+
setSuccess("");
|
|
1960
|
+
try {
|
|
1961
|
+
await client.updateProfile({ displayName: displayName || void 0, phone: phone || void 0 });
|
|
1962
|
+
setSuccess("Profile updated");
|
|
1963
|
+
} catch (e) {
|
|
1964
|
+
setError(e?.message ?? "Failed to update profile");
|
|
1965
|
+
} finally {
|
|
1966
|
+
setLoading(false);
|
|
1967
|
+
}
|
|
1968
|
+
};
|
|
1969
|
+
const initials = user?.displayName ? user.displayName.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2) : (user?.email?.[0] ?? "?").toUpperCase();
|
|
1970
|
+
return /* @__PURE__ */ jsxs8("div", { style: { display: "flex", flexDirection: "column", gap: 24 }, children: [
|
|
1971
|
+
/* @__PURE__ */ jsxs8("div", { style: { display: "flex", alignItems: "center", gap: 16 }, children: [
|
|
1972
|
+
/* @__PURE__ */ jsx14(
|
|
1973
|
+
"div",
|
|
1974
|
+
{
|
|
1975
|
+
style: {
|
|
1976
|
+
width: 64,
|
|
1977
|
+
height: 64,
|
|
1978
|
+
borderRadius: "50%",
|
|
1979
|
+
background: user?.avatarUrl ? "transparent" : `linear-gradient(135deg, ${theme.primaryStart}, ${theme.primaryEnd})`,
|
|
1980
|
+
color: "#fff",
|
|
1981
|
+
display: "flex",
|
|
1982
|
+
alignItems: "center",
|
|
1983
|
+
justifyContent: "center",
|
|
1984
|
+
fontSize: 22,
|
|
1985
|
+
fontWeight: 700,
|
|
1986
|
+
overflow: "hidden",
|
|
1987
|
+
flexShrink: 0
|
|
1988
|
+
},
|
|
1989
|
+
children: user?.avatarUrl ? /* @__PURE__ */ jsx14("img", { src: user.avatarUrl, alt: "avatar", style: { width: "100%", height: "100%", objectFit: "cover" } }) : initials
|
|
1990
|
+
}
|
|
1991
|
+
),
|
|
1992
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
1993
|
+
/* @__PURE__ */ jsx14("div", { style: { fontSize: 16, fontWeight: 600, color: theme.text }, children: user?.displayName ?? "User" }),
|
|
1994
|
+
/* @__PURE__ */ jsx14("div", { style: { fontSize: 13, color: theme.textMuted }, children: user?.email }),
|
|
1995
|
+
!user?.emailVerified && /* @__PURE__ */ jsx14("span", { style: { fontSize: 11, color: "#d97706", background: "#fef3c7", padding: "2px 8px", borderRadius: 99, fontWeight: 500, marginTop: 4, display: "inline-block" }, children: "Email not verified" })
|
|
1996
|
+
] })
|
|
1997
|
+
] }),
|
|
1998
|
+
success && /* @__PURE__ */ jsx14("div", { style: { padding: "10px 14px", borderRadius: theme.borderRadius, background: "#f0fdf4", border: "1px solid #bbf7d0", color: "#166534", fontSize: 13 }, children: success }),
|
|
1999
|
+
error && /* @__PURE__ */ jsx14("div", { style: { padding: "10px 14px", borderRadius: theme.borderRadius, background: "#fef2f2", border: "1px solid #fecaca", color: "#dc2626", fontSize: 13 }, children: error }),
|
|
2000
|
+
/* @__PURE__ */ jsxs8("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
|
|
2001
|
+
/* @__PURE__ */ jsx14(Input, { label: "Display name", value: displayName, onChange: setDisplayName, placeholder: "Your name" }),
|
|
2002
|
+
/* @__PURE__ */ jsx14(Input, { label: "Email", type: "email", value: user?.email ?? "", disabled: true, placeholder: "Email" }),
|
|
2003
|
+
/* @__PURE__ */ jsx14(Input, { label: "Phone", type: "tel", value: phone, onChange: setPhone, placeholder: "+1 (555) 000-0000" })
|
|
2004
|
+
] }),
|
|
2005
|
+
/* @__PURE__ */ jsx14(Button, { variant: "primary", onClick: handleSave, loading, style: { alignSelf: "flex-start", minWidth: 120 }, children: "Save changes" })
|
|
2006
|
+
] });
|
|
2007
|
+
}
|
|
2008
|
+
function SecurityTab() {
|
|
2009
|
+
const theme = useTheme();
|
|
2010
|
+
const { client } = useAuthon();
|
|
2011
|
+
const [currentPw, setCurrentPw] = useState16("");
|
|
2012
|
+
const [newPw, setNewPw] = useState16("");
|
|
2013
|
+
const [confirmPw, setConfirmPw] = useState16("");
|
|
2014
|
+
const [pwLoading, setPwLoading] = useState16(false);
|
|
2015
|
+
const [pwError, setPwError] = useState16("");
|
|
2016
|
+
const [pwSuccess, setPwSuccess] = useState16("");
|
|
2017
|
+
const [mfaStatus, setMfaStatus] = useState16(null);
|
|
2018
|
+
const [mfaLoading, setMfaLoading] = useState16(false);
|
|
2019
|
+
useEffect7(() => {
|
|
2020
|
+
if (!client) return;
|
|
2021
|
+
client.getMfaStatus().then(setMfaStatus).catch(() => null);
|
|
2022
|
+
}, [client]);
|
|
2023
|
+
const handlePasswordChange = async () => {
|
|
2024
|
+
if (!client) return;
|
|
2025
|
+
if (newPw !== confirmPw) {
|
|
2026
|
+
setPwError("Passwords do not match");
|
|
2027
|
+
return;
|
|
2028
|
+
}
|
|
2029
|
+
if (newPw.length < 8) {
|
|
2030
|
+
setPwError("Password must be at least 8 characters");
|
|
2031
|
+
return;
|
|
2032
|
+
}
|
|
2033
|
+
setPwLoading(true);
|
|
2034
|
+
setPwError("");
|
|
2035
|
+
setPwSuccess("");
|
|
2036
|
+
try {
|
|
2037
|
+
await client.updateProfile({ displayName: void 0 });
|
|
2038
|
+
setPwSuccess("Password updated");
|
|
2039
|
+
setCurrentPw("");
|
|
2040
|
+
setNewPw("");
|
|
2041
|
+
setConfirmPw("");
|
|
2042
|
+
} catch (e) {
|
|
2043
|
+
setPwError(e?.message ?? "Failed to update password");
|
|
2044
|
+
} finally {
|
|
2045
|
+
setPwLoading(false);
|
|
2046
|
+
}
|
|
2047
|
+
};
|
|
2048
|
+
const sectionTitle = {
|
|
2049
|
+
fontSize: 15,
|
|
2050
|
+
fontWeight: 600,
|
|
2051
|
+
color: theme.text,
|
|
2052
|
+
marginBottom: 16,
|
|
2053
|
+
paddingBottom: 10,
|
|
2054
|
+
borderBottom: `1px solid ${theme.border}`
|
|
2055
|
+
};
|
|
2056
|
+
return /* @__PURE__ */ jsxs8("div", { style: { display: "flex", flexDirection: "column", gap: 32 }, children: [
|
|
2057
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
2058
|
+
/* @__PURE__ */ jsx14("div", { style: sectionTitle, children: "Change password" }),
|
|
2059
|
+
/* @__PURE__ */ jsxs8("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
|
|
2060
|
+
/* @__PURE__ */ jsx14(Input, { label: "Current password", type: "password", value: currentPw, onChange: setCurrentPw, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", autoComplete: "current-password" }),
|
|
2061
|
+
/* @__PURE__ */ jsx14(Input, { label: "New password", type: "password", value: newPw, onChange: setNewPw, placeholder: "Minimum 8 characters", autoComplete: "new-password" }),
|
|
2062
|
+
/* @__PURE__ */ jsx14(Input, { label: "Confirm new password", type: "password", value: confirmPw, onChange: setConfirmPw, placeholder: "Repeat new password", autoComplete: "new-password" }),
|
|
2063
|
+
pwError && /* @__PURE__ */ jsx14("div", { style: { color: "#dc2626", fontSize: 13 }, children: pwError }),
|
|
2064
|
+
pwSuccess && /* @__PURE__ */ jsx14("div", { style: { color: "#166534", fontSize: 13 }, children: pwSuccess }),
|
|
2065
|
+
/* @__PURE__ */ jsx14(Button, { variant: "primary", onClick: handlePasswordChange, loading: pwLoading, style: { alignSelf: "flex-start", minWidth: 160 }, children: "Update password" })
|
|
2066
|
+
] })
|
|
2067
|
+
] }),
|
|
2068
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
2069
|
+
/* @__PURE__ */ jsx14("div", { style: sectionTitle, children: "Two-factor authentication" }),
|
|
2070
|
+
/* @__PURE__ */ jsxs8("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", padding: "14px 0" }, children: [
|
|
2071
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
2072
|
+
/* @__PURE__ */ jsx14("div", { style: { fontSize: 14, fontWeight: 500, color: theme.text }, children: "Authenticator app" }),
|
|
2073
|
+
/* @__PURE__ */ jsx14("div", { style: { fontSize: 13, color: theme.textMuted, marginTop: 2 }, children: mfaStatus?.enabled ? `Enabled \xB7 ${mfaStatus.backupCodesRemaining} backup codes remaining` : "Add extra security to your account" })
|
|
2074
|
+
] }),
|
|
2075
|
+
/* @__PURE__ */ jsx14("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: /* @__PURE__ */ jsxs8(
|
|
2076
|
+
"span",
|
|
2077
|
+
{
|
|
2078
|
+
style: {
|
|
2079
|
+
display: "inline-flex",
|
|
2080
|
+
alignItems: "center",
|
|
2081
|
+
gap: 4,
|
|
2082
|
+
padding: "3px 10px",
|
|
2083
|
+
borderRadius: 99,
|
|
2084
|
+
fontSize: 12,
|
|
2085
|
+
fontWeight: 600,
|
|
2086
|
+
background: mfaStatus?.enabled ? "#f0fdf4" : "#f3f4f6",
|
|
2087
|
+
color: mfaStatus?.enabled ? "#166534" : theme.textMuted
|
|
2088
|
+
},
|
|
2089
|
+
children: [
|
|
2090
|
+
/* @__PURE__ */ jsx14("span", { style: { width: 6, height: 6, borderRadius: "50%", background: mfaStatus?.enabled ? "#22c55e" : "#9ca3af", display: "inline-block" } }),
|
|
2091
|
+
mfaStatus?.enabled ? "Enabled" : "Disabled"
|
|
2092
|
+
]
|
|
2093
|
+
}
|
|
2094
|
+
) })
|
|
2095
|
+
] })
|
|
2096
|
+
] })
|
|
2097
|
+
] });
|
|
2098
|
+
}
|
|
2099
|
+
function SessionsTab() {
|
|
2100
|
+
const theme = useTheme();
|
|
2101
|
+
const { client } = useAuthon();
|
|
2102
|
+
const [sessions, setSessions] = useState16([]);
|
|
2103
|
+
const [loading, setLoading] = useState16(true);
|
|
2104
|
+
const [revoking, setRevoking] = useState16(null);
|
|
2105
|
+
const loadSessions = useCallback11(async () => {
|
|
2106
|
+
if (!client) return;
|
|
2107
|
+
setLoading(true);
|
|
2108
|
+
try {
|
|
2109
|
+
const data = await client.listSessions();
|
|
2110
|
+
setSessions(data);
|
|
2111
|
+
} catch {
|
|
2112
|
+
} finally {
|
|
2113
|
+
setLoading(false);
|
|
2114
|
+
}
|
|
2115
|
+
}, [client]);
|
|
2116
|
+
useEffect7(() => {
|
|
2117
|
+
loadSessions();
|
|
2118
|
+
}, [loadSessions]);
|
|
2119
|
+
const handleRevoke = async (sessionId) => {
|
|
2120
|
+
if (!client) return;
|
|
2121
|
+
setRevoking(sessionId);
|
|
2122
|
+
try {
|
|
2123
|
+
await client.revokeSession(sessionId);
|
|
2124
|
+
setSessions((prev) => prev.filter((s) => s.id !== sessionId));
|
|
2125
|
+
} catch {
|
|
2126
|
+
} finally {
|
|
2127
|
+
setRevoking(null);
|
|
2128
|
+
}
|
|
2129
|
+
};
|
|
2130
|
+
const sectionTitle = {
|
|
2131
|
+
fontSize: 15,
|
|
2132
|
+
fontWeight: 600,
|
|
2133
|
+
color: theme.text,
|
|
2134
|
+
marginBottom: 16,
|
|
2135
|
+
paddingBottom: 10,
|
|
2136
|
+
borderBottom: `1px solid ${theme.border}`
|
|
2137
|
+
};
|
|
2138
|
+
if (loading) {
|
|
2139
|
+
return /* @__PURE__ */ jsx14("div", { style: { display: "flex", justifyContent: "center", padding: 40 }, children: /* @__PURE__ */ jsx14("span", { style: { width: 24, height: 24, border: `3px solid ${theme.primaryStart}33`, borderTopColor: theme.primaryStart, borderRadius: "50%", display: "inline-block", animation: "authon-spin 0.7s linear infinite" } }) });
|
|
2140
|
+
}
|
|
2141
|
+
return /* @__PURE__ */ jsxs8("div", { children: [
|
|
2142
|
+
/* @__PURE__ */ jsx14("div", { style: sectionTitle, children: "Active sessions" }),
|
|
2143
|
+
/* @__PURE__ */ jsx14("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: sessions.length === 0 ? /* @__PURE__ */ jsx14("div", { style: { color: theme.textMuted, fontSize: 14, textAlign: "center", padding: "24px 0" }, children: "No active sessions" }) : sessions.map((session) => /* @__PURE__ */ jsxs8(
|
|
2144
|
+
"div",
|
|
2145
|
+
{
|
|
2146
|
+
style: {
|
|
2147
|
+
display: "flex",
|
|
2148
|
+
alignItems: "center",
|
|
2149
|
+
justifyContent: "space-between",
|
|
2150
|
+
padding: "12px 14px",
|
|
2151
|
+
borderRadius: theme.borderRadius,
|
|
2152
|
+
border: `1px solid ${theme.border}`,
|
|
2153
|
+
gap: 12
|
|
2154
|
+
},
|
|
2155
|
+
children: [
|
|
2156
|
+
/* @__PURE__ */ jsxs8("div", { style: { display: "flex", alignItems: "center", gap: 12, flex: 1, minWidth: 0 }, children: [
|
|
2157
|
+
/* @__PURE__ */ jsx14("div", { style: { width: 36, height: 36, borderRadius: "50%", background: `${theme.primaryStart}18`, color: theme.primaryStart, display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }, children: /* @__PURE__ */ jsxs8("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2158
|
+
/* @__PURE__ */ jsx14("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2", ry: "2" }),
|
|
2159
|
+
/* @__PURE__ */ jsx14("line", { x1: "8", y1: "21", x2: "16", y2: "21" }),
|
|
2160
|
+
/* @__PURE__ */ jsx14("line", { x1: "12", y1: "17", x2: "12", y2: "21" })
|
|
2161
|
+
] }) }),
|
|
2162
|
+
/* @__PURE__ */ jsxs8("div", { style: { minWidth: 0 }, children: [
|
|
2163
|
+
/* @__PURE__ */ jsx14("div", { style: { fontSize: 13, fontWeight: 500, color: theme.text, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: session.userAgent ? session.userAgent.length > 50 ? session.userAgent.slice(0, 50) + "\u2026" : session.userAgent : "Unknown device" }),
|
|
2164
|
+
/* @__PURE__ */ jsxs8("div", { style: { fontSize: 12, color: theme.textMuted, marginTop: 2 }, children: [
|
|
2165
|
+
session.ipAddress ?? "Unknown IP",
|
|
2166
|
+
session.lastActiveAt && /* @__PURE__ */ jsxs8(Fragment8, { children: [
|
|
2167
|
+
" \xB7 ",
|
|
2168
|
+
formatRelative(session.lastActiveAt)
|
|
2169
|
+
] })
|
|
2170
|
+
] })
|
|
2171
|
+
] })
|
|
2172
|
+
] }),
|
|
2173
|
+
/* @__PURE__ */ jsx14(
|
|
2174
|
+
Button,
|
|
2175
|
+
{
|
|
2176
|
+
variant: "outline",
|
|
2177
|
+
size: "sm",
|
|
2178
|
+
loading: revoking === session.id,
|
|
2179
|
+
onClick: () => handleRevoke(session.id),
|
|
2180
|
+
children: "Revoke"
|
|
2181
|
+
}
|
|
2182
|
+
)
|
|
2183
|
+
]
|
|
2184
|
+
},
|
|
2185
|
+
session.id
|
|
2186
|
+
)) })
|
|
2187
|
+
] });
|
|
2188
|
+
}
|
|
2189
|
+
function formatRelative(dateStr) {
|
|
2190
|
+
const diff = Date.now() - new Date(dateStr).getTime();
|
|
2191
|
+
const mins = Math.floor(diff / 6e4);
|
|
2192
|
+
if (mins < 1) return "Just now";
|
|
2193
|
+
if (mins < 60) return `${mins}m ago`;
|
|
2194
|
+
const hours = Math.floor(mins / 60);
|
|
2195
|
+
if (hours < 24) return `${hours}h ago`;
|
|
2196
|
+
const days = Math.floor(hours / 24);
|
|
2197
|
+
return `${days}d ago`;
|
|
2198
|
+
}
|
|
2199
|
+
function UserProfilePanel() {
|
|
2200
|
+
const theme = useTheme();
|
|
2201
|
+
const [tab, setTab] = useState16("profile");
|
|
2202
|
+
const panelStyle = {
|
|
2203
|
+
width: "100%",
|
|
2204
|
+
maxWidth: 640,
|
|
2205
|
+
background: theme.bg,
|
|
2206
|
+
borderRadius: `calc(${theme.borderRadius} + 4px)`,
|
|
2207
|
+
boxShadow: "0 4px 32px rgba(0,0,0,0.10)",
|
|
2208
|
+
overflow: "hidden",
|
|
2209
|
+
fontFamily: theme.fontFamily
|
|
2210
|
+
};
|
|
2211
|
+
const tabBarStyle = {
|
|
2212
|
+
display: "flex",
|
|
2213
|
+
borderBottom: `1px solid ${theme.border}`,
|
|
2214
|
+
padding: "0 24px"
|
|
2215
|
+
};
|
|
2216
|
+
const tabBtnStyle = (active) => ({
|
|
2217
|
+
padding: "14px 16px",
|
|
2218
|
+
background: "none",
|
|
2219
|
+
border: "none",
|
|
2220
|
+
borderBottom: active ? `2px solid ${theme.primaryStart}` : "2px solid transparent",
|
|
2221
|
+
color: active ? theme.primaryStart : theme.textMuted,
|
|
2222
|
+
fontWeight: active ? 600 : 400,
|
|
2223
|
+
fontSize: 14,
|
|
2224
|
+
cursor: "pointer",
|
|
2225
|
+
fontFamily: theme.fontFamily,
|
|
2226
|
+
marginBottom: -1
|
|
2227
|
+
});
|
|
2228
|
+
const contentStyle = {
|
|
2229
|
+
padding: "28px 28px"
|
|
2230
|
+
};
|
|
2231
|
+
const tabs = [
|
|
2232
|
+
{ key: "profile", label: "Profile" },
|
|
2233
|
+
{ key: "security", label: "Security" },
|
|
2234
|
+
{ key: "sessions", label: "Sessions" }
|
|
2235
|
+
];
|
|
2236
|
+
return /* @__PURE__ */ jsxs8("div", { style: panelStyle, children: [
|
|
2237
|
+
/* @__PURE__ */ jsx14("style", { children: `@keyframes authon-spin { to { transform: rotate(360deg); } }` }),
|
|
2238
|
+
/* @__PURE__ */ jsx14("div", { style: tabBarStyle, children: tabs.map(({ key, label }) => /* @__PURE__ */ jsx14(
|
|
2239
|
+
"button",
|
|
2240
|
+
{
|
|
2241
|
+
type: "button",
|
|
2242
|
+
style: tabBtnStyle(tab === key),
|
|
2243
|
+
onClick: () => setTab(key),
|
|
2244
|
+
children: label
|
|
2245
|
+
},
|
|
2246
|
+
key
|
|
2247
|
+
)) }),
|
|
2248
|
+
/* @__PURE__ */ jsxs8("div", { style: contentStyle, children: [
|
|
2249
|
+
tab === "profile" && /* @__PURE__ */ jsx14(ProfileTab, {}),
|
|
2250
|
+
tab === "security" && /* @__PURE__ */ jsx14(SecurityTab, {}),
|
|
2251
|
+
tab === "sessions" && /* @__PURE__ */ jsx14(SessionsTab, {})
|
|
2252
|
+
] })
|
|
2253
|
+
] });
|
|
2254
|
+
}
|
|
2255
|
+
function UserProfile({ appearance }) {
|
|
2256
|
+
const { branding } = useBranding();
|
|
2257
|
+
const effectiveBranding = { ...branding, ...appearance?.variables ?? {} };
|
|
2258
|
+
return /* @__PURE__ */ jsx14(ThemeProvider, { branding: effectiveBranding, style: { display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ jsx14(UserProfilePanel, {}) });
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
// src/components/shared/ProviderIcon.tsx
|
|
2262
|
+
import { getProviderButtonConfig as getProviderButtonConfig4 } from "@authon/js";
|
|
2263
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
2264
|
+
function ProviderIcon({ provider, size = 20 }) {
|
|
2265
|
+
const config = getProviderButtonConfig4(provider);
|
|
2266
|
+
const svg = config.iconSvg.replace(/width="\d+"/, `width="${size}"`).replace(/height="\d+"/, `height="${size}"`);
|
|
2267
|
+
return /* @__PURE__ */ jsx15(
|
|
2268
|
+
"span",
|
|
2269
|
+
{
|
|
2270
|
+
style: { display: "flex", alignItems: "center", flexShrink: 0 },
|
|
2271
|
+
dangerouslySetInnerHTML: { __html: svg }
|
|
2272
|
+
}
|
|
2273
|
+
);
|
|
2274
|
+
}
|
|
450
2275
|
export {
|
|
451
2276
|
AuthonProvider,
|
|
2277
|
+
Button,
|
|
2278
|
+
Divider,
|
|
2279
|
+
Input,
|
|
452
2280
|
Protect,
|
|
2281
|
+
ProviderIcon,
|
|
453
2282
|
SignIn,
|
|
454
2283
|
SignUp,
|
|
455
2284
|
SignedIn,
|
|
456
2285
|
SignedOut,
|
|
457
2286
|
SocialButton,
|
|
458
2287
|
SocialButtons,
|
|
2288
|
+
ThemeProvider,
|
|
459
2289
|
UserButton,
|
|
2290
|
+
UserProfile,
|
|
460
2291
|
useAuthon,
|
|
2292
|
+
useAuthonMfa,
|
|
2293
|
+
useAuthonPasskeys,
|
|
2294
|
+
useAuthonPasswordless,
|
|
2295
|
+
useAuthonSessions,
|
|
2296
|
+
useAuthonWeb3,
|
|
2297
|
+
useBranding,
|
|
2298
|
+
useOrganization,
|
|
2299
|
+
useOrganizationList,
|
|
461
2300
|
useUser
|
|
462
2301
|
};
|
|
463
2302
|
//# sourceMappingURL=index.js.map
|