DEV Community

Cover image for Directus Auth out of the Box — Registration, Login, Email Verification and Password Reset
Wade Thomas
Wade Thomas

Posted on

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),
  );
}
Enter fullscreen mode Exit fullscreen mode

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: noreply@yourdomain.com
Enter fullscreen mode Exit fullscreen mode

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 });
}
Enter fullscreen mode Exit fullscreen mode

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]);
}
Enter fullscreen mode Exit fullscreen mode

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);
  }
};
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)