diff --git a/apps/e2e/tests/js/cross-domain-auth.test.ts b/apps/e2e/tests/js/cross-domain-auth.test.ts index ed970daf6..d78d32fe0 100644 --- a/apps/e2e/tests/js/cross-domain-auth.test.ts +++ b/apps/e2e/tests/js/cross-domain-auth.test.ts @@ -111,7 +111,7 @@ it("adds secure cross-domain handoff parameters when redirecting to hosted sign- }); }); -it("returns static app.urls.signIn for hosted flows", async ({ expect }) => { +it("throws when app.urls.signIn is read for hosted flows", async ({ expect }) => { await withHostedDomainSuffix(async () => { const projectId = "44444444-4444-4444-8444-444444444444"; const currentHref = `${localRedirectUrl}/private-page?foo=bar`; @@ -124,17 +124,13 @@ it("returns static app.urls.signIn for hosted flows", async ({ expect }) => { href: currentHref, assign: () => { throw new Error("INTENTIONAL_TEST_ABORT"); }, }, + addEventListener: () => {}, + removeEventListener: () => {}, } as any; try { const clientApp = createClientApp(projectId); - const signInUrl = new URL(clientApp.urls.signIn); - expect(signInUrl.origin).toBe(`https://${projectId}.example-stack-hosted.test`); - expect(signInUrl.pathname).toBe("/handler/sign-in"); - expect(signInUrl.searchParams.get("after_auth_return_to")).toBeNull(); - expect(signInUrl.searchParams.get("hexclave_cross_domain_state")).toBeNull(); - expect(signInUrl.searchParams.get("hexclave_cross_domain_code_challenge")).toBeNull(); - expect(signInUrl.searchParams.get("hexclave_cross_domain_after_callback_redirect_url")).toBeNull(); + expect(() => clientApp.urls.signIn).toThrowError(/app\.urls\.signIn cannot be used when this app is configured to use hosted components.*Use app\.redirectToSignIn\(\) instead/s); } finally { globalThis.window = previousWindow; globalThis.document = previousDocument; @@ -142,7 +138,7 @@ it("returns static app.urls.signIn for hosted flows", async ({ expect }) => { }); }); -it("returns static app.urls.signOut for hosted flows", async ({ expect }) => { +it("throws when app.urls.signOut is read for hosted flows", async ({ expect }) => { await withHostedDomainSuffix(async () => { const projectId = "55555555-5555-4555-8555-555555555555"; const currentHref = `${localRedirectUrl}/signed-in-page?foo=bar`; @@ -155,14 +151,13 @@ it("returns static app.urls.signOut for hosted flows", async ({ expect }) => { href: currentHref, assign: () => { throw new Error("INTENTIONAL_TEST_ABORT"); }, }, + addEventListener: () => {}, + removeEventListener: () => {}, } as any; try { const clientApp = createClientApp(projectId); - const signOutUrl = new URL(clientApp.urls.signOut); - expect(signOutUrl.origin).toBe(`https://${projectId}.example-stack-hosted.test`); - expect(signOutUrl.pathname).toBe("/handler/sign-out"); - expect(signOutUrl.searchParams.get("after_auth_return_to")).toBeNull(); + expect(() => clientApp.urls.signOut).toThrowError(/app\.urls\.signOut cannot be used when this app is configured to use hosted components.*Use app\.redirectToSignOut\(\) instead/s); } finally { globalThis.window = previousWindow; globalThis.document = previousDocument; diff --git a/apps/hosted-components/src/hosted-components/auth/auth-page.tsx b/apps/hosted-components/src/hosted-components/auth/auth-page.tsx index bd9005a0f..3a5fbb987 100644 --- a/apps/hosted-components/src/hosted-components/auth/auth-page.tsx +++ b/apps/hosted-components/src/hosted-components/auth/auth-page.tsx @@ -185,7 +185,7 @@ function HostedAuthPageInner(props: {
Don't have an account?{" "}
{
event.preventDefault();
@@ -200,7 +200,7 @@ function HostedAuthPageInner(props: {
Already have an account?{" "}
{
event.preventDefault();
diff --git a/apps/hosted-components/src/hosted-components/auth/forgot-password-page.tsx b/apps/hosted-components/src/hosted-components/auth/forgot-password-page.tsx
index 635aead01..9fea6e416 100644
--- a/apps/hosted-components/src/hosted-components/auth/forgot-password-page.tsx
+++ b/apps/hosted-components/src/hosted-components/auth/forgot-password-page.tsx
@@ -107,7 +107,7 @@ export function HostedForgotPassword(props: {
Remembered your password?{" "}
{
event.preventDefault();
diff --git a/apps/hosted-components/src/hosted-components/auth/forms/credential-sign-in.tsx b/apps/hosted-components/src/hosted-components/auth/forms/credential-sign-in.tsx
index 7e6b674d9..04f4b79c5 100644
--- a/apps/hosted-components/src/hosted-components/auth/forms/credential-sign-in.tsx
+++ b/apps/hosted-components/src/hosted-components/auth/forms/credential-sign-in.tsx
@@ -64,7 +64,7 @@ export function CredentialSignIn() {
}>\n Loading... No notes found You are signed in User ID: {user.id} Loading... No notes found You are signed in User ID: {user.id} You are signed in User ID: {user.id} You are signed in User ID: {user.id} You are signed in User ID: {user.id} Loading... No notes found You are signed in User ID: {user.id} Loading... No notes found You are signed in User ID: {user.id} Welcome, {user.displayName ?? "unnamed user"} Your e-mail: {user.primaryEmail} You are not logged in Render a client component that calls You are signed in User ID: {user.id} You are signed in User ID: {user.id} {t('If you are not redirected automatically, ')} {t('If you are not redirected automatically, ')}Supabase data
\n {listContent}
\n Supabase data
\n {listContent}
\n Supabase data
{listContent}
diff --git a/docs-mintlify/guides/integrations/supabase/overview.mdx b/docs-mintlify/guides/integrations/supabase/overview.mdx
index 4d6113e12..c911d78fa 100644
--- a/docs-mintlify/guides/integrations/supabase/overview.mdx
+++ b/docs-mintlify/guides/integrations/supabase/overview.mdx
@@ -123,7 +123,6 @@ Let's create a sample table and some RLS policies to demonstrate how to integrat
import { createSupabaseClient } from "@/utils/supabase-client";
import { useHexclaveApp, useUser } from "@hexclave/next";
- import Link from "next/link";
import { useEffect, useState } from "react";
export default function Page() {
@@ -149,9 +148,9 @@ Let's create a sample table and some RLS policies to demonstrate how to integrat
<>
Supabase data
{listContent}
diff --git a/docs-mintlify/guides/other/tutorials/build-a-saas-with-hexclave.mdx b/docs-mintlify/guides/other/tutorials/build-a-saas-with-hexclave.mdx
index 86b91b285..9c00b4ee9 100644
--- a/docs-mintlify/guides/other/tutorials/build-a-saas-with-hexclave.mdx
+++ b/docs-mintlify/guides/other/tutorials/build-a-saas-with-hexclave.mdx
@@ -114,12 +114,11 @@ After setup, open the hosted auth UI (for example `/handler/sign-up`), create a
### Marketing header: sign in / sign out
-Use `useHexclaveApp()` so you do not hard-code handler URLs (they can be customized in the project):
+Use `useHexclaveApp()` so navigation goes through Hexclave's redirect helpers:
```tsx title="components/auth-header.tsx"
"use client";
-import Link from "next/link";
import { useHexclaveApp, useUser } from "@hexclave/next";
export function AuthHeader() {
@@ -131,13 +130,13 @@ export function AuthHeader() {
{user ? (
<>
{user.displayName ?? user.primaryEmail ?? user.id}
- Account
- Sign out
+
+
>
) : (
<>
- Sign in
- Sign up
+
+
>
)}
diff --git a/docs-mintlify/guides/other/tutorials/ship-production-ready-auth.mdx b/docs-mintlify/guides/other/tutorials/ship-production-ready-auth.mdx
index 05460d29a..990e40ab9 100644
--- a/docs-mintlify/guides/other/tutorials/ship-production-ready-auth.mdx
+++ b/docs-mintlify/guides/other/tutorials/ship-production-ready-auth.mdx
@@ -44,7 +44,7 @@ You typically combine **one or more** of:
Match **only** prefixes that should be gated. Do **not** blanket-match `/` or you can block static assets and Stack’s **`/handler`** routes (sign-in, sign-up, callbacks).
- If your project uses **custom handler base paths**, use the sign-in URL from `hexclaveServerApp.urls.signIn` as the redirect target instead of hard-coding `/handler/sign-in` (it may be an absolute URL depending on configuration—`NextResponse.redirect` accepts that).
+ If your project uses hosted components or cross-domain auth, prefer protecting pages with `hexclaveServerApp.getUser({ or: "redirect" })` in a Server Component. Middleware cannot run the runtime `redirectToSignIn()` helper, so only hard-code `/handler/sign-in` here when your app owns a same-domain handler route.
Supabase data
{listContent}
diff --git a/docs-mintlify/sdk/hooks/use-hexclave-app.mdx b/docs-mintlify/sdk/hooks/use-hexclave-app.mdx
index ced610ee8..985e70971 100644
--- a/docs-mintlify/sdk/hooks/use-hexclave-app.mdx
+++ b/docs-mintlify/sdk/hooks/use-hexclave-app.mdx
@@ -15,7 +15,7 @@ import { useHexclaveApp } from "@hexclave/next"; // replace `next` with the cor
function MyComponent() {
const hexclaveApp = useHexclaveApp();
- return Supabase data
\n {listContent}
\n Supabase data
\n {listContent}
\n stackApp.redirectToSignIn() or stackApp.redirectToSignUp().Supabase data
{listContent}
diff --git a/docs/content/docs/sdk/hooks/use-stack-app.mdx b/docs/content/docs/sdk/hooks/use-stack-app.mdx
index 3e55c03fb..341346127 100644
--- a/docs/content/docs/sdk/hooks/use-stack-app.mdx
+++ b/docs/content/docs/sdk/hooks/use-stack-app.mdx
@@ -11,6 +11,6 @@ import { useStackApp } from "@stackframe/stack";
function MyComponent() {
const stackApp = useStackApp();
- return Supabase data
{listContent}
diff --git a/packages/shared/src/interface/page-component-versions.ts b/packages/shared/src/interface/page-component-versions.ts
index 6d39e5f02..9eae4b909 100644
--- a/packages/shared/src/interface/page-component-versions.ts
+++ b/packages/shared/src/interface/page-component-versions.ts
@@ -218,7 +218,7 @@ function createAuthPagePrompt(type: AuthPagePromptType): CustomPagePrompt {
{JSON.stringify(error, null, 2)} : null}
diff --git a/packages/template/src/components-page/auth-page.tsx b/packages/template/src/components-page/auth-page.tsx
index 6731909da..8a331326e 100644
--- a/packages/template/src/components-page/auth-page.tsx
+++ b/packages/template/src/components-page/auth-page.tsx
@@ -143,7 +143,7 @@ function Inner(props: Props) {
project.config.signUpEnabled && (