@sylphx/sdk 0.10.5 → 0.10.7
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.ts +10 -8
- package/dist/index.mjs +6 -3
- package/dist/index.mjs.map +1 -1
- package/dist/nextjs/index.d.ts +22 -0
- package/dist/nextjs/index.mjs +294 -14
- package/dist/nextjs/index.mjs.map +1 -1
- package/dist/react/index.d.ts +5 -1
- package/dist/react/index.mjs +34 -17
- package/dist/react/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/health/index.d.ts +0 -681
package/dist/nextjs/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { AuthTokensResponse } from '@sylphx/contract';
|
|
|
6
6
|
*
|
|
7
7
|
* ONE middleware function handles EVERYTHING:
|
|
8
8
|
* - Auth routes (mounted automatically, zero manual API routes)
|
|
9
|
+
* - BaaS routes (same-origin proxy, no browser bearer-token exposure)
|
|
9
10
|
* - Token refresh (automatic, every request)
|
|
10
11
|
* - Route protection
|
|
11
12
|
* - Cookie management
|
|
@@ -63,15 +64,36 @@ interface SylphxMiddlewareConfig {
|
|
|
63
64
|
afterSignInUrl?: string;
|
|
64
65
|
/**
|
|
65
66
|
* Auth routes prefix. Routes are mounted at:
|
|
67
|
+
* - {prefix}/register — email/password registration handler
|
|
66
68
|
* - {prefix}/login — credentials login handler
|
|
69
|
+
* - {prefix}/verify-email — email verification handler
|
|
67
70
|
* - {prefix}/oauth-providers — enabled social login providers
|
|
68
71
|
* - {prefix}/oauth/authorize — social login start handler
|
|
69
72
|
* - {prefix}/callback — OAuth callback handler
|
|
73
|
+
* - {prefix}/passkey/options — passkey login challenge handler
|
|
74
|
+
* - {prefix}/passkey/authenticate — passkey login verification handler
|
|
75
|
+
* - {prefix}/verify-2fa — TOTP/backup-code verification handler
|
|
76
|
+
* - {prefix}/forgot-password — password reset email handler
|
|
77
|
+
* - {prefix}/reset-password — password reset verification handler
|
|
78
|
+
* - {prefix}/session — safe session metadata handler
|
|
70
79
|
* - {prefix}/signout — Sign out handler
|
|
71
80
|
*
|
|
72
81
|
* @default '/auth'
|
|
73
82
|
*/
|
|
74
83
|
authPrefix?: string;
|
|
84
|
+
/**
|
|
85
|
+
* Same-origin BaaS proxy prefix.
|
|
86
|
+
*
|
|
87
|
+
* Mounted routes are allowlisted SDK-owned BaaS surfaces such as
|
|
88
|
+
* `{prefix}/security/*` and `{prefix}/challenge/*`. The middleware converts
|
|
89
|
+
* the HttpOnly session cookie into an upstream bearer token, so browser code
|
|
90
|
+
* can use BaaS features without ever reading Platform access tokens.
|
|
91
|
+
*
|
|
92
|
+
* Set to `false` to disable the proxy.
|
|
93
|
+
*
|
|
94
|
+
* @default '/sylphx'
|
|
95
|
+
*/
|
|
96
|
+
baasPrefix?: string | false;
|
|
75
97
|
/**
|
|
76
98
|
* Enable debug logging.
|
|
77
99
|
* @default false
|
package/dist/nextjs/index.mjs
CHANGED
|
@@ -455,6 +455,19 @@ function resolveNextjsSecretKey(options) {
|
|
|
455
455
|
// src/nextjs/middleware.ts
|
|
456
456
|
var OAUTH_PKCE_TTL_SECONDS = 10 * 60;
|
|
457
457
|
var OAUTH_PKCE_TTL_MS = OAUTH_PKCE_TTL_SECONDS * 1e3;
|
|
458
|
+
var DEFAULT_BAAS_PREFIX = "/sylphx";
|
|
459
|
+
var BAAS_PROXY_AUTH_REQUIRED_PATHS = ["/challenge/", "/security/"];
|
|
460
|
+
var BAAS_PROXY_PUBLIC_PATHS = ["/app"];
|
|
461
|
+
var BODYLESS_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD"]);
|
|
462
|
+
var RESPONSE_HEADER_ALLOWLIST = [
|
|
463
|
+
"cache-control",
|
|
464
|
+
"content-language",
|
|
465
|
+
"content-type",
|
|
466
|
+
"etag",
|
|
467
|
+
"expires",
|
|
468
|
+
"last-modified",
|
|
469
|
+
"retry-after"
|
|
470
|
+
];
|
|
458
471
|
function isTokenResponse(data) {
|
|
459
472
|
return typeof data === "object" && data !== null && "accessToken" in data && "refreshToken" in data && "user" in data && typeof data.accessToken === "string" && typeof data.refreshToken === "string";
|
|
460
473
|
}
|
|
@@ -491,6 +504,46 @@ function matchesPattern(pathname, pattern) {
|
|
|
491
504
|
function matchesAny(pathname, patterns) {
|
|
492
505
|
return patterns.some((p) => matchesPattern(pathname, p));
|
|
493
506
|
}
|
|
507
|
+
function normalizeRoutePrefix(prefix) {
|
|
508
|
+
const normalized = prefix.trim().replace(/\/+$/u, "");
|
|
509
|
+
return normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
510
|
+
}
|
|
511
|
+
function stripRoutePrefix(pathname, prefix) {
|
|
512
|
+
if (pathname === prefix) return "/";
|
|
513
|
+
if (!pathname.startsWith(`${prefix}/`)) return null;
|
|
514
|
+
return pathname.slice(prefix.length);
|
|
515
|
+
}
|
|
516
|
+
function isPublicBaasProxyPath(pathname) {
|
|
517
|
+
return BAAS_PROXY_PUBLIC_PATHS.some((path) => pathname === path);
|
|
518
|
+
}
|
|
519
|
+
function isAuthenticatedBaasProxyPath(pathname) {
|
|
520
|
+
return BAAS_PROXY_AUTH_REQUIRED_PATHS.some((prefix) => pathname.startsWith(prefix));
|
|
521
|
+
}
|
|
522
|
+
function isAllowedBaasProxyPath(pathname) {
|
|
523
|
+
return isPublicBaasProxyPath(pathname) || isAuthenticatedBaasProxyPath(pathname);
|
|
524
|
+
}
|
|
525
|
+
function copyRequestHeader(source, target, name) {
|
|
526
|
+
const value = source.get(name);
|
|
527
|
+
if (value) target.set(name, value);
|
|
528
|
+
}
|
|
529
|
+
function buildUpstreamProxyHeaders(request, ctx, sessionToken) {
|
|
530
|
+
const headers = new Headers();
|
|
531
|
+
copyRequestHeader(request.headers, headers, "accept");
|
|
532
|
+
copyRequestHeader(request.headers, headers, "content-type");
|
|
533
|
+
copyRequestHeader(request.headers, headers, "user-agent");
|
|
534
|
+
copyRequestHeader(request.headers, headers, "x-correlation-id");
|
|
535
|
+
headers.set("x-app-secret", ctx.secretKey);
|
|
536
|
+
if (sessionToken) headers.set("authorization", `Bearer ${sessionToken}`);
|
|
537
|
+
return headers;
|
|
538
|
+
}
|
|
539
|
+
function copyResponseHeaders(source) {
|
|
540
|
+
const headers = new Headers();
|
|
541
|
+
for (const name of RESPONSE_HEADER_ALLOWLIST) {
|
|
542
|
+
const value = source.get(name);
|
|
543
|
+
if (value) headers.set(name, value);
|
|
544
|
+
}
|
|
545
|
+
return headers;
|
|
546
|
+
}
|
|
494
547
|
function requestPathWithSearch(request) {
|
|
495
548
|
const { pathname, search } = request.nextUrl;
|
|
496
549
|
return `${pathname}${search}`;
|
|
@@ -503,6 +556,22 @@ function resolveSafeRelativeRedirectPath(value, fallback) {
|
|
|
503
556
|
if (isSafeRelativeRedirectPath(fallback)) return fallback;
|
|
504
557
|
return "/";
|
|
505
558
|
}
|
|
559
|
+
function resolveSameOriginUrl(request, value, fallbackPath) {
|
|
560
|
+
if (!value) return new URL(fallbackPath, request.url).toString();
|
|
561
|
+
if (isSafeRelativeRedirectPath(value)) {
|
|
562
|
+
const url = new URL(value, request.url);
|
|
563
|
+
if (url.hash) return null;
|
|
564
|
+
return url.toString();
|
|
565
|
+
}
|
|
566
|
+
try {
|
|
567
|
+
const url = new URL(value);
|
|
568
|
+
if (url.origin !== new URL(request.url).origin) return null;
|
|
569
|
+
if (url.hash) return null;
|
|
570
|
+
return url.toString();
|
|
571
|
+
} catch {
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
506
575
|
function bytesToBase64Url(bytes) {
|
|
507
576
|
let binary = "";
|
|
508
577
|
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
@@ -565,12 +634,30 @@ function isTwoFactorLoginResponse(data) {
|
|
|
565
634
|
function isOAuthAuthorizeResponse(data) {
|
|
566
635
|
return typeof data === "object" && data !== null && typeof data.authorization_url === "string";
|
|
567
636
|
}
|
|
637
|
+
function authCookieJsonResponse(ctx, data) {
|
|
638
|
+
const response = NextResponse.json({ success: true, user: data.user });
|
|
639
|
+
setAuthCookiesMiddleware(response, ctx.namespace, data);
|
|
640
|
+
return response;
|
|
641
|
+
}
|
|
568
642
|
function headersForPlatformAuth(ctx) {
|
|
569
643
|
return {
|
|
570
644
|
"Content-Type": "application/json",
|
|
571
645
|
"x-app-secret": ctx.secretKey
|
|
572
646
|
};
|
|
573
647
|
}
|
|
648
|
+
async function handleRegister(request, ctx) {
|
|
649
|
+
if (request.method !== "POST") {
|
|
650
|
+
return NextResponse.json({ error: "Method not allowed" }, { status: 405 });
|
|
651
|
+
}
|
|
652
|
+
const body = await parseJsonObject(request);
|
|
653
|
+
const res = await fetch(`${ctx.platformUrl}/v1/auth/register`, {
|
|
654
|
+
method: "POST",
|
|
655
|
+
headers: headersForPlatformAuth(ctx),
|
|
656
|
+
body: JSON.stringify(body ?? {})
|
|
657
|
+
});
|
|
658
|
+
const data = await res.json().catch(() => ({}));
|
|
659
|
+
return NextResponse.json(data, { status: res.status });
|
|
660
|
+
}
|
|
574
661
|
async function handleLogin(request, ctx) {
|
|
575
662
|
if (request.method !== "POST") {
|
|
576
663
|
return NextResponse.json({ error: "Method not allowed" }, { status: 405 });
|
|
@@ -596,9 +683,78 @@ async function handleLogin(request, ctx) {
|
|
|
596
683
|
if (!isTokenResponse(data)) {
|
|
597
684
|
return NextResponse.json({ error: "invalid_response" }, { status: 502 });
|
|
598
685
|
}
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
686
|
+
return authCookieJsonResponse(ctx, data);
|
|
687
|
+
}
|
|
688
|
+
async function handleVerifyEmail(request, ctx) {
|
|
689
|
+
if (request.method !== "POST") {
|
|
690
|
+
return NextResponse.json({ error: "Method not allowed" }, { status: 405 });
|
|
691
|
+
}
|
|
692
|
+
const body = await parseJsonObject(request);
|
|
693
|
+
const token = typeof body?.token === "string" ? body.token : null;
|
|
694
|
+
if (!token) {
|
|
695
|
+
return NextResponse.json({ error: "token is required" }, { status: 400 });
|
|
696
|
+
}
|
|
697
|
+
const res = await fetch(`${ctx.platformUrl}/v1/auth/verify-email`, {
|
|
698
|
+
method: "POST",
|
|
699
|
+
headers: headersForPlatformAuth(ctx),
|
|
700
|
+
body: JSON.stringify({ token })
|
|
701
|
+
});
|
|
702
|
+
const data = await res.json().catch(() => ({}));
|
|
703
|
+
return NextResponse.json(data, { status: res.status });
|
|
704
|
+
}
|
|
705
|
+
async function handleVerifyTwoFactor(request, ctx) {
|
|
706
|
+
if (request.method !== "POST") {
|
|
707
|
+
return NextResponse.json({ error: "Method not allowed" }, { status: 405 });
|
|
708
|
+
}
|
|
709
|
+
const body = await parseJsonObject(request);
|
|
710
|
+
const userId = typeof body?.userId === "string" ? body.userId : null;
|
|
711
|
+
const code = typeof body?.code === "string" ? body.code : null;
|
|
712
|
+
if (!userId || !code) {
|
|
713
|
+
return NextResponse.json({ error: "userId and code are required" }, { status: 400 });
|
|
714
|
+
}
|
|
715
|
+
const res = await fetch(`${ctx.platformUrl}/v1/auth/verify-2fa`, {
|
|
716
|
+
method: "POST",
|
|
717
|
+
headers: headersForPlatformAuth(ctx),
|
|
718
|
+
body: JSON.stringify({ userId, code })
|
|
719
|
+
});
|
|
720
|
+
const data = await res.json().catch(() => ({}));
|
|
721
|
+
if (!res.ok) {
|
|
722
|
+
return NextResponse.json(data, { status: res.status });
|
|
723
|
+
}
|
|
724
|
+
if (!isTokenResponse(data)) {
|
|
725
|
+
return NextResponse.json({ error: "invalid_response" }, { status: 502 });
|
|
726
|
+
}
|
|
727
|
+
return authCookieJsonResponse(ctx, data);
|
|
728
|
+
}
|
|
729
|
+
function handleSession(request, ctx) {
|
|
730
|
+
if (request.method !== "GET") {
|
|
731
|
+
return NextResponse.json({ error: "Method not allowed" }, { status: 405 });
|
|
732
|
+
}
|
|
733
|
+
const sessionToken = request.cookies.get(ctx.cookieNames.SESSION)?.value;
|
|
734
|
+
const userCookieValue = request.cookies.get(ctx.cookieNames.USER)?.value;
|
|
735
|
+
let userCookie = userCookieValue ? parseUserCookie2(userCookieValue) : null;
|
|
736
|
+
if (!userCookie && userCookieValue) {
|
|
737
|
+
try {
|
|
738
|
+
userCookie = parseUserCookie2(decodeURIComponent(userCookieValue));
|
|
739
|
+
} catch {
|
|
740
|
+
userCookie = null;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
if (!sessionToken || isTokenExpired(sessionToken) || !userCookie?.user) {
|
|
744
|
+
return NextResponse.json({ success: true, session: null, user: null });
|
|
745
|
+
}
|
|
746
|
+
const payload = decodeJwtPayload(sessionToken);
|
|
747
|
+
const userId = payload?.sub ?? userCookie.user.id;
|
|
748
|
+
const expiresAt = typeof payload?.exp === "number" ? new Date(payload.exp * 1e3).toISOString() : new Date(userCookie.expiresAt).toISOString();
|
|
749
|
+
return NextResponse.json({
|
|
750
|
+
success: true,
|
|
751
|
+
session: {
|
|
752
|
+
id: `platform:${userId}`,
|
|
753
|
+
userId,
|
|
754
|
+
expiresAt
|
|
755
|
+
},
|
|
756
|
+
user: userCookie.user
|
|
757
|
+
});
|
|
602
758
|
}
|
|
603
759
|
async function handleOAuthProviders(ctx) {
|
|
604
760
|
const res = await fetch(`${ctx.platformUrl}/v1/auth/oauth-providers`, {
|
|
@@ -648,6 +804,78 @@ async function handleOAuthAuthorize(request, ctx) {
|
|
|
648
804
|
setOAuthPkceCookie(response, ctx, { verifier, createdAt: Date.now() });
|
|
649
805
|
return response;
|
|
650
806
|
}
|
|
807
|
+
async function handlePasskeyOptions(request, ctx) {
|
|
808
|
+
if (request.method !== "POST") {
|
|
809
|
+
return NextResponse.json({ error: "Method not allowed" }, { status: 405 });
|
|
810
|
+
}
|
|
811
|
+
const body = await parseJsonObject(request);
|
|
812
|
+
const res = await fetch(`${ctx.platformUrl}/v1/auth/passkey/options`, {
|
|
813
|
+
method: "POST",
|
|
814
|
+
headers: headersForPlatformAuth(ctx),
|
|
815
|
+
body: JSON.stringify(body ?? {})
|
|
816
|
+
});
|
|
817
|
+
const data = await res.json().catch(() => ({}));
|
|
818
|
+
return NextResponse.json(data, { status: res.status });
|
|
819
|
+
}
|
|
820
|
+
async function handlePasskeyAuthenticate(request, ctx) {
|
|
821
|
+
if (request.method !== "POST") {
|
|
822
|
+
return NextResponse.json({ error: "Method not allowed" }, { status: 405 });
|
|
823
|
+
}
|
|
824
|
+
const body = await parseJsonObject(request);
|
|
825
|
+
const res = await fetch(`${ctx.platformUrl}/v1/auth/passkey/authenticate`, {
|
|
826
|
+
method: "POST",
|
|
827
|
+
headers: headersForPlatformAuth(ctx),
|
|
828
|
+
body: JSON.stringify(body ?? {})
|
|
829
|
+
});
|
|
830
|
+
const data = await res.json().catch(() => ({}));
|
|
831
|
+
if (!res.ok) {
|
|
832
|
+
return NextResponse.json(data, { status: res.status });
|
|
833
|
+
}
|
|
834
|
+
if (!isTokenResponse(data)) {
|
|
835
|
+
return NextResponse.json({ error: "invalid_response" }, { status: 502 });
|
|
836
|
+
}
|
|
837
|
+
return authCookieJsonResponse(ctx, data);
|
|
838
|
+
}
|
|
839
|
+
async function handleForgotPassword(request, ctx) {
|
|
840
|
+
if (request.method !== "POST") {
|
|
841
|
+
return NextResponse.json({ error: "Method not allowed" }, { status: 405 });
|
|
842
|
+
}
|
|
843
|
+
const body = await parseJsonObject(request);
|
|
844
|
+
const email = typeof body?.email === "string" ? body.email : null;
|
|
845
|
+
const rawRedirect = typeof body?.redirectUrl === "string" ? body.redirectUrl : typeof body?.redirectTo === "string" ? body.redirectTo : null;
|
|
846
|
+
const redirectUrl = resolveSameOriginUrl(request, rawRedirect, "/reset-password");
|
|
847
|
+
if (!email) {
|
|
848
|
+
return NextResponse.json({ error: "email is required" }, { status: 400 });
|
|
849
|
+
}
|
|
850
|
+
if (!redirectUrl) {
|
|
851
|
+
return NextResponse.json({ error: "invalid_redirect_url" }, { status: 400 });
|
|
852
|
+
}
|
|
853
|
+
const res = await fetch(`${ctx.platformUrl}/v1/auth/forgot-password`, {
|
|
854
|
+
method: "POST",
|
|
855
|
+
headers: headersForPlatformAuth(ctx),
|
|
856
|
+
body: JSON.stringify({ email, redirectUrl })
|
|
857
|
+
});
|
|
858
|
+
const data = await res.json().catch(() => ({}));
|
|
859
|
+
return NextResponse.json(data, { status: res.status });
|
|
860
|
+
}
|
|
861
|
+
async function handleResetPassword(request, ctx) {
|
|
862
|
+
if (request.method !== "POST") {
|
|
863
|
+
return NextResponse.json({ error: "Method not allowed" }, { status: 405 });
|
|
864
|
+
}
|
|
865
|
+
const body = await parseJsonObject(request);
|
|
866
|
+
const token = typeof body?.token === "string" ? body.token : null;
|
|
867
|
+
const password = typeof body?.password === "string" ? body.password : typeof body?.newPassword === "string" ? body.newPassword : null;
|
|
868
|
+
if (!token || !password) {
|
|
869
|
+
return NextResponse.json({ error: "token and password are required" }, { status: 400 });
|
|
870
|
+
}
|
|
871
|
+
const res = await fetch(`${ctx.platformUrl}/v1/auth/reset-password`, {
|
|
872
|
+
method: "POST",
|
|
873
|
+
headers: headersForPlatformAuth(ctx),
|
|
874
|
+
body: JSON.stringify({ token, password })
|
|
875
|
+
});
|
|
876
|
+
const data = await res.json().catch(() => ({}));
|
|
877
|
+
return NextResponse.json(data, { status: res.status });
|
|
878
|
+
}
|
|
651
879
|
async function handleCallback(request, ctx) {
|
|
652
880
|
const { searchParams } = request.nextUrl;
|
|
653
881
|
const code = searchParams.get("code");
|
|
@@ -948,12 +1176,15 @@ async function handleSwitchOrg(request, ctx) {
|
|
|
948
1176
|
}
|
|
949
1177
|
const expiresIn = resolveOrgScopedTokenExpiresIn(data);
|
|
950
1178
|
const expiresAt = Date.now() + expiresIn * 1e3;
|
|
1179
|
+
const payload = decodeJwtPayload(accessToken);
|
|
1180
|
+
const activeOrganization = {
|
|
1181
|
+
id: payload?.org_id ?? orgId,
|
|
1182
|
+
slug: payload?.org_slug ?? requestedOrgSlug
|
|
1183
|
+
};
|
|
951
1184
|
const response = NextResponse.json({
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
tokenType: resolveOrgScopedTokenType(data),
|
|
956
|
-
user: data.user ?? null
|
|
1185
|
+
success: true,
|
|
1186
|
+
user: data.user ?? null,
|
|
1187
|
+
organization: activeOrganization
|
|
957
1188
|
});
|
|
958
1189
|
response.cookies.set(ctx.cookieNames.SESSION, accessToken, {
|
|
959
1190
|
...SECURE_COOKIE_OPTIONS,
|
|
@@ -974,11 +1205,7 @@ async function handleSwitchOrg(request, ctx) {
|
|
|
974
1205
|
}
|
|
975
1206
|
);
|
|
976
1207
|
}
|
|
977
|
-
|
|
978
|
-
setActiveOrganizationCookies(response, ctx, {
|
|
979
|
-
id: payload?.org_id ?? orgId,
|
|
980
|
-
slug: payload?.org_slug ?? requestedOrgSlug
|
|
981
|
-
});
|
|
1208
|
+
setActiveOrganizationCookies(response, ctx, activeOrganization);
|
|
982
1209
|
ctx.log("Switch org success", { orgId });
|
|
983
1210
|
return response;
|
|
984
1211
|
} catch (err) {
|
|
@@ -986,6 +1213,30 @@ async function handleSwitchOrg(request, ctx) {
|
|
|
986
1213
|
return NextResponse.json({ error: "internal_error" }, { status: 500 });
|
|
987
1214
|
}
|
|
988
1215
|
}
|
|
1216
|
+
async function handleBaasProxy(request, ctx, sessionToken) {
|
|
1217
|
+
const prefix = ctx.config.baasPrefix;
|
|
1218
|
+
if (!prefix) return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
1219
|
+
const upstreamPath = stripRoutePrefix(request.nextUrl.pathname, prefix);
|
|
1220
|
+
if (!upstreamPath || !isAllowedBaasProxyPath(upstreamPath)) {
|
|
1221
|
+
return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
1222
|
+
}
|
|
1223
|
+
if (isAuthenticatedBaasProxyPath(upstreamPath) && !sessionToken) {
|
|
1224
|
+
return NextResponse.json({ error: "Not authenticated" }, { status: 401 });
|
|
1225
|
+
}
|
|
1226
|
+
const upstreamUrl = new URL(`${ctx.platformUrl}/v1${upstreamPath}`);
|
|
1227
|
+
upstreamUrl.search = request.nextUrl.search;
|
|
1228
|
+
const method = request.method.toUpperCase();
|
|
1229
|
+
const body = BODYLESS_METHODS.has(method) ? void 0 : await request.arrayBuffer();
|
|
1230
|
+
const res = await fetch(upstreamUrl, {
|
|
1231
|
+
method,
|
|
1232
|
+
headers: buildUpstreamProxyHeaders(request, ctx, sessionToken),
|
|
1233
|
+
body
|
|
1234
|
+
});
|
|
1235
|
+
return new NextResponse(res.body, {
|
|
1236
|
+
status: res.status,
|
|
1237
|
+
headers: copyResponseHeaders(res.headers)
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
989
1240
|
async function refreshTokens(refreshToken, ctx) {
|
|
990
1241
|
ctx.log("Refreshing tokens");
|
|
991
1242
|
try {
|
|
@@ -1050,6 +1301,7 @@ function createSylphxMiddleware(userConfig = {}) {
|
|
|
1050
1301
|
afterSignOutUrl: userConfig.afterSignOutUrl ?? "/",
|
|
1051
1302
|
afterSignInUrl: userConfig.afterSignInUrl ?? "/dashboard",
|
|
1052
1303
|
authPrefix: userConfig.authPrefix ?? "/auth",
|
|
1304
|
+
baasPrefix: userConfig.baasPrefix === false ? null : normalizeRoutePrefix(userConfig.baasPrefix ?? DEFAULT_BAAS_PREFIX),
|
|
1053
1305
|
debug: userConfig.debug ?? false,
|
|
1054
1306
|
orgRequired: userConfig.orgRequired ?? false,
|
|
1055
1307
|
orgSelectionUrl: userConfig.orgSelectionUrl ?? "/select-organization",
|
|
@@ -1065,7 +1317,8 @@ function createSylphxMiddleware(userConfig = {}) {
|
|
|
1065
1317
|
...config.publicRoutes,
|
|
1066
1318
|
config.signInUrl,
|
|
1067
1319
|
"/signup",
|
|
1068
|
-
`${config.authPrefix}
|
|
1320
|
+
`${config.authPrefix}/*`,
|
|
1321
|
+
...config.baasPrefix ? [`${config.baasPrefix}/*`] : []
|
|
1069
1322
|
];
|
|
1070
1323
|
const log = (msg, data) => {
|
|
1071
1324
|
if (config.debug) {
|
|
@@ -1098,18 +1351,42 @@ function createSylphxMiddleware(userConfig = {}) {
|
|
|
1098
1351
|
if (pathname.includes(".") || pathname.startsWith("/_next")) {
|
|
1099
1352
|
return NextResponse.next();
|
|
1100
1353
|
}
|
|
1354
|
+
if (pathname === `${config.authPrefix}/register`) {
|
|
1355
|
+
return handleRegister(request, ctx);
|
|
1356
|
+
}
|
|
1101
1357
|
if (pathname === `${config.authPrefix}/callback`) {
|
|
1102
1358
|
return handleCallback(request, ctx);
|
|
1103
1359
|
}
|
|
1104
1360
|
if (pathname === `${config.authPrefix}/login`) {
|
|
1105
1361
|
return handleLogin(request, ctx);
|
|
1106
1362
|
}
|
|
1363
|
+
if (pathname === `${config.authPrefix}/verify-email`) {
|
|
1364
|
+
return handleVerifyEmail(request, ctx);
|
|
1365
|
+
}
|
|
1107
1366
|
if (pathname === `${config.authPrefix}/oauth-providers`) {
|
|
1108
1367
|
return handleOAuthProviders(ctx);
|
|
1109
1368
|
}
|
|
1110
1369
|
if (pathname === `${config.authPrefix}/oauth/authorize`) {
|
|
1111
1370
|
return handleOAuthAuthorize(request, ctx);
|
|
1112
1371
|
}
|
|
1372
|
+
if (pathname === `${config.authPrefix}/passkey/options`) {
|
|
1373
|
+
return handlePasskeyOptions(request, ctx);
|
|
1374
|
+
}
|
|
1375
|
+
if (pathname === `${config.authPrefix}/passkey/authenticate`) {
|
|
1376
|
+
return handlePasskeyAuthenticate(request, ctx);
|
|
1377
|
+
}
|
|
1378
|
+
if (pathname === `${config.authPrefix}/verify-2fa`) {
|
|
1379
|
+
return handleVerifyTwoFactor(request, ctx);
|
|
1380
|
+
}
|
|
1381
|
+
if (pathname === `${config.authPrefix}/forgot-password`) {
|
|
1382
|
+
return handleForgotPassword(request, ctx);
|
|
1383
|
+
}
|
|
1384
|
+
if (pathname === `${config.authPrefix}/reset-password`) {
|
|
1385
|
+
return handleResetPassword(request, ctx);
|
|
1386
|
+
}
|
|
1387
|
+
if (pathname === `${config.authPrefix}/session`) {
|
|
1388
|
+
return handleSession(request, ctx);
|
|
1389
|
+
}
|
|
1113
1390
|
if (pathname === `${config.authPrefix}/signout`) {
|
|
1114
1391
|
return handleSignOut(request, ctx);
|
|
1115
1392
|
}
|
|
@@ -1147,6 +1424,9 @@ function createSylphxMiddleware(userConfig = {}) {
|
|
|
1147
1424
|
activeSessionToken = null;
|
|
1148
1425
|
}
|
|
1149
1426
|
}
|
|
1427
|
+
if (config.baasPrefix && stripRoutePrefix(pathname, config.baasPrefix)) {
|
|
1428
|
+
return handleBaasProxy(request, ctx, activeSessionToken);
|
|
1429
|
+
}
|
|
1150
1430
|
const isPublic = matchesAny(pathname, publicRoutes);
|
|
1151
1431
|
if (!isPublic && !isAuthenticated) {
|
|
1152
1432
|
log("Redirecting to sign-in");
|