Nextbase Docs
Supabase supports login with Magic Link. This is a secure way to authenticate users without requiring them to remember a password.
This server action below is used to login with Magic Link.
'use server'; import { createSupabaseUserServerActionClient } from '@/supabase-clients/user/createSupabaseUserServerActionClient'; import { AuthProvider } from '@/types'; import { toSiteURL } from '@/utils/helpers'; export const signInWithMagicLink = async (email: string, next?: string) => { const supabase = createSupabaseUserServerActionClient(); const redirectUrl = new URL(toSiteURL('/auth/callback')); if (next) { redirectUrl.searchParams.set('next', next); } const { error } = await supabase.auth.signInWithOtp({ email, options: { emailRedirectTo: redirectUrl.toString(), }, }); if (error) { console.log(error); throw error; } };
'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 /> </div> )} </div> ); }