Build your own sign-in form
Use your own design instead of the hosted pages.
The hosted pages are the quickest option. To keep users on your own domain with your own design, call the SDK directly. Security is the same either way.
Before you start
Add your app's origin to the instance's allowed origins. The service rejects sign-in requests from origins it doesn't know, which stops other sites from submitting forms on your users' behalf.
In the dashboard, add it under Settings > Allowed origins.
Sign-in form
tsx
"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { useAuth, AuthError } from "@daywalker/auth/react";
export function SignInForm() {
const { signIn } = useAuth();
const router = useRouter();
const [error, setError] = useState("");
const [busy, setBusy] = useState(false);
async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setBusy(true);
setError("");
const f = new FormData(e.currentTarget);
try {
const result = await signIn({ email: f.get("email") as string, password: f.get("password") as string });
if (result.status === "needs_second_factor") {
router.push("/sign-in/code"); // see the two-factor guide
return;
}
router.push("/dashboard");
} catch (err) {
setError(err instanceof AuthError ? err.message : "Network error, try again");
} finally {
setBusy(false);
}
}
return (
<form onSubmit={onSubmit}>
<label>Email <input name="email" type="email" autoComplete="email" required /></label>
<label>Password <input name="password" type="password" autoComplete="current-password" required /></label>
{error && <p role="alert">{error}</p>}
<button disabled={busy}>{busy ? "Signing in…" : "Sign in"}</button>
</form>
);
}Some things to get right:
autoCompleteattributes let password managers fill and save credentials.- Show
err.messageas-is. It's written for end users. On a wrong password it deliberately doesn't say whether the email exists. - Disable the button while busy to avoid double submissions.
- Handle
needs_second_factor. Accounts with 2FA need a second step; see Two-factor authentication.
Sign-up form
The same idea with signUp. Show field-level problems from err.fields:
tsx
try {
await signUp({ email, password, firstName, lastName });
} catch (err) {
if (err instanceof AuthError) {
// e.g. [{ path: "email", message: "Invalid email address" }]
for (const f of err.fields) showFieldError(f.path, f.message);
if (err.code === "email_taken") showFieldError("email", err.message);
}
}Then send new users through email verification.
Sign out
tsx
const { signOut } = useAuth();
await signOut(); // ends the session on the server and in every open tab
// (for company SSO with single logout on, it also signs out at the identity provider; see Enterprise SSO)