@caffeinebounce/identity 0.12.1 → 0.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +178 -114
- package/dist/index.mjs +178 -114
- 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
|
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
|
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
|
),
|
|
@@ -5535,12 +5599,6 @@ var EMAIL_OTP_TYPES = [
|
|
|
5535
5599
|
"email_change",
|
|
5536
5600
|
"email"
|
|
5537
5601
|
];
|
|
5538
|
-
function getSafeRedirectPath(candidate, fallback) {
|
|
5539
|
-
if (candidate?.startsWith("/") && !candidate.startsWith("//")) {
|
|
5540
|
-
return candidate;
|
|
5541
|
-
}
|
|
5542
|
-
return fallback;
|
|
5543
|
-
}
|
|
5544
5602
|
function getEmailOtpType(value) {
|
|
5545
5603
|
if (!value) {
|
|
5546
5604
|
return null;
|
|
@@ -5631,8 +5689,9 @@ function createRedirectResponse(target, origin) {
|
|
|
5631
5689
|
if (target instanceof URL) {
|
|
5632
5690
|
return server.NextResponse.redirect(target.toString());
|
|
5633
5691
|
}
|
|
5634
|
-
|
|
5635
|
-
|
|
5692
|
+
const safeTarget = getSafeInternalRedirect(target, null);
|
|
5693
|
+
if (safeTarget) {
|
|
5694
|
+
return server.NextResponse.redirect(`${origin}${safeTarget}`);
|
|
5636
5695
|
}
|
|
5637
5696
|
return null;
|
|
5638
5697
|
}
|
|
@@ -5694,15 +5753,20 @@ function createAuthCallbackHandler({
|
|
|
5694
5753
|
isLinkingFlow = isDefaultLinkingFlow,
|
|
5695
5754
|
resolveLinkingErrorMessage
|
|
5696
5755
|
}) {
|
|
5756
|
+
const safeDefaultRedirect = getSafeInternalRedirect(
|
|
5757
|
+
defaultRedirect,
|
|
5758
|
+
"/dashboard"
|
|
5759
|
+
);
|
|
5760
|
+
const safeSignInPath = getSafeInternalRedirect(signInPath, "/signin");
|
|
5697
5761
|
return async function GET(request) {
|
|
5698
5762
|
const requestUrl = new URL(request.url);
|
|
5699
5763
|
const code = requestUrl.searchParams.get("code");
|
|
5700
5764
|
const tokenHash = requestUrl.searchParams.get("token_hash");
|
|
5701
5765
|
const rawOtpType = requestUrl.searchParams.get("type");
|
|
5702
5766
|
const otpType = getEmailOtpType(rawOtpType);
|
|
5703
|
-
const next =
|
|
5767
|
+
const next = getSafeInternalRedirect(
|
|
5704
5768
|
requestUrl.searchParams.get("next") ?? requestUrl.searchParams.get("redirect_to"),
|
|
5705
|
-
|
|
5769
|
+
safeDefaultRedirect
|
|
5706
5770
|
);
|
|
5707
5771
|
const origin = requestUrl.origin;
|
|
5708
5772
|
const error = requestUrl.searchParams.get("error");
|
|
@@ -5724,7 +5788,7 @@ function createAuthCallbackHandler({
|
|
|
5724
5788
|
return linkingErrorRedirect;
|
|
5725
5789
|
}
|
|
5726
5790
|
return server.NextResponse.redirect(
|
|
5727
|
-
`${origin}${
|
|
5791
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent(message)}`
|
|
5728
5792
|
);
|
|
5729
5793
|
}
|
|
5730
5794
|
if (code) {
|
|
@@ -5739,7 +5803,7 @@ function createAuthCallbackHandler({
|
|
|
5739
5803
|
flow: "oauth",
|
|
5740
5804
|
postAuthHook,
|
|
5741
5805
|
postAuthHookErrorMode,
|
|
5742
|
-
signInPath,
|
|
5806
|
+
signInPath: safeSignInPath,
|
|
5743
5807
|
resolveSuccessRedirect
|
|
5744
5808
|
});
|
|
5745
5809
|
}
|
|
@@ -5759,13 +5823,13 @@ function createAuthCallbackHandler({
|
|
|
5759
5823
|
return linkingErrorRedirect;
|
|
5760
5824
|
}
|
|
5761
5825
|
return server.NextResponse.redirect(
|
|
5762
|
-
`${origin}${
|
|
5826
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent(errorMessage)}`
|
|
5763
5827
|
);
|
|
5764
5828
|
}
|
|
5765
5829
|
if (tokenHash || rawOtpType) {
|
|
5766
5830
|
if (!tokenHash || !otpType) {
|
|
5767
5831
|
return server.NextResponse.redirect(
|
|
5768
|
-
`${origin}${
|
|
5832
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent("Invalid verification link")}`
|
|
5769
5833
|
);
|
|
5770
5834
|
}
|
|
5771
5835
|
const supabase = await createClient();
|
|
@@ -5783,16 +5847,16 @@ function createAuthCallbackHandler({
|
|
|
5783
5847
|
otpType,
|
|
5784
5848
|
postAuthHook,
|
|
5785
5849
|
postAuthHookErrorMode,
|
|
5786
|
-
signInPath,
|
|
5850
|
+
signInPath: safeSignInPath,
|
|
5787
5851
|
resolveSuccessRedirect
|
|
5788
5852
|
});
|
|
5789
5853
|
}
|
|
5790
5854
|
return server.NextResponse.redirect(
|
|
5791
|
-
`${origin}${
|
|
5855
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent(verifyError.message)}`
|
|
5792
5856
|
);
|
|
5793
5857
|
}
|
|
5794
5858
|
return server.NextResponse.redirect(
|
|
5795
|
-
`${origin}${
|
|
5859
|
+
`${origin}${safeSignInPath}?error=No authorization code received`
|
|
5796
5860
|
);
|
|
5797
5861
|
};
|
|
5798
5862
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -14,6 +14,17 @@ import { REGEXP_ONLY_DIGITS } from 'input-otp';
|
|
|
14
14
|
import { NextResponse } from 'next/server';
|
|
15
15
|
|
|
16
16
|
// src/components/auth/EmailVerificationPending.tsx
|
|
17
|
+
|
|
18
|
+
// src/types.ts
|
|
19
|
+
var defaultAuthLinks = {
|
|
20
|
+
signIn: "/signin",
|
|
21
|
+
signUp: "/signup",
|
|
22
|
+
forgotPassword: "/forgot-password",
|
|
23
|
+
resetPassword: "/reset-password",
|
|
24
|
+
callback: "/callback",
|
|
25
|
+
home: "/",
|
|
26
|
+
defaultRedirect: "/dashboard"
|
|
27
|
+
};
|
|
17
28
|
function AuthFormLayout({
|
|
18
29
|
children,
|
|
19
30
|
footer,
|
|
@@ -50,6 +61,122 @@ function AuthFormLayout({
|
|
|
50
61
|
] }) })
|
|
51
62
|
] });
|
|
52
63
|
}
|
|
64
|
+
|
|
65
|
+
// src/utils/redirect.ts
|
|
66
|
+
var INTERNAL_REDIRECT_BASE = new URL("https://identity.invalid");
|
|
67
|
+
var ENCODED_PATH_SEPARATOR = /%(?:2f|5c)/i;
|
|
68
|
+
function hasControlCharacter(value) {
|
|
69
|
+
return Array.from(value).some((character) => {
|
|
70
|
+
const code = character.charCodeAt(0);
|
|
71
|
+
return code <= 31 || code === 127;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function isStrictInternalRedirect(candidate) {
|
|
75
|
+
if (!candidate?.startsWith("/") || hasControlCharacter(candidate)) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
const pathEnd = candidate.search(/[?#]/);
|
|
79
|
+
const pathname = pathEnd === -1 ? candidate : candidate.slice(0, pathEnd);
|
|
80
|
+
if (pathname.includes("\\") || ENCODED_PATH_SEPARATOR.test(pathname)) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const target = new URL(candidate, INTERNAL_REDIRECT_BASE);
|
|
85
|
+
return target.origin === INTERNAL_REDIRECT_BASE.origin;
|
|
86
|
+
} catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function getSafeInternalRedirect(candidate, fallback) {
|
|
91
|
+
if (isStrictInternalRedirect(candidate)) {
|
|
92
|
+
return candidate;
|
|
93
|
+
}
|
|
94
|
+
if (fallback === null) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
return isStrictInternalRedirect(fallback) ? fallback : "/";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/components/auth/utils.ts
|
|
101
|
+
var warnedSupabaseRedirectOrigins = /* @__PURE__ */ new Set();
|
|
102
|
+
function hardRedirect(redirectTo) {
|
|
103
|
+
window.location.href = redirectTo;
|
|
104
|
+
}
|
|
105
|
+
function sanitizeAuthError(message) {
|
|
106
|
+
const lowerMessage = message.toLowerCase();
|
|
107
|
+
if (lowerMessage.includes("user already registered") || lowerMessage.includes("already registered") || lowerMessage.includes("already exists") || lowerMessage.includes("email already") || lowerMessage.includes("duplicate")) {
|
|
108
|
+
return "An account with this email already exists. Please sign in instead.";
|
|
109
|
+
}
|
|
110
|
+
if (lowerMessage.includes("invalid login credentials") || lowerMessage.includes("invalid credentials") || lowerMessage.includes("wrong password") || lowerMessage.includes("incorrect password")) {
|
|
111
|
+
return "Invalid email or password. Please try again.";
|
|
112
|
+
}
|
|
113
|
+
if (lowerMessage.includes("invalid email") || lowerMessage.includes("password")) {
|
|
114
|
+
return message;
|
|
115
|
+
}
|
|
116
|
+
if (message === "The string did not match the expected pattern.") {
|
|
117
|
+
return "Please check your email address format and try again.";
|
|
118
|
+
}
|
|
119
|
+
if (lowerMessage.includes("rate limit") || lowerMessage.includes("too many requests") || lowerMessage.includes("try again later")) {
|
|
120
|
+
return "Too many attempts. Please wait a moment and try again.";
|
|
121
|
+
}
|
|
122
|
+
if (lowerMessage.includes("hook") || lowerMessage.includes("authorization") || lowerMessage.includes("token") || lowerMessage.includes("internal") || lowerMessage.includes("server") || lowerMessage.includes("database") || lowerMessage.includes("connection")) {
|
|
123
|
+
return "An error occurred. Please try again.";
|
|
124
|
+
}
|
|
125
|
+
return "An error occurred. Please try again.";
|
|
126
|
+
}
|
|
127
|
+
function sanitizeSignupError(message) {
|
|
128
|
+
const sanitized = sanitizeAuthError(message);
|
|
129
|
+
if (sanitized === "An error occurred. Please try again.") {
|
|
130
|
+
return "An error occurred during sign up. Please try again.";
|
|
131
|
+
}
|
|
132
|
+
return sanitized;
|
|
133
|
+
}
|
|
134
|
+
function buildOAuthRedirectTo(origin, nextPath) {
|
|
135
|
+
const siteUrl = origin.replace(/\/$/, "");
|
|
136
|
+
return `${siteUrl}/callback?next=${encodeURIComponent(nextPath)}`;
|
|
137
|
+
}
|
|
138
|
+
function buildOAuthSignInOptions(provider, redirectTo, options = {}) {
|
|
139
|
+
return {
|
|
140
|
+
redirectTo,
|
|
141
|
+
...provider === "azure" ? { scopes: "email" } : {},
|
|
142
|
+
...options
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function warnAboutSupabaseRedirectAllowlist(origin) {
|
|
146
|
+
const normalizedOrigin = origin.replace(/\/$/, "");
|
|
147
|
+
let hostname = "";
|
|
148
|
+
try {
|
|
149
|
+
hostname = new URL(normalizedOrigin).host;
|
|
150
|
+
} catch {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (!isPreviewEnvironment(hostname)) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (warnedSupabaseRedirectOrigins.has(normalizedOrigin)) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
warnedSupabaseRedirectOrigins.add(normalizedOrigin);
|
|
160
|
+
console.warn(
|
|
161
|
+
`[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.`
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
function clearStalePKCEState() {
|
|
165
|
+
if (typeof window === "undefined") return;
|
|
166
|
+
try {
|
|
167
|
+
const keysToRemove = [];
|
|
168
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
169
|
+
const key = localStorage.key(i);
|
|
170
|
+
if (key?.includes("-code-verifier")) {
|
|
171
|
+
keysToRemove.push(key);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
for (const key of keysToRemove) {
|
|
175
|
+
localStorage.removeItem(key);
|
|
176
|
+
}
|
|
177
|
+
} catch {
|
|
178
|
+
}
|
|
179
|
+
}
|
|
53
180
|
var POLL_INTERVAL_MS = 6e4;
|
|
54
181
|
var MAX_POLL_COUNT = 60;
|
|
55
182
|
function EmailVerificationPending({
|
|
@@ -120,9 +247,19 @@ function EmailVerificationPending({
|
|
|
120
247
|
setResendStatus("sending");
|
|
121
248
|
try {
|
|
122
249
|
const supabase = createClient();
|
|
250
|
+
const safeRedirectTo = getSafeInternalRedirect(
|
|
251
|
+
redirectTo,
|
|
252
|
+
defaultAuthLinks.defaultRedirect
|
|
253
|
+
);
|
|
123
254
|
const { error } = await supabase.auth.resend({
|
|
124
255
|
type: "signup",
|
|
125
|
-
email
|
|
256
|
+
email,
|
|
257
|
+
options: {
|
|
258
|
+
emailRedirectTo: buildOAuthRedirectTo(
|
|
259
|
+
window.location.origin,
|
|
260
|
+
safeRedirectTo
|
|
261
|
+
)
|
|
262
|
+
}
|
|
126
263
|
});
|
|
127
264
|
if (error) {
|
|
128
265
|
setResendStatus("error");
|
|
@@ -242,17 +379,6 @@ function EmailVerificationPending({
|
|
|
242
379
|
}
|
|
243
380
|
);
|
|
244
381
|
}
|
|
245
|
-
|
|
246
|
-
// src/types.ts
|
|
247
|
-
var defaultAuthLinks = {
|
|
248
|
-
signIn: "/signin",
|
|
249
|
-
signUp: "/signup",
|
|
250
|
-
forgotPassword: "/forgot-password",
|
|
251
|
-
resetPassword: "/reset-password",
|
|
252
|
-
callback: "/callback",
|
|
253
|
-
home: "/",
|
|
254
|
-
defaultRedirect: "/dashboard"
|
|
255
|
-
};
|
|
256
382
|
function AuthHeader({
|
|
257
383
|
logo,
|
|
258
384
|
title,
|
|
@@ -274,82 +400,6 @@ function AuthHeader({
|
|
|
274
400
|
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: title })
|
|
275
401
|
] });
|
|
276
402
|
}
|
|
277
|
-
var warnedSupabaseRedirectOrigins = /* @__PURE__ */ new Set();
|
|
278
|
-
function sanitizeAuthError(message) {
|
|
279
|
-
const lowerMessage = message.toLowerCase();
|
|
280
|
-
if (lowerMessage.includes("user already registered") || lowerMessage.includes("already registered") || lowerMessage.includes("already exists") || lowerMessage.includes("email already") || lowerMessage.includes("duplicate")) {
|
|
281
|
-
return "An account with this email already exists. Please sign in instead.";
|
|
282
|
-
}
|
|
283
|
-
if (lowerMessage.includes("invalid login credentials") || lowerMessage.includes("invalid credentials") || lowerMessage.includes("wrong password") || lowerMessage.includes("incorrect password")) {
|
|
284
|
-
return "Invalid email or password. Please try again.";
|
|
285
|
-
}
|
|
286
|
-
if (lowerMessage.includes("invalid email") || lowerMessage.includes("password")) {
|
|
287
|
-
return message;
|
|
288
|
-
}
|
|
289
|
-
if (message === "The string did not match the expected pattern.") {
|
|
290
|
-
return "Please check your email address format and try again.";
|
|
291
|
-
}
|
|
292
|
-
if (lowerMessage.includes("rate limit") || lowerMessage.includes("too many requests") || lowerMessage.includes("try again later")) {
|
|
293
|
-
return "Too many attempts. Please wait a moment and try again.";
|
|
294
|
-
}
|
|
295
|
-
if (lowerMessage.includes("hook") || lowerMessage.includes("authorization") || lowerMessage.includes("token") || lowerMessage.includes("internal") || lowerMessage.includes("server") || lowerMessage.includes("database") || lowerMessage.includes("connection")) {
|
|
296
|
-
return "An error occurred. Please try again.";
|
|
297
|
-
}
|
|
298
|
-
return "An error occurred. Please try again.";
|
|
299
|
-
}
|
|
300
|
-
function sanitizeSignupError(message) {
|
|
301
|
-
const sanitized = sanitizeAuthError(message);
|
|
302
|
-
if (sanitized === "An error occurred. Please try again.") {
|
|
303
|
-
return "An error occurred during sign up. Please try again.";
|
|
304
|
-
}
|
|
305
|
-
return sanitized;
|
|
306
|
-
}
|
|
307
|
-
function buildOAuthRedirectTo(origin, nextPath) {
|
|
308
|
-
const siteUrl = origin.replace(/\/$/, "");
|
|
309
|
-
return `${siteUrl}/callback?next=${encodeURIComponent(nextPath)}`;
|
|
310
|
-
}
|
|
311
|
-
function buildOAuthSignInOptions(provider, redirectTo, options = {}) {
|
|
312
|
-
return {
|
|
313
|
-
redirectTo,
|
|
314
|
-
...provider === "azure" ? { scopes: "email" } : {},
|
|
315
|
-
...options
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
function warnAboutSupabaseRedirectAllowlist(origin) {
|
|
319
|
-
const normalizedOrigin = origin.replace(/\/$/, "");
|
|
320
|
-
let hostname = "";
|
|
321
|
-
try {
|
|
322
|
-
hostname = new URL(normalizedOrigin).host;
|
|
323
|
-
} catch {
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
if (!isPreviewEnvironment(hostname)) {
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
if (warnedSupabaseRedirectOrigins.has(normalizedOrigin)) {
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
warnedSupabaseRedirectOrigins.add(normalizedOrigin);
|
|
333
|
-
console.warn(
|
|
334
|
-
`[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.`
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
function clearStalePKCEState() {
|
|
338
|
-
if (typeof window === "undefined") return;
|
|
339
|
-
try {
|
|
340
|
-
const keysToRemove = [];
|
|
341
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
342
|
-
const key = localStorage.key(i);
|
|
343
|
-
if (key?.includes("-code-verifier")) {
|
|
344
|
-
keysToRemove.push(key);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
for (const key of keysToRemove) {
|
|
348
|
-
localStorage.removeItem(key);
|
|
349
|
-
}
|
|
350
|
-
} catch {
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
403
|
function ForgotPasswordForm({
|
|
354
404
|
createClient,
|
|
355
405
|
logo,
|
|
@@ -1090,7 +1140,14 @@ function SigninForm({
|
|
|
1090
1140
|
const oauthTimeoutRef = useRef(null);
|
|
1091
1141
|
const router = useRouter();
|
|
1092
1142
|
const searchParams = useSearchParams();
|
|
1093
|
-
const
|
|
1143
|
+
const defaultRedirect = getSafeInternalRedirect(
|
|
1144
|
+
mergedLinks.defaultRedirect,
|
|
1145
|
+
defaultAuthLinks.defaultRedirect
|
|
1146
|
+
);
|
|
1147
|
+
const redirectTo = getSafeInternalRedirect(
|
|
1148
|
+
searchParams.get("redirect"),
|
|
1149
|
+
defaultRedirect
|
|
1150
|
+
);
|
|
1094
1151
|
const message = searchParams.get("message");
|
|
1095
1152
|
const mfaRequired = searchParams.get("mfa") === "required";
|
|
1096
1153
|
const [showMFA, setShowMFA] = useState(mfaRequired);
|
|
@@ -1262,7 +1319,7 @@ function SigninForm({
|
|
|
1262
1319
|
} else {
|
|
1263
1320
|
onAuthEvent?.onSignInSuccess?.(userId, email.trim(), false);
|
|
1264
1321
|
recordSignIn("email", email.trim());
|
|
1265
|
-
|
|
1322
|
+
hardRedirect(redirectTo);
|
|
1266
1323
|
}
|
|
1267
1324
|
} catch (err) {
|
|
1268
1325
|
logError(err, {
|
|
@@ -1422,10 +1479,13 @@ function SigninForm({
|
|
|
1422
1479
|
}
|
|
1423
1480
|
);
|
|
1424
1481
|
}
|
|
1482
|
+
var EMPTY_CONSENT_ITEMS = [];
|
|
1425
1483
|
function SignupForm({
|
|
1426
1484
|
createClient,
|
|
1427
1485
|
logo,
|
|
1428
1486
|
appName = "your account",
|
|
1487
|
+
title,
|
|
1488
|
+
initialEmail = "",
|
|
1429
1489
|
termsUrl = "/terms",
|
|
1430
1490
|
privacyUrl = "/privacy",
|
|
1431
1491
|
links = {},
|
|
@@ -1437,7 +1497,7 @@ function SignupForm({
|
|
|
1437
1497
|
oauthIconMonochromeOnHover = false,
|
|
1438
1498
|
className,
|
|
1439
1499
|
onAuthEvent,
|
|
1440
|
-
consentItems =
|
|
1500
|
+
consentItems = EMPTY_CONSENT_ITEMS,
|
|
1441
1501
|
consentPosition = "above",
|
|
1442
1502
|
consentSize = "default",
|
|
1443
1503
|
showHomeLink = true,
|
|
@@ -1447,7 +1507,11 @@ function SignupForm({
|
|
|
1447
1507
|
const Link = LinkComponent;
|
|
1448
1508
|
const Image = ImageComponent;
|
|
1449
1509
|
const router = useRouter();
|
|
1450
|
-
const
|
|
1510
|
+
const redirectTo = getSafeInternalRedirect(
|
|
1511
|
+
mergedLinks.defaultRedirect,
|
|
1512
|
+
defaultAuthLinks.defaultRedirect
|
|
1513
|
+
);
|
|
1514
|
+
const [email, setEmail] = useState(initialEmail);
|
|
1451
1515
|
const [emailTouched, setEmailTouched] = useState(false);
|
|
1452
1516
|
const [password, setPassword] = useState("");
|
|
1453
1517
|
const [confirmPassword, setConfirmPassword] = useState("");
|
|
@@ -1512,7 +1576,7 @@ function SignupForm({
|
|
|
1512
1576
|
provider,
|
|
1513
1577
|
options: buildOAuthSignInOptions(
|
|
1514
1578
|
provider,
|
|
1515
|
-
buildOAuthRedirectTo(siteUrl,
|
|
1579
|
+
buildOAuthRedirectTo(siteUrl, redirectTo)
|
|
1516
1580
|
)
|
|
1517
1581
|
});
|
|
1518
1582
|
if (error) {
|
|
@@ -1547,7 +1611,7 @@ function SignupForm({
|
|
|
1547
1611
|
email: email.trim(),
|
|
1548
1612
|
password,
|
|
1549
1613
|
options: {
|
|
1550
|
-
emailRedirectTo:
|
|
1614
|
+
emailRedirectTo: buildOAuthRedirectTo(siteUrl, redirectTo),
|
|
1551
1615
|
// Pass all consent values as user metadata
|
|
1552
1616
|
data: consentState
|
|
1553
1617
|
}
|
|
@@ -1565,7 +1629,7 @@ function SignupForm({
|
|
|
1565
1629
|
} else if (data.user) {
|
|
1566
1630
|
const userId = data.user.id;
|
|
1567
1631
|
onAuthEvent?.onSignUpSuccess?.(userId, email.trim());
|
|
1568
|
-
router.push(
|
|
1632
|
+
router.push(redirectTo);
|
|
1569
1633
|
router.refresh();
|
|
1570
1634
|
} else {
|
|
1571
1635
|
onAuthEvent?.onSignUpFailure?.(email.trim(), "no_user_data_in_response");
|
|
@@ -1581,7 +1645,7 @@ function SignupForm({
|
|
|
1581
1645
|
logo,
|
|
1582
1646
|
ImageComponent: Image,
|
|
1583
1647
|
createClient,
|
|
1584
|
-
redirectTo
|
|
1648
|
+
redirectTo,
|
|
1585
1649
|
className
|
|
1586
1650
|
}
|
|
1587
1651
|
);
|
|
@@ -1603,7 +1667,7 @@ function SignupForm({
|
|
|
1603
1667
|
AuthHeader,
|
|
1604
1668
|
{
|
|
1605
1669
|
logo: showLogo !== false ? logo : void 0,
|
|
1606
|
-
title: `Sign up for ${appName}`,
|
|
1670
|
+
title: title ?? `Sign up for ${appName}`,
|
|
1607
1671
|
ImageComponent: Image
|
|
1608
1672
|
}
|
|
1609
1673
|
),
|
|
@@ -5530,12 +5594,6 @@ var EMAIL_OTP_TYPES = [
|
|
|
5530
5594
|
"email_change",
|
|
5531
5595
|
"email"
|
|
5532
5596
|
];
|
|
5533
|
-
function getSafeRedirectPath(candidate, fallback) {
|
|
5534
|
-
if (candidate?.startsWith("/") && !candidate.startsWith("//")) {
|
|
5535
|
-
return candidate;
|
|
5536
|
-
}
|
|
5537
|
-
return fallback;
|
|
5538
|
-
}
|
|
5539
5597
|
function getEmailOtpType(value) {
|
|
5540
5598
|
if (!value) {
|
|
5541
5599
|
return null;
|
|
@@ -5626,8 +5684,9 @@ function createRedirectResponse(target, origin) {
|
|
|
5626
5684
|
if (target instanceof URL) {
|
|
5627
5685
|
return NextResponse.redirect(target.toString());
|
|
5628
5686
|
}
|
|
5629
|
-
|
|
5630
|
-
|
|
5687
|
+
const safeTarget = getSafeInternalRedirect(target, null);
|
|
5688
|
+
if (safeTarget) {
|
|
5689
|
+
return NextResponse.redirect(`${origin}${safeTarget}`);
|
|
5631
5690
|
}
|
|
5632
5691
|
return null;
|
|
5633
5692
|
}
|
|
@@ -5689,15 +5748,20 @@ function createAuthCallbackHandler({
|
|
|
5689
5748
|
isLinkingFlow = isDefaultLinkingFlow,
|
|
5690
5749
|
resolveLinkingErrorMessage
|
|
5691
5750
|
}) {
|
|
5751
|
+
const safeDefaultRedirect = getSafeInternalRedirect(
|
|
5752
|
+
defaultRedirect,
|
|
5753
|
+
"/dashboard"
|
|
5754
|
+
);
|
|
5755
|
+
const safeSignInPath = getSafeInternalRedirect(signInPath, "/signin");
|
|
5692
5756
|
return async function GET(request) {
|
|
5693
5757
|
const requestUrl = new URL(request.url);
|
|
5694
5758
|
const code = requestUrl.searchParams.get("code");
|
|
5695
5759
|
const tokenHash = requestUrl.searchParams.get("token_hash");
|
|
5696
5760
|
const rawOtpType = requestUrl.searchParams.get("type");
|
|
5697
5761
|
const otpType = getEmailOtpType(rawOtpType);
|
|
5698
|
-
const next =
|
|
5762
|
+
const next = getSafeInternalRedirect(
|
|
5699
5763
|
requestUrl.searchParams.get("next") ?? requestUrl.searchParams.get("redirect_to"),
|
|
5700
|
-
|
|
5764
|
+
safeDefaultRedirect
|
|
5701
5765
|
);
|
|
5702
5766
|
const origin = requestUrl.origin;
|
|
5703
5767
|
const error = requestUrl.searchParams.get("error");
|
|
@@ -5719,7 +5783,7 @@ function createAuthCallbackHandler({
|
|
|
5719
5783
|
return linkingErrorRedirect;
|
|
5720
5784
|
}
|
|
5721
5785
|
return NextResponse.redirect(
|
|
5722
|
-
`${origin}${
|
|
5786
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent(message)}`
|
|
5723
5787
|
);
|
|
5724
5788
|
}
|
|
5725
5789
|
if (code) {
|
|
@@ -5734,7 +5798,7 @@ function createAuthCallbackHandler({
|
|
|
5734
5798
|
flow: "oauth",
|
|
5735
5799
|
postAuthHook,
|
|
5736
5800
|
postAuthHookErrorMode,
|
|
5737
|
-
signInPath,
|
|
5801
|
+
signInPath: safeSignInPath,
|
|
5738
5802
|
resolveSuccessRedirect
|
|
5739
5803
|
});
|
|
5740
5804
|
}
|
|
@@ -5754,13 +5818,13 @@ function createAuthCallbackHandler({
|
|
|
5754
5818
|
return linkingErrorRedirect;
|
|
5755
5819
|
}
|
|
5756
5820
|
return NextResponse.redirect(
|
|
5757
|
-
`${origin}${
|
|
5821
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent(errorMessage)}`
|
|
5758
5822
|
);
|
|
5759
5823
|
}
|
|
5760
5824
|
if (tokenHash || rawOtpType) {
|
|
5761
5825
|
if (!tokenHash || !otpType) {
|
|
5762
5826
|
return NextResponse.redirect(
|
|
5763
|
-
`${origin}${
|
|
5827
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent("Invalid verification link")}`
|
|
5764
5828
|
);
|
|
5765
5829
|
}
|
|
5766
5830
|
const supabase = await createClient();
|
|
@@ -5778,16 +5842,16 @@ function createAuthCallbackHandler({
|
|
|
5778
5842
|
otpType,
|
|
5779
5843
|
postAuthHook,
|
|
5780
5844
|
postAuthHookErrorMode,
|
|
5781
|
-
signInPath,
|
|
5845
|
+
signInPath: safeSignInPath,
|
|
5782
5846
|
resolveSuccessRedirect
|
|
5783
5847
|
});
|
|
5784
5848
|
}
|
|
5785
5849
|
return NextResponse.redirect(
|
|
5786
|
-
`${origin}${
|
|
5850
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent(verifyError.message)}`
|
|
5787
5851
|
);
|
|
5788
5852
|
}
|
|
5789
5853
|
return NextResponse.redirect(
|
|
5790
|
-
`${origin}${
|
|
5854
|
+
`${origin}${safeSignInPath}?error=No authorization code received`
|
|
5791
5855
|
);
|
|
5792
5856
|
};
|
|
5793
5857
|
}
|
package/dist/server.js
CHANGED
|
@@ -3,6 +3,43 @@
|
|
|
3
3
|
var server = require('next/server');
|
|
4
4
|
var sharedUtils = require('@caffeinebounce/shared-utils');
|
|
5
5
|
|
|
6
|
+
// src/handlers/callback.ts
|
|
7
|
+
|
|
8
|
+
// src/utils/redirect.ts
|
|
9
|
+
var INTERNAL_REDIRECT_BASE = new URL("https://identity.invalid");
|
|
10
|
+
var ENCODED_PATH_SEPARATOR = /%(?:2f|5c)/i;
|
|
11
|
+
function hasControlCharacter(value) {
|
|
12
|
+
return Array.from(value).some((character) => {
|
|
13
|
+
const code = character.charCodeAt(0);
|
|
14
|
+
return code <= 31 || code === 127;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function isStrictInternalRedirect(candidate) {
|
|
18
|
+
if (!candidate?.startsWith("/") || hasControlCharacter(candidate)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const pathEnd = candidate.search(/[?#]/);
|
|
22
|
+
const pathname = pathEnd === -1 ? candidate : candidate.slice(0, pathEnd);
|
|
23
|
+
if (pathname.includes("\\") || ENCODED_PATH_SEPARATOR.test(pathname)) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const target = new URL(candidate, INTERNAL_REDIRECT_BASE);
|
|
28
|
+
return target.origin === INTERNAL_REDIRECT_BASE.origin;
|
|
29
|
+
} catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function getSafeInternalRedirect(candidate, fallback) {
|
|
34
|
+
if (isStrictInternalRedirect(candidate)) {
|
|
35
|
+
return candidate;
|
|
36
|
+
}
|
|
37
|
+
if (fallback === null) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return isStrictInternalRedirect(fallback) ? fallback : "/";
|
|
41
|
+
}
|
|
42
|
+
|
|
6
43
|
// src/handlers/callback.ts
|
|
7
44
|
var POST_AUTH_HOOK_ERROR_MESSAGE = "Authentication completed, but setup failed. Please try again.";
|
|
8
45
|
var DEFAULT_LINKING_ACCOUNT_ERROR_MESSAGE = "This account is already connected to another account. Each external account can only be linked to one account.";
|
|
@@ -14,12 +51,6 @@ var EMAIL_OTP_TYPES = [
|
|
|
14
51
|
"email_change",
|
|
15
52
|
"email"
|
|
16
53
|
];
|
|
17
|
-
function getSafeRedirectPath(candidate, fallback) {
|
|
18
|
-
if (candidate?.startsWith("/") && !candidate.startsWith("//")) {
|
|
19
|
-
return candidate;
|
|
20
|
-
}
|
|
21
|
-
return fallback;
|
|
22
|
-
}
|
|
23
54
|
function getEmailOtpType(value) {
|
|
24
55
|
if (!value) {
|
|
25
56
|
return null;
|
|
@@ -110,8 +141,9 @@ function createRedirectResponse(target, origin) {
|
|
|
110
141
|
if (target instanceof URL) {
|
|
111
142
|
return server.NextResponse.redirect(target.toString());
|
|
112
143
|
}
|
|
113
|
-
|
|
114
|
-
|
|
144
|
+
const safeTarget = getSafeInternalRedirect(target, null);
|
|
145
|
+
if (safeTarget) {
|
|
146
|
+
return server.NextResponse.redirect(`${origin}${safeTarget}`);
|
|
115
147
|
}
|
|
116
148
|
return null;
|
|
117
149
|
}
|
|
@@ -173,15 +205,20 @@ function createAuthCallbackHandler({
|
|
|
173
205
|
isLinkingFlow = isDefaultLinkingFlow,
|
|
174
206
|
resolveLinkingErrorMessage
|
|
175
207
|
}) {
|
|
208
|
+
const safeDefaultRedirect = getSafeInternalRedirect(
|
|
209
|
+
defaultRedirect,
|
|
210
|
+
"/dashboard"
|
|
211
|
+
);
|
|
212
|
+
const safeSignInPath = getSafeInternalRedirect(signInPath, "/signin");
|
|
176
213
|
return async function GET(request) {
|
|
177
214
|
const requestUrl = new URL(request.url);
|
|
178
215
|
const code = requestUrl.searchParams.get("code");
|
|
179
216
|
const tokenHash = requestUrl.searchParams.get("token_hash");
|
|
180
217
|
const rawOtpType = requestUrl.searchParams.get("type");
|
|
181
218
|
const otpType = getEmailOtpType(rawOtpType);
|
|
182
|
-
const next =
|
|
219
|
+
const next = getSafeInternalRedirect(
|
|
183
220
|
requestUrl.searchParams.get("next") ?? requestUrl.searchParams.get("redirect_to"),
|
|
184
|
-
|
|
221
|
+
safeDefaultRedirect
|
|
185
222
|
);
|
|
186
223
|
const origin = requestUrl.origin;
|
|
187
224
|
const error = requestUrl.searchParams.get("error");
|
|
@@ -203,7 +240,7 @@ function createAuthCallbackHandler({
|
|
|
203
240
|
return linkingErrorRedirect;
|
|
204
241
|
}
|
|
205
242
|
return server.NextResponse.redirect(
|
|
206
|
-
`${origin}${
|
|
243
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent(message)}`
|
|
207
244
|
);
|
|
208
245
|
}
|
|
209
246
|
if (code) {
|
|
@@ -218,7 +255,7 @@ function createAuthCallbackHandler({
|
|
|
218
255
|
flow: "oauth",
|
|
219
256
|
postAuthHook,
|
|
220
257
|
postAuthHookErrorMode,
|
|
221
|
-
signInPath,
|
|
258
|
+
signInPath: safeSignInPath,
|
|
222
259
|
resolveSuccessRedirect
|
|
223
260
|
});
|
|
224
261
|
}
|
|
@@ -238,13 +275,13 @@ function createAuthCallbackHandler({
|
|
|
238
275
|
return linkingErrorRedirect;
|
|
239
276
|
}
|
|
240
277
|
return server.NextResponse.redirect(
|
|
241
|
-
`${origin}${
|
|
278
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent(errorMessage)}`
|
|
242
279
|
);
|
|
243
280
|
}
|
|
244
281
|
if (tokenHash || rawOtpType) {
|
|
245
282
|
if (!tokenHash || !otpType) {
|
|
246
283
|
return server.NextResponse.redirect(
|
|
247
|
-
`${origin}${
|
|
284
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent("Invalid verification link")}`
|
|
248
285
|
);
|
|
249
286
|
}
|
|
250
287
|
const supabase = await createClient();
|
|
@@ -262,16 +299,16 @@ function createAuthCallbackHandler({
|
|
|
262
299
|
otpType,
|
|
263
300
|
postAuthHook,
|
|
264
301
|
postAuthHookErrorMode,
|
|
265
|
-
signInPath,
|
|
302
|
+
signInPath: safeSignInPath,
|
|
266
303
|
resolveSuccessRedirect
|
|
267
304
|
});
|
|
268
305
|
}
|
|
269
306
|
return server.NextResponse.redirect(
|
|
270
|
-
`${origin}${
|
|
307
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent(verifyError.message)}`
|
|
271
308
|
);
|
|
272
309
|
}
|
|
273
310
|
return server.NextResponse.redirect(
|
|
274
|
-
`${origin}${
|
|
311
|
+
`${origin}${safeSignInPath}?error=No authorization code received`
|
|
275
312
|
);
|
|
276
313
|
};
|
|
277
314
|
}
|
package/dist/server.mjs
CHANGED
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server';
|
|
2
2
|
export { generateSecureToken, getClientIP, getGeolocationFromIP, hashString } from '@caffeinebounce/shared-utils';
|
|
3
3
|
|
|
4
|
+
// src/handlers/callback.ts
|
|
5
|
+
|
|
6
|
+
// src/utils/redirect.ts
|
|
7
|
+
var INTERNAL_REDIRECT_BASE = new URL("https://identity.invalid");
|
|
8
|
+
var ENCODED_PATH_SEPARATOR = /%(?:2f|5c)/i;
|
|
9
|
+
function hasControlCharacter(value) {
|
|
10
|
+
return Array.from(value).some((character) => {
|
|
11
|
+
const code = character.charCodeAt(0);
|
|
12
|
+
return code <= 31 || code === 127;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function isStrictInternalRedirect(candidate) {
|
|
16
|
+
if (!candidate?.startsWith("/") || hasControlCharacter(candidate)) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
const pathEnd = candidate.search(/[?#]/);
|
|
20
|
+
const pathname = pathEnd === -1 ? candidate : candidate.slice(0, pathEnd);
|
|
21
|
+
if (pathname.includes("\\") || ENCODED_PATH_SEPARATOR.test(pathname)) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const target = new URL(candidate, INTERNAL_REDIRECT_BASE);
|
|
26
|
+
return target.origin === INTERNAL_REDIRECT_BASE.origin;
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function getSafeInternalRedirect(candidate, fallback) {
|
|
32
|
+
if (isStrictInternalRedirect(candidate)) {
|
|
33
|
+
return candidate;
|
|
34
|
+
}
|
|
35
|
+
if (fallback === null) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return isStrictInternalRedirect(fallback) ? fallback : "/";
|
|
39
|
+
}
|
|
40
|
+
|
|
4
41
|
// src/handlers/callback.ts
|
|
5
42
|
var POST_AUTH_HOOK_ERROR_MESSAGE = "Authentication completed, but setup failed. Please try again.";
|
|
6
43
|
var DEFAULT_LINKING_ACCOUNT_ERROR_MESSAGE = "This account is already connected to another account. Each external account can only be linked to one account.";
|
|
@@ -12,12 +49,6 @@ var EMAIL_OTP_TYPES = [
|
|
|
12
49
|
"email_change",
|
|
13
50
|
"email"
|
|
14
51
|
];
|
|
15
|
-
function getSafeRedirectPath(candidate, fallback) {
|
|
16
|
-
if (candidate?.startsWith("/") && !candidate.startsWith("//")) {
|
|
17
|
-
return candidate;
|
|
18
|
-
}
|
|
19
|
-
return fallback;
|
|
20
|
-
}
|
|
21
52
|
function getEmailOtpType(value) {
|
|
22
53
|
if (!value) {
|
|
23
54
|
return null;
|
|
@@ -108,8 +139,9 @@ function createRedirectResponse(target, origin) {
|
|
|
108
139
|
if (target instanceof URL) {
|
|
109
140
|
return NextResponse.redirect(target.toString());
|
|
110
141
|
}
|
|
111
|
-
|
|
112
|
-
|
|
142
|
+
const safeTarget = getSafeInternalRedirect(target, null);
|
|
143
|
+
if (safeTarget) {
|
|
144
|
+
return NextResponse.redirect(`${origin}${safeTarget}`);
|
|
113
145
|
}
|
|
114
146
|
return null;
|
|
115
147
|
}
|
|
@@ -171,15 +203,20 @@ function createAuthCallbackHandler({
|
|
|
171
203
|
isLinkingFlow = isDefaultLinkingFlow,
|
|
172
204
|
resolveLinkingErrorMessage
|
|
173
205
|
}) {
|
|
206
|
+
const safeDefaultRedirect = getSafeInternalRedirect(
|
|
207
|
+
defaultRedirect,
|
|
208
|
+
"/dashboard"
|
|
209
|
+
);
|
|
210
|
+
const safeSignInPath = getSafeInternalRedirect(signInPath, "/signin");
|
|
174
211
|
return async function GET(request) {
|
|
175
212
|
const requestUrl = new URL(request.url);
|
|
176
213
|
const code = requestUrl.searchParams.get("code");
|
|
177
214
|
const tokenHash = requestUrl.searchParams.get("token_hash");
|
|
178
215
|
const rawOtpType = requestUrl.searchParams.get("type");
|
|
179
216
|
const otpType = getEmailOtpType(rawOtpType);
|
|
180
|
-
const next =
|
|
217
|
+
const next = getSafeInternalRedirect(
|
|
181
218
|
requestUrl.searchParams.get("next") ?? requestUrl.searchParams.get("redirect_to"),
|
|
182
|
-
|
|
219
|
+
safeDefaultRedirect
|
|
183
220
|
);
|
|
184
221
|
const origin = requestUrl.origin;
|
|
185
222
|
const error = requestUrl.searchParams.get("error");
|
|
@@ -201,7 +238,7 @@ function createAuthCallbackHandler({
|
|
|
201
238
|
return linkingErrorRedirect;
|
|
202
239
|
}
|
|
203
240
|
return NextResponse.redirect(
|
|
204
|
-
`${origin}${
|
|
241
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent(message)}`
|
|
205
242
|
);
|
|
206
243
|
}
|
|
207
244
|
if (code) {
|
|
@@ -216,7 +253,7 @@ function createAuthCallbackHandler({
|
|
|
216
253
|
flow: "oauth",
|
|
217
254
|
postAuthHook,
|
|
218
255
|
postAuthHookErrorMode,
|
|
219
|
-
signInPath,
|
|
256
|
+
signInPath: safeSignInPath,
|
|
220
257
|
resolveSuccessRedirect
|
|
221
258
|
});
|
|
222
259
|
}
|
|
@@ -236,13 +273,13 @@ function createAuthCallbackHandler({
|
|
|
236
273
|
return linkingErrorRedirect;
|
|
237
274
|
}
|
|
238
275
|
return NextResponse.redirect(
|
|
239
|
-
`${origin}${
|
|
276
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent(errorMessage)}`
|
|
240
277
|
);
|
|
241
278
|
}
|
|
242
279
|
if (tokenHash || rawOtpType) {
|
|
243
280
|
if (!tokenHash || !otpType) {
|
|
244
281
|
return NextResponse.redirect(
|
|
245
|
-
`${origin}${
|
|
282
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent("Invalid verification link")}`
|
|
246
283
|
);
|
|
247
284
|
}
|
|
248
285
|
const supabase = await createClient();
|
|
@@ -260,16 +297,16 @@ function createAuthCallbackHandler({
|
|
|
260
297
|
otpType,
|
|
261
298
|
postAuthHook,
|
|
262
299
|
postAuthHookErrorMode,
|
|
263
|
-
signInPath,
|
|
300
|
+
signInPath: safeSignInPath,
|
|
264
301
|
resolveSuccessRedirect
|
|
265
302
|
});
|
|
266
303
|
}
|
|
267
304
|
return NextResponse.redirect(
|
|
268
|
-
`${origin}${
|
|
305
|
+
`${origin}${safeSignInPath}?error=${encodeURIComponent(verifyError.message)}`
|
|
269
306
|
);
|
|
270
307
|
}
|
|
271
308
|
return NextResponse.redirect(
|
|
272
|
-
`${origin}${
|
|
309
|
+
`${origin}${safeSignInPath}?error=No authorization code received`
|
|
273
310
|
);
|
|
274
311
|
};
|
|
275
312
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@caffeinebounce/identity",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.2",
|
|
4
4
|
"description": "Authentication components and handlers for Caffeine Bounce projects",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@caffeinebounce/logger": "^0.10.0",
|
|
63
63
|
"@caffeinebounce/shared-utils": "^0.7.136",
|
|
64
|
-
"@caffeinebounce/ui": "^0.62.
|
|
64
|
+
"@caffeinebounce/ui": "^0.62.3",
|
|
65
65
|
"@supabase/ssr": "^0.8.0",
|
|
66
66
|
"@supabase/supabase-js": "^2.49.4",
|
|
67
67
|
"input-otp": "^1.4.2",
|