# Sign In

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

*Canonical: https://makerkit.dev/docs/nextjs-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.

**Page location:** `apps/web/app/[locale]/auth/sign-in/page.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.local" %}
# Enable email/password sign in (default: true)
NEXT_PUBLIC_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.local" %}
# Enable magic link sign in (default: false)
NEXT_PUBLIC_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 %}

{% alert type="warning" title="Unverified users" %}
If the user registered with a password but never clicked the verification link, signing in by magic link (or email OTP) removes their existing password, linked accounts, and sessions before creating the new session, then marks the email verified. They must reset their password to use it again. Verifying via the email link instead keeps the password.
{% /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.local" %}
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

Edit the auth layout at `apps/web/app/[locale]/auth/layout.tsx`:

```tsx {% title="apps/web/app/[locale]/auth/layout.tsx" %}
export default function AuthLayout({ children }) {
  return (
    <AuthLayoutWrapper>
      <YourCustomBranding />
      {children}
    </AuthLayoutWrapper>
  );
}
```

### 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 `NEXT_PUBLIC_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)
