Interface utilisateur · Guide pratique
Pages d'authentification
Ajoutez les routes de connexion, vérification, réinitialisation et deuxième facteur autour des formulaires fournis.
Le SDK fournit les formulaires, mais votre application possède les routes, la mise en page, les métadonnées et la navigation. L'expérience reste cohérente avec votre produit tandis que Krakstack Auth gère les identifiants et sessions.
Fournisseur requis
Placez KrakstackAuthProvider au-dessus de toutes les pages auth. Avec le proxy recommandé, omettez baseUrl afin que le client navigateur utilise l'origine de l'application.
import { Outlet, createRootRoute } from "@tanstack/react-router";
import { KrakstackAuthProvider } from "@krak-stack/auth/components";
import { KrakstackProvider } from "@krak-stack/registry/krakstack-provider";
import { getLocale, locales } from "@/paraglide/runtime";
export const Route = createRootRoute({
component: () => (
<KrakstackProvider locale={getLocale()} locales={locales}>
<KrakstackAuthProvider
projectId={import.meta.env.VITE_KRAKSTACK_AUTH_PROJECT_ID}
>
<Outlet />
</KrakstackAuthProvider>
</KrakstackProvider>
),
});Le fournisseur charge la marque et les méthodes activées pour le projet. Transmettez la langue Paraglide active via le KrakstackProvider englobant, pas par une prop sur KrakstackAuthProvider. Le fournisseur auth synchronise aussi cette langue avec le compte de l'utilisateur connecté; consultez la synchronisation de la langue pour les limites des pages publiques et la gestion des échecs.
Mise en page auth
Créez une disposition sans chemin _auth pour le cadre commun et la redirection des utilisateurs déjà connectés. Validez et conservez le paramètre redirect.
import { useAtomSuspense } from "@effect/atom-react";
import { Navigate, Outlet, createFileRoute } from "@tanstack/react-router";
import { authSessionAtom } from "@krak-stack/auth/components";
import { Schema } from "effect";
const AuthSearch = Schema.toStandardSchemaV1(
Schema.Struct({
redirect: Schema.optional(Schema.String),
}).annotate({ identifier: "AuthSearch" }),
);
const safeReturnPath = (value: string | undefined) => {
if (!value?.startsWith("/") || value.startsWith("//")) return "/";
const url = new URL(value, "https://app.invalid");
if (url.origin !== "https://app.invalid") return "/";
return `${url.pathname}${url.search}${url.hash}`;
};
export const Route = createFileRoute("/_auth")({
validateSearch: AuthSearch,
component: AuthLayout,
});
function AuthLayout() {
const search = Route.useSearch();
const session = useAtomSuspense(authSessionAtom(), {
suspendOnWaiting: true,
}).value;
if (session) {
return <Navigate href={safeReturnPath(search.redirect)} reloadDocument />;
}
return (
<main className="mx-auto flex min-h-screen max-w-md items-center px-4">
<Outlet />
</main>
);
}Le schéma valide la forme de la recherche; safeReturnPath impose la frontière de sécurité. Ne transmettez jamais une URL absolue ou relative au protocole non approuvée à <Navigate href>.
Ajouter les routes
Créez les chemins publics suivants. Les composants utilisent ces noms pour leurs liens et redirections :
| Route | Composant | Fonction |
|---|---|---|
/sign-in | Signin | Mot de passe, code courriel et connexion sociale |
/verify-email | VerifyEmail | Code de vérification du courriel |
/reset-password | ResetPassword | Demande ou finalisation d'une réinitialisation |
/2fa | TwoFactor | Défi TOTP, courriel ou code de secours |
Chaque route TanStack suit le même modèle :
import { Signin } from "@krak-stack/auth/components";
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/_auth/sign-in")({
component: () => <Signin />,
});Répétez avec VerifyEmail, ResetPassword et TwoFactor. Il n'existe ni route /sign-up ni composant Signup. Le code à usage unique par courriel et Google créent un compte lorsque l'identité n'existe pas, directement depuis le flux de connexion; l'authentification par mot de passe sert uniquement à se connecter. Conservez les chemins publics exacts sauf si vous personnalisez aussi tous les liens et rappels.
Mot de passe oublié
Signin pointe vers /reset-password. Sans jeton, ResetPassword collecte le courriel et demande un lien de réinitialisation. Lorsque l'utilisateur revient depuis ce courriel avec un jeton, le même composant affiche le formulaire du nouveau mot de passe. La réponse reste générique afin de ne pas révéler l'existence d'un compte.
Routes protégées
Utilisez une route parente pour l'expérience de navigation :
import { useAtomSuspense } from "@effect/atom-react";
import { Navigate, Outlet, createFileRoute } from "@tanstack/react-router";
import { authSessionAtom } from "@krak-stack/auth/components";
export const Route = createFileRoute("/_protected")({
component: ProtectedLayout,
});
function ProtectedLayout() {
const location = Route.useLocation();
const session = useAtomSuspense(authSessionAtom(), {
suspendOnWaiting: true,
}).value;
if (!session?.user) {
return <Navigate to="/sign-in" search={{ redirect: location.href }} />;
}
return <Outlet />;
}Cette garde ne remplace pas l'autorisation backend. Chaque opération protégée doit aussi appeler AuthService.requireUser(), requireOrganization() ou requireUserOrganization().
Accès à l'organisation
MemberRequired peut expliquer l'absence d'accès et permettre l'acceptation d'une invitation :
import { MemberRequired } from "@krak-stack/auth/components";
<MemberRequired
organizationId={organizationId}
contactEmail="[email protected]"
>
<OrganizationDashboard />
</MemberRequired>;Il s'agit uniquement d'une frontière d'interface. Répétez les vérifications d'adhésion et de rôle dans les services backend.