Directus Auth out of the Box — Registration, Login, Email Verification and Password Reset

One of the things I appreciate most about Directus is that authentication is built in. Registration, login, email verification, and password reset — all handled without reaching for a third party auth service like Auth0 or Clerk.

Here’s how I implement the full auth flow in a TanStack Start app using the Directus SDK.

User Registration

The SDK’s registerUser function handles registration. You pass the email, password and any additional fields. The verification_url tells Directus where to redirect the user after they click the link in their email — Directus appends the token to that URL automatically.

export async function registerUser(
  email: string,
  password: string,
  firstName: string,
  lastName: string,
) {
  const options = {
    first_name: firstName,
    last_name: lastName,
    verification_url: `${import.meta.env.VITE_TANSTACK_URL}/auth/verify-email`,
  };
  return await directus.request(
    registerUserDirectus(email, password, options),
  );
}

Directus sends the verification email automatically via your configured SMTP provider. I use Resend — SMTP is configured directly in the Docker Compose file via environment variables and the whole setup takes just a few minutes.

EMAIL_TRANSPORT: smtp
EMAIL_SMTP_HOST: smtp.resend.com
EMAIL_SMTP_PORT: 465
EMAIL_SMTP_USER: resend
EMAIL_SMTP_PASSWORD: your_resend_api_key
EMAIL_FROM: [email protected]

Login

Logging in is a single call on the Directus client. Session cookies are handled automatically thanks to the credentials: 'include' config set up on the client.

export async function loginUser(email: string, password: string) {
  return await directus.login({ email, password });
}

Clean, no token management, no localStorage juggling. The session is maintained via cookie.

Email Verification

When the user clicks the link in their verification email, they land on your /auth/verify-email route with a token in the query string. You take that token and hit Directus’s verify endpoint directly.

function VerifyEmailComponent() {
  const { token } = Route.useSearch();
  const [status, setStatus] = useState<
    'verifying' | 'success' | 'error' | 'pending'
  >('pending');

  useEffect(() => {
    if (!token) return;

    async function verify() {
      setStatus('verifying');
      try {
        const response = await fetch(
          `${import.meta.env.VITE_DIRECTUS_URL}/users/register/verify-email?token=${token}`,
          { method: 'GET' },
        );
        if (response.ok) {
          setStatus('success');
        } else {
          setStatus('error');
        }
      } catch (err) {
        setStatus('error');
      }
    }
    verify();
  }, [token]);
}

The status state drives your UI — show a spinner while verifying, a success message on completion, or an error state if the token is invalid or expired.

Password Reset

The password reset flow works the same way — Directus emails the user a reset link with a token, they land on your reset page, and you POST the new password along with the token to Directus’s reset endpoint.

const handleSubmit = async (e: React.FormEvent) => {
  e.preventDefault();
  if (password !== confirm) {
    setError('Passwords do not match.');
    return;
  }
  setLoading(true);
  setError(null);

  try {
    const response = await fetch(
      `${import.meta.env.VITE_DIRECTUS_URL}/auth/password/reset`,
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ token, password }),
      },
    );

    if (response.ok || response.status === 204) {
      navigate({ to: '/auth/login' });
    } else {
      setError('Failed to reset password. The link may have expired.');
    }
  } catch {
    setError('Something went wrong. Please try again.');
  } finally {
    setLoading(false);
  }
};

A 204 response means success — redirect the user to login. Handle expired tokens gracefully with a clear error message.

What Directus Handles For You

  • ✅ Sending the verification email — via your SMTP provider configured in Docker Compose
  • ✅ Generating and validating tokens
  • ✅ Sending the password reset email
  • ✅ Session management via cookies
  • ✅ Secure password storage

All you need is your SMTP credentials added to your Docker Compose file. I use Resend for email delivery — it’s straightforward to set up and works reliably. Everything else is handled by Directus out of the box.

This is the auth setup I use across all my TanStack Start projects. Combined with Directus roles and permissions, it covers everything a production app needs without adding a separate auth service to your stack.

Have questions about the setup? Drop a comment below.