Implement OTP-based email verification system
Add custom email verification using 6-digit OTP codes via Mailketing API: Database: - Create auth_otps table with 15-minute expiry - Add indexes and RLS policies for security - Add cleanup function for expired tokens - Insert default auth_email_verification template Edge Functions: - send-auth-otp: Generate OTP, store in DB, send via Mailketing - verify-auth-otp: Validate OTP, confirm email in Supabase Auth Frontend: - Add OTP input state to auth page - Implement send/verify OTP in useAuth hook - Add resend countdown timer (60 seconds) - Update auth flow: signup → OTP verification → login Features: - Instant email delivery (no queue/cron) - 6-digit OTP with 15-minute expiry - Resend OTP with cooldown - Admin-configurable email templates - Indonesian UI text 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,8 +8,10 @@ interface AuthContextType {
|
||||
loading: boolean;
|
||||
isAdmin: boolean;
|
||||
signIn: (email: string, password: string) => Promise<{ error: Error | null }>;
|
||||
signUp: (email: string, password: string, name: string) => Promise<{ error: Error | null }>;
|
||||
signUp: (email: string, password: string, name: string) => Promise<{ error: Error | null; data?: { user?: User; session?: Session } }>;
|
||||
signOut: () => Promise<void>;
|
||||
sendAuthOTP: (userId: string, email: string) => Promise<{ success: boolean; message: string }>;
|
||||
verifyAuthOTP: (userId: string, otpCode: string) => Promise<{ success: boolean; message: string }>;
|
||||
}
|
||||
|
||||
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
||||
@@ -87,7 +89,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
const signUp = async (email: string, password: string, name: string) => {
|
||||
const redirectUrl = `${window.location.origin}/`;
|
||||
const { error } = await supabase.auth.signUp({
|
||||
const { data, error } = await supabase.auth.signUp({
|
||||
email,
|
||||
password,
|
||||
options: {
|
||||
@@ -95,15 +97,69 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
data: { name }
|
||||
}
|
||||
});
|
||||
return { error };
|
||||
return { error, data };
|
||||
};
|
||||
|
||||
const signOut = async () => {
|
||||
await supabase.auth.signOut();
|
||||
};
|
||||
|
||||
const sendAuthOTP = async (userId: string, email: string) => {
|
||||
try {
|
||||
const { data: { session } } = await supabase.auth.getSession();
|
||||
|
||||
const response = await fetch(
|
||||
`${import.meta.env.VITE_SUPABASE_URL}/functions/v1/send-auth-otp`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${session?.access_token || import.meta.env.VITE_SUPABASE_ANON_KEY}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ user_id: userId, email }),
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
return result;
|
||||
} catch (error: any) {
|
||||
console.error('Error sending OTP:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: error.message || 'Failed to send OTP'
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const verifyAuthOTP = async (userId: string, otpCode: string) => {
|
||||
try {
|
||||
const { data: { session } } = await supabase.auth.getSession();
|
||||
|
||||
const response = await fetch(
|
||||
`${import.meta.env.VITE_SUPABASE_URL}/functions/v1/verify-auth-otp`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${session?.access_token || import.meta.env.VITE_SUPABASE_ANON_KEY}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ user_id: userId, otp_code: otpCode }),
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
return result;
|
||||
} catch (error: any) {
|
||||
console.error('Error verifying OTP:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: error.message || 'Failed to verify OTP'
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ user, session, loading, isAdmin, signIn, signUp, signOut }}>
|
||||
<AuthContext.Provider value={{ user, session, loading, isAdmin, signIn, signUp, signOut, sendAuthOTP, verifyAuthOTP }}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user