@caffeinebounce/identity 0.12.1 → 0.12.3
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/dist/index.d.mts +8 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +203 -124
- package/dist/index.mjs +205 -126
- package/dist/server.js +54 -17
- package/dist/server.mjs +54 -17
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -241,6 +241,10 @@ interface ConsentItem {
|
|
|
241
241
|
errorMessage?: string;
|
|
242
242
|
}
|
|
243
243
|
interface SignupFormProps extends AuthFormConfig {
|
|
244
|
+
/** Optional heading override for the sign-up form */
|
|
245
|
+
title?: string;
|
|
246
|
+
/** Optional initial value for the email field */
|
|
247
|
+
initialEmail?: string;
|
|
244
248
|
/** Navigation links configuration */
|
|
245
249
|
links?: AuthLinks;
|
|
246
250
|
/** Image component to use (e.g., Next.js Image) */
|
|
@@ -283,7 +287,7 @@ interface SignupFormProps extends AuthFormConfig {
|
|
|
283
287
|
/**
|
|
284
288
|
* SignupForm - Configurable sign-up form with email/password and OAuth support
|
|
285
289
|
*/
|
|
286
|
-
declare function SignupForm({ createClient, logo, appName, termsUrl, privacyUrl, links, ImageComponent, LinkComponent, oauthProviders, googleComingSoon, azureComingSoon, oauthIconMonochromeOnHover, className, onAuthEvent, consentItems, consentPosition, consentSize, showHomeLink, showLogo, }: SignupFormProps): react_jsx_runtime.JSX.Element;
|
|
290
|
+
declare function SignupForm({ createClient, logo, appName, title, initialEmail, termsUrl, privacyUrl, links, ImageComponent, LinkComponent, oauthProviders, googleComingSoon, azureComingSoon, oauthIconMonochromeOnHover, className, onAuthEvent, consentItems, consentPosition, consentSize, showHomeLink, showLogo, }: SignupFormProps): react_jsx_runtime.JSX.Element;
|
|
287
291
|
|
|
288
292
|
/**
|
|
289
293
|
* MFA event logging callbacks
|
|
@@ -480,6 +484,8 @@ interface DeleteAccountSectionProps {
|
|
|
480
484
|
declare function DeleteAccountSection({ createClient, email, retentionDays, }: DeleteAccountSectionProps): react_jsx_runtime.JSX.Element;
|
|
481
485
|
|
|
482
486
|
interface EmailSectionProps {
|
|
487
|
+
/** Render the change action as a compact pencil icon instead of a text label */
|
|
488
|
+
changeButtonPresentation?: "label" | "icon";
|
|
483
489
|
/** Function to create a Supabase client */
|
|
484
490
|
createClient: CreateClientFn;
|
|
485
491
|
/** User ID (reserved for future use) */
|
|
@@ -491,7 +497,7 @@ interface EmailSectionProps {
|
|
|
491
497
|
/** Callback when email is changed */
|
|
492
498
|
onEmailChanged: (newEmail: string) => void;
|
|
493
499
|
}
|
|
494
|
-
declare function EmailSection({ createClient, userId: _userId, email, isVerified, onEmailChanged, }: EmailSectionProps): react_jsx_runtime.JSX.Element;
|
|
500
|
+
declare function EmailSection({ changeButtonPresentation, createClient, userId: _userId, email, isVerified, onEmailChanged, }: EmailSectionProps): react_jsx_runtime.JSX.Element;
|
|
495
501
|
|
|
496
502
|
interface LinkedAccountsSectionProps {
|
|
497
503
|
/** Function to create a Supabase client */
|
package/dist/index.d.ts
CHANGED
|
@@ -241,6 +241,10 @@ interface ConsentItem {
|
|
|
241
241
|
errorMessage?: string;
|
|
242
242
|
}
|
|
243
243
|
interface SignupFormProps extends AuthFormConfig {
|
|
244
|
+
/** Optional heading override for the sign-up form */
|
|
245
|
+
title?: string;
|
|
246
|
+
/** Optional initial value for the email field */
|
|
247
|
+
initialEmail?: string;
|
|
244
248
|
/** Navigation links configuration */
|
|
245
249
|
links?: AuthLinks;
|
|
246
250
|
/** Image component to use (e.g., Next.js Image) */
|
|
@@ -283,7 +287,7 @@ interface SignupFormProps extends AuthFormConfig {
|
|
|
283
287
|
/**
|
|
284
288
|
* SignupForm - Configurable sign-up form with email/password and OAuth support
|
|
285
289
|
*/
|
|
286
|
-
declare function SignupForm({ createClient, logo, appName, termsUrl, privacyUrl, links, ImageComponent, LinkComponent, oauthProviders, googleComingSoon, azureComingSoon, oauthIconMonochromeOnHover, className, onAuthEvent, consentItems, consentPosition, consentSize, showHomeLink, showLogo, }: SignupFormProps): react_jsx_runtime.JSX.Element;
|
|
290
|
+
declare function SignupForm({ createClient, logo, appName, title, initialEmail, termsUrl, privacyUrl, links, ImageComponent, LinkComponent, oauthProviders, googleComingSoon, azureComingSoon, oauthIconMonochromeOnHover, className, onAuthEvent, consentItems, consentPosition, consentSize, showHomeLink, showLogo, }: SignupFormProps): react_jsx_runtime.JSX.Element;
|
|
287
291
|
|
|
288
292
|
/**
|
|
289
293
|
* MFA event logging callbacks
|
|
@@ -480,6 +484,8 @@ interface DeleteAccountSectionProps {
|
|
|
480
484
|
declare function DeleteAccountSection({ createClient, email, retentionDays, }: DeleteAccountSectionProps): react_jsx_runtime.JSX.Element;
|
|
481
485
|
|
|
482
486
|
interface EmailSectionProps {
|
|
487
|
+
/** Render the change action as a compact pencil icon instead of a text label */
|
|
488
|
+
changeButtonPresentation?: "label" | "icon";
|
|
483
489
|
/** Function to create a Supabase client */
|
|
484
490
|
createClient: CreateClientFn;
|
|
485
491
|
/** User ID (reserved for future use) */
|
|
@@ -491,7 +497,7 @@ interface EmailSectionProps {
|
|
|
491
497
|
/** Callback when email is changed */
|
|
492
498
|
onEmailChanged: (newEmail: string) => void;
|
|
493
499
|
}
|
|
494
|
-
declare function EmailSection({ createClient, userId: _userId, email, isVerified, onEmailChanged, }: EmailSectionProps): react_jsx_runtime.JSX.Element;
|
|
500
|
+
declare function EmailSection({ changeButtonPresentation, createClient, userId: _userId, email, isVerified, onEmailChanged, }: EmailSectionProps): react_jsx_runtime.JSX.Element;
|
|
495
501
|
|
|
496
502
|
interface LinkedAccountsSectionProps {
|
|
497
503
|
/** Function to create a Supabase client */
|
package/dist/index.js
CHANGED
|
@@ -19,6 +19,17 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
19
19
|
var isEmail__default = /*#__PURE__*/_interopDefault(isEmail);
|
|
20
20
|
|
|
21
21
|
// src/components/auth/EmailVerificationPending.tsx
|
|
22
|
+
|
|
23
|
+
// src/types.ts
|
|
24
|
+
var defaultAuthLinks = {
|
|
25
|
+
signIn: "/signin",
|
|
26
|
+
signUp: "/signup",
|
|
27
|
+
forgotPassword: "/forgot-password",
|
|
28
|
+
resetPassword: "/reset-password",
|
|
29
|
+
callback: "/callback",
|
|
30
|
+
home: "/",
|
|
31
|
+
defaultRedirect: "/dashboard"
|
|
32
|
+
};
|
|
22
33
|
function AuthFormLayout({
|
|
23
34
|
children,
|
|
24
35
|
footer,
|
|
@@ -55,6 +66,122 @@ function AuthFormLayout({
|
|
|
55
66
|
] }) })
|
|
56
67
|
] });
|
|
57
68
|
}
|
|
69
|
+
|
|
70
|
+
// src/utils/redirect.ts
|
|
71
|
+
var INTERNAL_REDIRECT_BASE = new URL("https://identity.invalid");
|
|
72
|
+
var ENCODED_PATH_SEPARATOR = /%(?:2f|5c)/i;
|
|
73
|
+
function hasControlCharacter(value) {
|
|
74
|
+
return Array.from(value).some((character) => {
|
|
75
|
+
const code = character.charCodeAt(0);
|
|
76
|
+
return code <= 31 || code === 127;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function isStrictInternalRedirect(candidate) {
|
|
80
|
+
if (!candidate?.startsWith("/") || hasControlCharacter(candidate)) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const pathEnd = candidate.search(/[?#]/);
|
|
84
|
+
const pathname = pathEnd === -1 ? candidate : candidate.slice(0, pathEnd);
|
|
85
|
+
if (pathname.includes("\\") || ENCODED_PATH_SEPARATOR.test(pathname)) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const target = new URL(candidate, INTERNAL_REDIRECT_BASE);
|
|
90
|
+
return target.origin === INTERNAL_REDIRECT_BASE.origin;
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function getSafeInternalRedirect(candidate, fallback) {
|
|
96
|
+
if (isStrictInternalRedirect(candidate)) {
|
|
97
|
+
return candidate;
|
|
98
|
+
}
|
|
99
|
+
if (fallback === null) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
return isStrictInternalRedirect(fallback) ? fallback : "/";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/components/auth/utils.ts
|
|
106
|
+
var warnedSupabaseRedirectOrigins = /* @__PURE__ */ new Set();
|
|
107
|
+
function hardRedirect(redirectTo) {
|
|
108
|
+
window.location.href = redirectTo;
|
|
109
|
+
}
|
|
110
|
+
function sanitizeAuthError(message) {
|
|
111
|
+
const lowerMessage = message.toLowerCase();
|
|
112
|
+
if (lowerMessage.includes("user already registered") || lowerMessage.includes("already registered") || lowerMessage.includes("already exists") || lowerMessage.includes("email already") || lowerMessage.includes("duplicate")) {
|
|
113
|
+
return "An account with this email already exists. Please sign in instead.";
|
|
114
|
+
}
|
|
115
|
+
if (lowerMessage.includes("invalid login credentials") || lowerMessage.includes("invalid credentials") || lowerMessage.includes("wrong password") || lowerMessage.includes("incorrect password")) {
|
|
116
|
+
return "Invalid email or password. Please try again.";
|
|
117
|
+
}
|
|
118
|
+
if (lowerMessage.includes("invalid email") || lowerMessage.includes("password")) {
|
|
119
|
+
return message;
|
|
120
|
+
}
|
|
121
|
+
if (message === "The string did not match the expected pattern.") {
|
|
122
|
+
return "Please check your email address format and try again.";
|
|
123
|
+
}
|
|
124
|
+
if (lowerMessage.includes("rate limit") || lowerMessage.includes("too many requests") || lowerMessage.includes("try again later")) {
|
|
125
|
+
return "Too many attempts. Please wait a moment and try again.";
|
|
126
|
+
}
|
|
127
|
+
if (lowerMessage.includes("hook") || lowerMessage.includes("authorization") || lowerMessage.includes("token") || lowerMessage.includes("internal") || lowerMessage.includes("server") || lowerMessage.includes("database") || lowerMessage.includes("connection")) {
|
|
128
|
+
return "An error occurred. Please try again.";
|
|
129
|
+
}
|
|
130
|
+
return "An error occurred. Please try again.";
|
|
131
|
+
}
|
|
132
|
+
function sanitizeSignupError(message) {
|
|
133
|
+
const sanitized = sanitizeAuthError(message);
|
|
134
|
+
if (sanitized === "An error occurred. Please try again.") {
|
|
135
|
+
return "An error occurred during sign up. Please try again.";
|
|
136
|
+
}
|
|
137
|
+
return sanitized;
|
|
138
|
+
}
|
|
139
|
+
function buildOAuthRedirectTo(origin, nextPath) {
|
|
140
|
+
const siteUrl = origin.replace(/\/$/, "");
|
|
141
|
+
return `${siteUrl}/callback?next=${encodeURIComponent(nextPath)}`;
|
|
142
|
+
}
|
|
143
|
+
function buildOAuthSignInOptions(provider, redirectTo, options = {}) {
|
|
144
|
+
return {
|
|
145
|
+
redirectTo,
|
|
146
|
+
...provider === "azure" ? { scopes: "email" } : {},
|
|
147
|
+
...options
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function warnAboutSupabaseRedirectAllowlist(origin) {
|
|
151
|
+
const normalizedOrigin = origin.replace(/\/$/, "");
|
|
152
|
+
let hostname = "";
|
|
153
|
+
try {
|
|
154
|
+
hostname = new URL(normalizedOrigin).host;
|
|
155
|
+
} catch {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (!sharedUtils.isPreviewEnvironment(hostname)) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (warnedSupabaseRedirectOrigins.has(normalizedOrigin)) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
warnedSupabaseRedirectOrigins.add(normalizedOrigin);
|
|
165
|
+
console.warn(
|
|
166
|
+
`[identity] Supabase redirect allowlist reminder: add "${normalizedOrigin}/**" to Supabase Auth > URL Configuration > Additional Redirect URLs. The auth components already use the active origin for callbacks, but Supabase must allow that origin too or OAuth/email flows can bounce to another host.`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
function clearStalePKCEState() {
|
|
170
|
+
if (typeof window === "undefined") return;
|
|
171
|
+
try {
|
|
172
|
+
const keysToRemove = [];
|
|
173
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
174
|
+
const key = localStorage.key(i);
|
|
175
|
+
if (key?.includes("-code-verifier")) {
|
|
176
|
+
keysToRemove.push(key);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
for (const key of keysToRemove) {
|
|
180
|
+
localStorage.removeItem(key);
|
|
181
|
+
}
|
|
182
|
+
} catch {
|
|
183
|
+
}
|
|
184
|
+
}
|
|
58
185
|
var POLL_INTERVAL_MS = 6e4;
|
|
59
186
|
var MAX_POLL_COUNT = 60;
|
|
60
187
|
function EmailVerificationPending({
|
|
@@ -125,9 +252,19 @@ function EmailVerificationPending({
|
|
|
125
252
|
setResendStatus("sending");
|
|
126
253
|
try {
|
|
127
254
|
const supabase = createClient();
|
|
255
|
+
const safeRedirectTo = getSafeInternalRedirect(
|
|
256
|
+
redirectTo,
|
|
257
|
+
defaultAuthLinks.defaultRedirect
|
|
258
|
+
);
|
|
128
259
|
const { error } = await supabase.auth.resend({
|
|
129
260
|
type: "signup",
|
|
130
|
-
email
|
|
261
|
+
email,
|
|
262
|
+
options: {
|
|
263
|
+
emailRedirectTo: buildOAuthRedirectTo(
|
|
264
|
+
window.location.origin,
|
|
265
|
+
safeRedirectTo
|
|
266
|
+
)
|
|
267
|
+
}
|
|
131
268
|
});
|
|
132
269
|
if (error) {
|
|
133
270
|
setResendStatus("error");
|
|
@@ -247,17 +384,6 @@ function EmailVerificationPending({
|
|
|
247
384
|
}
|
|
248
385
|
);
|
|
249
386
|
}
|
|
250
|
-
|
|
251
|
-
// src/types.ts
|
|
252
|
-
var defaultAuthLinks = {
|
|
253
|
-
signIn: "/signin",
|
|
254
|
-
signUp: "/signup",
|
|
255
|
-
forgotPassword: "/forgot-password",
|
|
256
|
-
resetPassword: "/reset-password",
|
|
257
|
-
callback: "/callback",
|
|
258
|
-
home: "/",
|
|
259
|
-
defaultRedirect: "/dashboard"
|
|
260
|
-
};
|
|
261
387
|
function AuthHeader({
|
|
262
388
|
logo,
|
|
263
389
|
title,
|
|
@@ -279,82 +405,6 @@ function AuthHeader({
|
|
|
279
405
|
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: title })
|
|
280
406
|
] });
|
|
281
407
|
}
|
|
282
|
-
var warnedSupabaseRedirectOrigins = /* @__PURE__ */ new Set();
|
|
283
|
-
function sanitizeAuthError(message) {
|
|
284
|
-
const lowerMessage = message.toLowerCase();
|
|
285
|
-
if (lowerMessage.includes("user already registered") || lowerMessage.includes("already registered") || lowerMessage.includes("already exists") || lowerMessage.includes("email already") || lowerMessage.includes("duplicate")) {
|
|
286
|
-
return "An account with this email already exists. Please sign in instead.";
|
|
287
|
-
}
|
|
288
|
-
if (lowerMessage.includes("invalid login credentials") || lowerMessage.includes("invalid credentials") || lowerMessage.includes("wrong password") || lowerMessage.includes("incorrect password")) {
|
|
289
|
-
return "Invalid email or password. Please try again.";
|
|
290
|
-
}
|
|
291
|
-
if (lowerMessage.includes("invalid email") || lowerMessage.includes("password")) {
|
|
292
|
-
return message;
|
|
293
|
-
}
|
|
294
|
-
if (message === "The string did not match the expected pattern.") {
|
|
295
|
-
return "Please check your email address format and try again.";
|
|
296
|
-
}
|
|
297
|
-
if (lowerMessage.includes("rate limit") || lowerMessage.includes("too many requests") || lowerMessage.includes("try again later")) {
|
|
298
|
-
return "Too many attempts. Please wait a moment and try again.";
|
|
299
|
-
}
|
|
300
|
-
if (lowerMessage.includes("hook") || lowerMessage.includes("authorization") || lowerMessage.includes("token") || lowerMessage.includes("internal") || lowerMessage.includes("server") || lowerMessage.includes("database") || lowerMessage.includes("connection")) {
|
|
301
|
-
return "An error occurred. Please try again.";
|
|
302
|
-
}
|
|
303
|
-
return "An error occurred. Please try again.";
|
|
304
|
-
}
|
|
305
|
-
function sanitizeSignupError(message) {
|
|
306
|
-
const sanitized = sanitizeAuthError(message);
|
|
307
|
-
if (sanitized === "An error occurred. Please try again.") {
|
|
308
|
-
return "An error occurred during sign up. Please try again.";
|
|
309
|
-
}
|
|
310
|
-
return sanitized;
|
|
311
|
-
}
|
|
312
|
-
function buildOAuthRedirectTo(origin, nextPath) {
|
|
313
|
-
const siteUrl = origin.replace(/\/$/, "");
|
|
314
|
-
return `${siteUrl}/callback?next=${encodeURIComponent(nextPath)}`;
|
|
315
|
-
}
|
|
316
|
-
function buildOAuthSignInOptions(provider, redirectTo, options = {}) {
|
|
317
|
-
return {
|
|
318
|
-
redirectTo,
|
|
319
|
-
...provider === "azure" ? { scopes: "email" } : {},
|
|
320
|
-
...options
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
function warnAboutSupabaseRedirectAllowlist(origin) {
|
|
324
|
-
const normalizedOrigin = origin.replace(/\/$/, "");
|
|
325
|
-
let hostname = "";
|
|
326
|
-
try {
|
|
327
|
-
hostname = new URL(normalizedOrigin).host;
|
|
328
|
-
} catch {
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
if (!sharedUtils.isPreviewEnvironment(hostname)) {
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
if (warnedSupabaseRedirectOrigins.has(normalizedOrigin)) {
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
337
|
-
warnedSupabaseRedirectOrigins.add(normalizedOrigin);
|
|
338
|
-
console.warn(
|
|
339
|
-
`[identity] Supabase redirect allowlist reminder: add "${normalizedOrigin}/**" to Supabase Auth > URL Configuration > Additional Redirect URLs. The auth components already use the active origin for callbacks, but Supabase must allow that origin too or OAuth/email flows can bounce to another host.`
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
function clearStalePKCEState() {
|
|
343
|
-
if (typeof window === "undefined") return;
|
|
344
|
-
try {
|
|
345
|
-
const keysToRemove = [];
|
|
346
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
347
|
-
const key = localStorage.key(i);
|
|
348
|
-
if (key?.includes("-code-verifier")) {
|
|
349
|
-
keysToRemove.push(key);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
for (const key of keysToRemove) {
|
|
353
|
-
localStorage.removeItem(key);
|
|
354
|
-
}
|
|
355
|
-
} catch {
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
408
|
function ForgotPasswordForm({
|
|
359
409
|
createClient,
|
|
360
410
|
logo,
|
|
@@ -1095,7 +1145,14 @@ function SigninForm({
|
|
|
1095
1145
|
const oauthTimeoutRef = react.useRef(null);
|
|
1096
1146
|
const router = navigation.useRouter();
|
|
1097
1147
|
const searchParams = navigation.useSearchParams();
|
|
1098
|
-
const
|
|
1148
|
+
const defaultRedirect = getSafeInternalRedirect(
|
|
1149
|
+
mergedLinks.defaultRedirect,
|
|
1150
|
+
defaultAuthLinks.defaultRedirect
|
|
1151
|
+
);
|
|
1152
|
+
const redirectTo = getSafeInternalRedirect(
|
|
1153
|
+
searchParams.get("redirect"),
|
|
1154
|
+
defaultRedirect
|
|
1155
|
+
);
|
|
1099
1156
|
const message = searchParams.get("message");
|
|
1100
1157
|
const mfaRequired = searchParams.get("mfa") === "required";
|
|
1101
1158
|
const [showMFA, setShowMFA] = react.useState(mfaRequired);
|
|
@@ -1267,7 +1324,7 @@ function SigninForm({
|
|
|
1267
1324
|
} else {
|
|
1268
1325
|
onAuthEvent?.onSignInSuccess?.(userId, email.trim(), false);
|
|
1269
1326
|
recordSignIn("email", email.trim());
|
|
1270
|
-
|
|
1327
|
+
hardRedirect(redirectTo);
|
|
1271
1328
|
}
|
|
1272
1329
|
} catch (err) {
|
|
1273
1330
|
logError(err, {
|
|
@@ -1427,10 +1484,13 @@ function SigninForm({
|
|
|
1427
1484
|
}
|
|
1428
1485
|
);
|
|
1429
1486
|
}
|
|
1487
|
+
var EMPTY_CONSENT_ITEMS = [];
|
|
1430
1488
|
function SignupForm({
|
|
1431
1489
|
createClient,
|
|
1432
1490
|
logo,
|
|
1433
1491
|
appName = "your account",
|
|
1492
|
+
title,
|
|
1493
|
+
initialEmail = "",
|
|
1434
1494
|
termsUrl = "/terms",
|
|
1435
1495
|
privacyUrl = "/privacy",
|
|
1436
1496
|
links = {},
|
|
@@ -1442,7 +1502,7 @@ function SignupForm({
|
|
|
1442
1502
|
oauthIconMonochromeOnHover = false,
|
|
1443
1503
|
className,
|
|
1444
1504
|
onAuthEvent,
|
|
1445
|
-
consentItems =
|
|
1505
|
+
consentItems = EMPTY_CONSENT_ITEMS,
|
|
1446
1506
|
consentPosition = "above",
|
|
1447
1507
|
consentSize = "default",
|
|
1448
1508
|
showHomeLink = true,
|
|
@@ -1452,7 +1512,11 @@ function SignupForm({
|
|
|
1452
1512
|
const Link = LinkComponent;
|
|
1453
1513
|
const Image = ImageComponent;
|
|
1454
1514
|
const router = navigation.useRouter();
|
|
1455
|
-
const
|
|
1515
|
+
const redirectTo = getSafeInternalRedirect(
|
|
1516
|
+
mergedLinks.defaultRedirect,
|
|
1517
|
+
defaultAuthLinks.defaultRedirect
|
|
1518
|
+
);
|
|
1519
|
+
const [email, setEmail] = react.useState(initialEmail);
|
|
1456
1520
|
const [emailTouched, setEmailTouched] = react.useState(false);
|
|
1457
1521
|
const [password, setPassword] = react.useState("");
|
|
1458
1522
|
const [confirmPassword, setConfirmPassword] = react.useState("");
|
|
@@ -1517,7 +1581,7 @@ function SignupForm({
|
|
|
1517
1581
|
provider,
|
|
1518
1582
|
options: buildOAuthSignInOptions(
|
|
1519
1583
|
provider,
|
|
1520
|
-
buildOAuthRedirectTo(siteUrl,
|
|
1584
|
+
buildOAuthRedirectTo(siteUrl, redirectTo)
|
|
1521
1585
|
)
|
|
1522
1586
|
});
|
|
1523
1587
|
if (error) {
|
|
@@ -1552,7 +1616,7 @@ function SignupForm({
|
|
|
1552
1616
|
email: email.trim(),
|
|
1553
1617
|
password,
|
|
1554
1618
|
options: {
|
|
1555
|
-
emailRedirectTo:
|
|
1619
|
+
emailRedirectTo: buildOAuthRedirectTo(siteUrl, redirectTo),
|
|
1556
1620
|
// Pass all consent values as user metadata
|
|
1557
1621
|
data: consentState
|
|
1558
1622
|
}
|
|
@@ -1570,7 +1634,7 @@ function SignupForm({
|
|
|
1570
1634
|
} else if (data.user) {
|
|
1571
1635
|
const userId = data.user.id;
|
|
1572
1636
|
onAuthEvent?.onSignUpSuccess?.(userId, email.trim());
|
|
1573
|
-
router.push(
|
|
1637
|
+
router.push(redirectTo);
|
|
1574
1638
|
router.refresh();
|
|
1575
1639
|
} else {
|
|
1576
1640
|
onAuthEvent?.onSignUpFailure?.(email.trim(), "no_user_data_in_response");
|
|
@@ -1586,7 +1650,7 @@ function SignupForm({
|
|
|
1586
1650
|
logo,
|
|
1587
1651
|
ImageComponent: Image,
|
|
1588
1652
|
createClient,
|
|
1589
|
-
redirectTo
|
|
1653
|
+
redirectTo,
|
|
1590
1654
|
className
|
|
1591
1655
|
}
|
|
1592
1656
|
);
|
|
@@ -1608,7 +1672,7 @@ function SignupForm({
|
|
|
1608
1672
|
AuthHeader,
|
|
1609
1673
|
{
|
|
1610
1674
|
logo: showLogo !== false ? logo : void 0,
|
|
1611
|
-
title: `Sign up for ${appName}`,
|
|
1675
|
+
title: title ?? `Sign up for ${appName}`,
|
|
1612
1676
|
ImageComponent: Image
|
|
1613
1677
|
}
|
|
1614
1678
|
),
|
|
@@ -3722,6 +3786,7 @@ function useVerificationFlow({
|
|
|
3722
3786
|
};
|
|
3723
3787
|
}
|
|
3724
3788
|
function EmailSection({
|
|
3789
|
+
changeButtonPresentation = "label",
|
|
3725
3790
|
createClient,
|
|
3726
3791
|
userId: _userId,
|
|
3727
3792
|
email,
|
|
@@ -3749,6 +3814,26 @@ function EmailSection({
|
|
|
3749
3814
|
const handleStartChange = () => {
|
|
3750
3815
|
openEntryStep();
|
|
3751
3816
|
};
|
|
3817
|
+
const changeButton = changeButtonPresentation === "icon" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3818
|
+
primitives.Button,
|
|
3819
|
+
{
|
|
3820
|
+
"aria-label": "Change email",
|
|
3821
|
+
onClick: handleStartChange,
|
|
3822
|
+
size: "icon-sm",
|
|
3823
|
+
type: "button",
|
|
3824
|
+
variant: "outline",
|
|
3825
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PencilLine, { "aria-hidden": "true", className: "size-4" })
|
|
3826
|
+
}
|
|
3827
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
3828
|
+
primitives.Button,
|
|
3829
|
+
{
|
|
3830
|
+
onClick: handleStartChange,
|
|
3831
|
+
size: "sm",
|
|
3832
|
+
type: "button",
|
|
3833
|
+
variant: "outline",
|
|
3834
|
+
children: "Change"
|
|
3835
|
+
}
|
|
3836
|
+
);
|
|
3752
3837
|
const handleSendCode = async () => {
|
|
3753
3838
|
if (!newEmail || !newEmail.includes("@")) {
|
|
3754
3839
|
setError("Please enter a valid email address");
|
|
@@ -3828,16 +3913,10 @@ function EmailSection({
|
|
|
3828
3913
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: email })
|
|
3829
3914
|
] })
|
|
3830
3915
|
] }),
|
|
3831
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3832
|
-
primitives.
|
|
3833
|
-
{
|
|
3834
|
-
|
|
3835
|
-
variant: "outline",
|
|
3836
|
-
size: "sm",
|
|
3837
|
-
onClick: handleStartChange,
|
|
3838
|
-
children: "Change"
|
|
3839
|
-
}
|
|
3840
|
-
),
|
|
3916
|
+
changeButtonPresentation === "icon" ? /* @__PURE__ */ jsxRuntime.jsx(primitives.TooltipProvider, { delayDuration: 300, children: /* @__PURE__ */ jsxRuntime.jsxs(primitives.Tooltip, { children: [
|
|
3917
|
+
/* @__PURE__ */ jsxRuntime.jsx(primitives.TooltipTrigger, { asChild: true, children: changeButton }),
|
|
3918
|
+
/* @__PURE__ */ jsxRuntime.jsx(primitives.TooltipContent, { side: "top", children: "Change email" })
|
|
3919
|
+
] }) }) : changeButton,
|
|
3841
3920
|
/* @__PURE__ */ jsxRuntime.jsx(primitives.Dialog, { open: dialogOpen, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(primitives.DialogContent, { className: "sm:max-w-md", children: [
|
|
3842
3921
|
step === "enter-email" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3843
3922
|
/* @__PURE__ */ jsxRuntime.jsxs(primitives.DialogHeader, { children: [
|
|
@@ -5535,12 +5614,6 @@ var EMAIL_OTP_TYPES = [
|
|
|
5535
5614
|
"email_change",
|
|
5536
5615
|
"email"
|
|
5537
5616
|
];
|
|
5538
|
-
function getSafeRedirectPath(candidate, fallback) {
|
|
5539
|
-
if (candidate?.startsWith("/") && !candidate.startsWith("//")) {
|
|
5540
|
-
return candidate;
|
|
5541
|
-
}
|
|
5542
|
-
return fallback;
|
|
5543
|
-
}
|
|
5544
5617
|
function getEmailOtpType(value) {
|
|
5545
5618
|
if (!value) {
|
|
5546
5619
|
return null;
|
|
@@ -5631,8 +5704,9 @@ function createRedirectResponse(target, origin) {
|
|
|
5631
5704
|
if (target instanceof URL) {
|
|
5632
5705
|
return server.NextResponse.redirect(target.toString());
|
|
5633
5706
|
}
|
|
5634
|
-
|
|
5635
|
-
|
|
5707
|
+
const safeTarget = getSafeInternalRedirect(target, null);
|
|
5708
|
+
if (safeTarget) {
|
|
5709
|
+
return server.NextResponse.redirect(`${origin}${safeTarget}`);
|
|
5636
5710
|
}
|
|
5637
5711
|
return null;
|
|
5638
5712
|
}
|
|
@@ -5694,15 +5768,20 @@ function createAuthCallbackHandler({
|
|
|
5694
5768
|
isLinkingFlow = isDefaultLinkingFlow,
|
|
5695
5769
|
resolveLinkingErrorMessage
|
|
5696
5770
|
}) {
|
|
5771
|
+
const safeDefaultRedirect = getSafeInternalRedirect(
|
|
5772
|
+
defaultRedirect,
|
|
5773
|
+
"/dashboard"
|
|
5774
|
+
);
|
|
5775
|
+
const safeSignInPath = getSafeInternalRedirect(signInPath, "/signin");
|
|
5697
5776
|
return async function GET(request) {
|
|
5698
5777
|
const requestUrl = new URL(request.url);
|
|
5699
5778
|
const code = requestUrl.searchParams.get("code");
|
|
5700
5779
|
const tokenHash = requestUrl.searchParams.get("token_hash");
|
|
5701
5780
|
const rawOtpType = requestUrl.searchParams.get("type");
|
|
5702
5781
|
const otpType = getEmailOtpType(rawOtpType);
|
|
5703
|
-
const next =
|
|
5782
|
+
const next = getSafeInternalRedirect(
|
|
5704
5783
|
requestUrl.searchParams.get("next") ?? requestUrl.searchParams.get("redirect_to"),
|
|
5705
|
-
|
|
5784
|
+
safeDefaultRedirect
|
|
5706
5785
|
);
|
|
5707
5786
|
const origin = requestUrl.origin;
|
|
5708
5787
|
const error = requestUrl.searchParams.get("error");
|
|
@@ -5724,7 +5803,7 @@ function createAuthCallbackHandler({
|
|
|
5724
5803
|
return linkingErrorRedirect;
|
|
5725
5804
|
}
|
|
5726
5805
|
return server.NextResponse.redirect(
|
|
5727
|
-
`${origin}${
|
|
5806
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent(message)}`
|
|
5728
5807
|
);
|
|
5729
5808
|
}
|
|
5730
5809
|
if (code) {
|
|
@@ -5739,7 +5818,7 @@ function createAuthCallbackHandler({
|
|
|
5739
5818
|
flow: "oauth",
|
|
5740
5819
|
postAuthHook,
|
|
5741
5820
|
postAuthHookErrorMode,
|
|
5742
|
-
signInPath,
|
|
5821
|
+
signInPath: safeSignInPath,
|
|
5743
5822
|
resolveSuccessRedirect
|
|
5744
5823
|
});
|
|
5745
5824
|
}
|
|
@@ -5759,13 +5838,13 @@ function createAuthCallbackHandler({
|
|
|
5759
5838
|
return linkingErrorRedirect;
|
|
5760
5839
|
}
|
|
5761
5840
|
return server.NextResponse.redirect(
|
|
5762
|
-
`${origin}${
|
|
5841
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent(errorMessage)}`
|
|
5763
5842
|
);
|
|
5764
5843
|
}
|
|
5765
5844
|
if (tokenHash || rawOtpType) {
|
|
5766
5845
|
if (!tokenHash || !otpType) {
|
|
5767
5846
|
return server.NextResponse.redirect(
|
|
5768
|
-
`${origin}${
|
|
5847
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent("Invalid verification link")}`
|
|
5769
5848
|
);
|
|
5770
5849
|
}
|
|
5771
5850
|
const supabase = await createClient();
|
|
@@ -5783,16 +5862,16 @@ function createAuthCallbackHandler({
|
|
|
5783
5862
|
otpType,
|
|
5784
5863
|
postAuthHook,
|
|
5785
5864
|
postAuthHookErrorMode,
|
|
5786
|
-
signInPath,
|
|
5865
|
+
signInPath: safeSignInPath,
|
|
5787
5866
|
resolveSuccessRedirect
|
|
5788
5867
|
});
|
|
5789
5868
|
}
|
|
5790
5869
|
return server.NextResponse.redirect(
|
|
5791
|
-
`${origin}${
|
|
5870
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent(verifyError.message)}`
|
|
5792
5871
|
);
|
|
5793
5872
|
}
|
|
5794
5873
|
return server.NextResponse.redirect(
|
|
5795
|
-
`${origin}${
|
|
5874
|
+
`${origin}${safeSignInPath}?error=No authorization code received`
|
|
5796
5875
|
);
|
|
5797
5876
|
};
|
|
5798
5877
|
}
|