Login with Magic Link

Supabase supports login with Password too.

Server action

This server action below is used to login with Magic Link.

src/data/auth/auth.ts
'use server';
import { createSupabaseUserServerActionClient } from '@/supabase-clients/user/createSupabaseUserServerActionClient';
import { AuthProvider } from '@/types';
import { toSiteURL } from '@/utils/helpers';
 
export const signInWithPassword = async (email: string, password: string) => {
  const supabase = createSupabaseUserServerActionClient();
  const { error } = await supabase.auth.signInWithPassword({
    email,
    password,
  });
 
  if (error) {
    throw error;
  }
};

Usage

src/.../Login/page.tsx
'use client';
import { Email } from '@/components/presentational/tailwind/Auth/Email';
import { EmailAndPassword } from '@/components/presentational/tailwind/Auth/EmailAndPassword';
import { RenderProviders } from '@/components/presentational/tailwind/Auth/RenderProviders';
import {
  signInWithMagicLink,
  signInWithPassword,
  signInWithProvider,
} from '@/data/auth/auth';
import { useToastMutation } from '@/hooks/useToastMutation';
import { AuthProvider } from '@/types';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
 
export function Login({
  next,
  nextActionType,
}: {
  next?: string;
  nextActionType?: string;
}) {
  const [successMessage, setSuccessMessage] = useState<string | null>(null);
 
  const router = useRouter();
 
  function redirectToDashboard() {
    router.refresh();
    if (next) {
      router.push(`/auth/callback?next=${next}`);
    } else {
      router.push('/auth/callback');
    }
  }
  const magicLinkMutation = useToastMutation(
    async (email: string) => {
      return await signInWithMagicLink(email, next);
    },
    {
      loadingMessage: 'Sending magic link...',
      errorMessage: 'Failed to send magic link',
      successMessage: 'Magic link sent!',
      onSuccess: () => {
        setSuccessMessage('A magic link has been sent to your email!');
      },
      onError: (error) => {
        console.log(error);
      },
      onMutate: () => {
        setSuccessMessage(null);
      },
    },
  );
  const passwordMutation = useToastMutation(
    async ({ email, password }: { email: string; password: string }) => {
      return await signInWithPassword(email, password);
    },
    {
      onSuccess: redirectToDashboard,
      loadingMessage: 'Logging in...',
      errorMessage: 'Failed to login',
      successMessage: 'Logged in!',
    },
  );
  const providerMutation = useToastMutation(
    async (provider: AuthProvider) => {
      return signInWithProvider(provider, next);
    },
    {
      loadingMessage: 'Requesting login...',
      successMessage: 'Redirecting...',
      errorMessage: 'Failed to login',
    },
  );
  return (
    <div className="container h-full grid items-center text-left max-w-lg mx-auto overflow-auto">
      {successMessage ? (
        <p className="text-blue-500 text-sm">{successMessage}</p>
      ) : (
        <div className="space-y-8 ">
          <div className="flex flex-col items-start gap-0 w-[320px]">
            <h1 className="text-xl font-[700]">Login to Nextbase</h1>
            <p className="text-base text-left font-[400]">
              Login with the account you used to signup.
            </p>
          </div>
          <RenderProviders
            providers={['google', 'github', 'twitter']}
            isLoading={providerMutation.isLoading}
            onProviderLoginRequested={providerMutation.mutate}
          />
          <hr />
          <Email
            onSubmit={magicLinkMutation.mutate}
            isLoading={magicLinkMutation.isLoading}
            view="sign-in"
          />
          <hr />
          <EmailAndPassword
            isLoading={passwordMutation.isLoading}
            onSubmit={(data) => {
              passwordMutation.mutate(data);
            }}
            view="sign-in"
          />
        </div>
      )}
    </div>
  );
}