Authentication
Tether uses Supabase for authentication, providing secure and scalable user management out of the box.
Overview
Tether supports multiple authentication methods through Supabase:
- Email and password
- Magic links (passwordless)
- OAuth providers (Google, GitHub, etc.)
- Phone authentication
Setup Authentication
First, ensure your Supabase project is configured in .env.local:
1NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
2NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_keyEmail/Password Authentication
Sign Up
Create a new user with email and password:
1import { supabase } from '@/lib/supabase/client';
2
3async function signUp(email: string, password: string) {
4 const { data, error } = await supabase.auth.signUp({
5 email,
6 password,
7 });
8
9 if (error) {
10 console.error('Error signing up:', error.message);
11 return null;
12 }
13
14 return data;
15}Sign In
Authenticate an existing user:
1import { supabase } from '@/lib/supabase/client';
2
3async function signIn(email: string, password: string) {
4 const { data, error } = await supabase.auth.signInWithPassword({
5 email,
6 password,
7 });
8
9 if (error) {
10 console.error('Error signing in:', error.message);
11 return null;
12 }
13
14 return data;
15}Sign Out
Log out the current user:
1import { supabase } from '@/lib/supabase/client';
2
3async function signOut() {
4 const { error } = await supabase.auth.signOut();
5
6 if (error) {
7 console.error('Error signing out:', error.message);
8 }
9}OAuth Authentication
Enable social login with OAuth providers:
1import { supabase } from '@/lib/supabase/client';
2
3async function signInWithGitHub() {
4 const { data, error } = await supabase.auth.signInWithOAuth({
5 provider: 'github',
6 options: {
7 redirectTo: `${window.location.origin}/auth/callback`,
8 },
9 });
10
11 if (error) {
12 console.error('Error with OAuth:', error.message);
13 }
14}
15
16// Also available: 'google', 'gitlab', 'bitbucket', etc.Get Current User
Retrieve the currently authenticated user:
1import { supabase } from '@/lib/supabase/client';
2
3async function getCurrentUser() {
4 const { data: { user } } = await supabase.auth.getUser();
5 return user;
6}
7
8// Or get the session
9async function getSession() {
10 const { data: { session } } = await supabase.auth.getSession();
11 return session;
12}Protected Routes
Tether uses cookie-based auth with @supabase/ssr. Middleware refreshes the session; server components check the user and redirect when needed.
1// middleware.ts - refresh session (required for cookie-based auth)
2import { createServerClient } from '@supabase/ssr';
3import { NextResponse, type NextRequest } from 'next/server';
4
5export async function middleware(request: NextRequest) {
6 let response = NextResponse.next({ request });
7
8 const supabase = createServerClient(
9 process.env.NEXT_PUBLIC_SUPABASE_URL!,
10 process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
11 {
12 cookies: {
13 getAll: () => request.cookies.getAll(),
14 setAll: (cookies) =>
15 cookies.forEach(({ name, value, options }) =>
16 response.cookies.set(name, value, options)
17 ),
18 },
19 }
20 );
21
22 await supabase.auth.getUser(); // Refresh session if expired
23 return response;
24}
25
26export const config = {
27 matcher: ['/((?!_next/static|_next/image|favicon.ico|.*\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'],
28};In protected pages or layouts, use the server client to check auth and redirect:
1// app/dashboard/page.tsx (Server Component)
2import { createClient } from '@/lib/supabase/server';
3import { redirect } from 'next/navigation';
4
5export default async function DashboardPage() {
6 const supabase = await createClient();
7 const { data: { user } } = await supabase.auth.getUser();
8
9 if (!user) {
10 redirect('/login');
11 }
12
13 return <div>Welcome, {user.email}</div>;
14}Auth State Changes
Listen for authentication state changes:
1import { useEffect } from 'react';
2import { supabase } from '@/lib/supabase/client';
3
4function useAuth() {
5 useEffect(() => {
6 const { data: { subscription } } = supabase.auth.onAuthStateChange(
7 (event, session) => {
8 console.log('Auth event:', event);
9 console.log('Session:', session);
10
11 if (event === 'SIGNED_IN') {
12 // Handle sign in
13 } else if (event === 'SIGNED_OUT') {
14 // Handle sign out
15 }
16 }
17 );
18
19 return () => {
20 subscription.unsubscribe();
21 };
22 }, []);
23}Best Practices
- Always validate user input before authentication
- Use secure password requirements (min length, complexity)
- Implement rate limiting to prevent brute force attacks
- Store sensitive data only in environment variables
- Use HTTPS in production
- Implement proper error handling and user feedback
⚠️ Security Note
Never expose your service role key or other sensitive credentials in client-side code. Always use the anon key for client-side operations.