# Sign In

> Sign in with email/password, magic link, or social providers. Configure MFA and customize the authentication flow.

*Canonical: https://makerkit.dev/docs/tanstack-drizzle/authentication/sign-in*

---

{% sequence title="Sign In Methods" description="Available authentication methods in the kit." %}

[Email/Password Sign In](#emailpassword-sign-in)

[Magic Link Sign In](#magic-link-sign-in)

[Social Provider Sign In](#social-provider-sign-in)

[MFA Challenge](#mfa-challenge)

{% /sequence %}

The sign in page supports multiple authentication methods that can be enabled or disabled independently. Users see only the methods you've configured. This guide applies to the [Drizzle](/drizzle) kit.

**Route file:** `apps/web/src/routes/auth/sign-in.tsx`
**Route:** `/auth/sign-in`

## Email/Password Sign In

The default authentication method. Users enter their email and password to sign in.

{% img src="/images/docs/sign-in-email-password.webp" width="2388" height="1798" alt="Email and password sign in form showing email field, password field, forgot password link, and sign in button" /%}

### Configuration

```bash {% title="apps/web/.env" %}
# Enable email/password sign in (default: true)
VITE_AUTH_PASSWORD=true
```

### Component

The sign in form is rendered by `SignInMethodsContainer`:

```tsx {% title="packages/auth/src/components/sign-in-methods-container.tsx" %}
<SignInMethodsContainer
  paths={{
    returnPath: '/dashboard',
  }}
  providers={{
    password: true,
    magicLink: false,
    passkey: false,
    oAuth: ['google'],
  }}
/>
```

### Flow

1. User enters email and password
2. Better Auth validates credentials against the database
3. If MFA is enabled for the user, they're redirected to `/auth/verify`
4. On success, a session cookie is set and user is redirected to the dashboard

## Magic Link Sign In

Passwordless authentication via email link. Disabled by default.

{% img src="/images/docs/sign-in-magic-link.webp" width="2388" height="1798" alt="Magic link sign in form showing email field and send magic link button" /%}

### Configuration

```bash {% title="apps/web/.env" %}
# Enable magic link sign in (default: false)
VITE_AUTH_MAGIC_LINK=true
```

### How It Works

1. User enters their email address
2. Server sends an email with a secure, time-limited link
3. User clicks the link to authenticate
4. Session is created automatically

{% alert type="default" title="MFA bypass" %}
Magic link authentication bypasses MFA. If you require MFA for all users, consider disabling magic links. This is by design since the email itself serves as a second factor (possession of the email account).
{% /alert %}

### Email Configuration

Magic link emails are sent using your configured mailer. In development, the link is also logged to the console:

```bash
[DEV] Magic link for user@example.com: http://localhost:3000/api/auth/magic-link/verify?token=...
```

For production, configure your [email provider](../emails/configuration).

## Social Provider Sign In

OAuth authentication with Google by default, plus any additional providers you wire into `packages/better-auth/src/plugins/social-providers.ts`.

{% img src="/images/docs/social-providers.webp" width="2388" height="1798" alt="Social provider sign in buttons showing Continue with Google option" /%}

### Google Configuration

```bash {% title="apps/web/.env" %}
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-client-secret
```

To get these credentials:

1. Go to [Google Cloud Console](https://console.cloud.google.com/)
2. Create or select a project
3. Navigate to **APIs & Services > Credentials**
4. Create an **OAuth 2.0 Client ID**
5. Add `http://localhost:3000/api/auth/callback/google` to authorized redirect URIs
6. For production, add your production callback URL

### Adding More Providers

Edit `packages/better-auth/src/plugins/social-providers.ts` to add providers:

```typescript {% title="packages/better-auth/src/plugins/social-providers.ts" %}
export function createSocialProviderPlugin(): SocialProviders {
  const googleSocialProviderPlugin = createGoogleProviderPlugin();

  return {
    ...googleSocialProviderPlugin,
    // Add GitHub (requires GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET env vars)
    github: {
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    },
  };
}
```

See the [Better Auth Social Providers documentation](https://www.better-auth.com/docs/authentication/social-sign-in) for the full list of supported providers.

## MFA Challenge

When a user with MFA enabled signs in with email/password, they're redirected to a verification page.

{% img src="/images/docs/verify-mfa.webp" width="2388" height="1798" alt="MFA verification form showing 6-digit code input field" /%}

### How It Works

1. User signs in with email/password
2. Better Auth detects MFA is enabled for this user
3. User is redirected to `/auth/verify`
4. User enters the 6-digit code from their authenticator app
5. On success, session is created and user is redirected

### Enabling MFA

Users enable MFA from their security settings at `/settings/security`. See the [MFA documentation](./mfa) for details.

### Client-Side Handling

The auth client automatically handles MFA redirects:

```typescript {% title="packages/better-auth/src/auth-client.ts" %}
twoFactorClient({
  onTwoFactorRedirect() {
    const redirect = new URLSearchParams(location.search).get('redirect');
    window.location.href = '/auth/verify' + (redirect ? `?redirect=${redirect}` : '');
  },
}),
```

## Programmatic Sign In

For custom sign in flows, use the auth client directly:

```typescript
'use client';

import { authClient } from '@kit/better-auth/client';

async function handleSignIn(email: string, password: string) {
  const result = await authClient.signIn.email({
    email,
    password,
  });

  if (result.error) {
    console.error(result.error.message);
    return;
  }

  // User is signed in, redirect or update UI
  window.location.href = '/dashboard';
}
```

## Customizing the Sign In Page

### Change the Layout

The auth screens share a pathless layout route at `apps/web/src/routes/auth/route.tsx`. Its `beforeLoad` runs `redirectIfAuthenticated` (so signed-in users skip the auth screens) and it renders the shared `AuthLayoutShell` around the child `<Outlet />`:

```tsx {% title="apps/web/src/routes/auth/route.tsx" %}
import { Outlet, createFileRoute } from '@tanstack/react-router';

import { AuthLayoutShell } from '@kit/auth/shared';

import { AppLogo } from '#/components/app-logo.tsx';
import { redirectIfAuthenticated } from '#/lib/auth/guards';

export const Route = createFileRoute('/auth')({
  beforeLoad: redirectIfAuthenticated,
  component: AuthLayout,
});

function AuthLayout() {
  return (
    <AuthLayoutShell Logo={AppLogo}>
      <Outlet />
    </AuthLayoutShell>
  );
}
```

### Add Terms of Service

The sign in form can display a terms of service checkbox. Configure it in the `SignInMethodsContainer` props.

## Common Issues

### "Invalid credentials" Error

1. Verify the user exists in the database
2. Check that the password is correct
3. Ensure email verification is complete (if required)

### Social Sign In Redirect Fails

1. Verify callback URLs match in provider settings
2. Check that client ID and secret are correct
3. Ensure `VITE_SITE_URL` matches your domain

### Magic Link Not Received

1. Check spam/junk folder
2. Verify mailer configuration
3. In development, check the console for the logged link

---

**Previous:** [Authentication Overview ←](./overview) | **Next:** [Sign Up →](./sign-up)
