diff --git a/src/lib/identityService.ts b/src/lib/identityService.ts index e5ea27b0..e8fb31c0 100644 --- a/src/lib/identityService.ts +++ b/src/lib/identityService.ts @@ -218,12 +218,40 @@ export async function addPasswordToAccount( const { error: updateError } = await supabase.auth.updateUser({ password }); if (updateError) throw updateError; - // Step 2: Log the password addition + // Step 2: Get user profile for email personalization + console.log('[IdentityService] Fetching user profile for email'); + const { data: profile } = await supabase + .from('profiles') + .select('display_name, username') + .eq('user_id', user!.id) + .single(); + + // Step 3: Send password addition email (before logging out) + console.log('[IdentityService] Sending password addition email'); + try { + const { error: emailError } = await supabase.functions.invoke('send-password-added-email', { + body: { + email: userEmail, + displayName: profile?.display_name, + username: profile?.username, + }, + }); + + if (emailError) { + console.error('[IdentityService] Failed to send email:', emailError); + } else { + console.log('[IdentityService] Email sent successfully'); + } + } catch (emailError) { + console.error('[IdentityService] Email sending error:', emailError); + } + + // Step 4: Log the password addition await logIdentityChange(user!.id, 'password_added', { method: 'oauth_with_relogin_required' }); - // Step 3: Sign the user out so they can sign back in with email/password + // Step 5: Sign the user out so they can sign back in with email/password console.log('[IdentityService] Signing user out to force re-login'); await supabase.auth.signOut(); diff --git a/supabase/config.toml b/supabase/config.toml index 9a011d32..4998c2ef 100644 --- a/supabase/config.toml +++ b/supabase/config.toml @@ -1,5 +1,8 @@ project_id = "ydvtmnrszybqnbcqbdcy" +[functions.send-password-added-email] +verify_jwt = true + [functions.create-novu-subscriber] verify_jwt = true diff --git a/supabase/functions/send-password-added-email/index.ts b/supabase/functions/send-password-added-email/index.ts new file mode 100644 index 00000000..17479774 --- /dev/null +++ b/supabase/functions/send-password-added-email/index.ts @@ -0,0 +1,161 @@ +import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'; +import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'; + +const corsHeaders = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', +}; + +interface EmailRequest { + email: string; + displayName?: string; + username?: string; +} + +serve(async (req) => { + if (req.method === 'OPTIONS') { + return new Response(null, { headers: corsHeaders }); + } + + try { + const supabaseClient = createClient( + Deno.env.get('SUPABASE_URL') ?? '', + Deno.env.get('SUPABASE_ANON_KEY') ?? '', + { + global: { + headers: { Authorization: req.headers.get('Authorization')! }, + }, + } + ); + + const { data: { user }, error: userError } = await supabaseClient.auth.getUser(); + + if (userError || !user) { + throw new Error('Unauthorized'); + } + + const { email, displayName, username }: EmailRequest = await req.json(); + + if (!email) { + throw new Error('Email is required'); + } + + const recipientName = displayName || username || 'there'; + const siteUrl = Deno.env.get('SITE_URL') || 'https://thrillwiki.com'; + + const emailHTML = ` + + + + + + +
+
+

Password Successfully Added

+
+
+

Hi ${recipientName},

+ +

Great news! A password has been successfully added to your ThrillWiki account (${email}).

+ +

✅ What This Means

+

You now have an additional way to access your account. You can sign in using:

+ + +

🔐 Complete Your Setup

+

To activate your password authentication, please sign in with your new credentials:

+ + + Sign In Now + + +
+ ⚠️ Security Notice
+ If you didn't add a password to your account, please contact our support team immediately at support@thrillwiki.com +
+ +

Thanks for being part of the ThrillWiki community!

+ +

+ Best regards,
+ The ThrillWiki Team +

+
+ +
+ + + `; + + const forwardEmailKey = Deno.env.get('FORWARDEMAIL_API_KEY'); + const fromEmail = Deno.env.get('FROM_EMAIL_ADDRESS') || 'noreply@thrillwiki.com'; + + if (!forwardEmailKey) { + console.error('FORWARDEMAIL_API_KEY not configured'); + throw new Error('Email service not configured'); + } + + const emailResponse = await fetch('https://api.forwardemail.net/v1/emails', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Basic ${btoa(forwardEmailKey + ':')}`, + }, + body: JSON.stringify({ + from: fromEmail, + to: email, + subject: 'Password Added to Your ThrillWiki Account', + html: emailHTML, + }), + }); + + if (!emailResponse.ok) { + const errorText = await emailResponse.text(); + console.error('ForwardEmail API error:', errorText); + throw new Error(`Failed to send email: ${emailResponse.statusText}`); + } + + console.log(`Password addition email sent successfully to: ${email}`); + + return new Response( + JSON.stringify({ + success: true, + message: 'Password addition email sent successfully', + }), + { + status: 200, + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + } + ); + + } catch (error) { + console.error('Error in send-password-added-email function:', error); + + return new Response( + JSON.stringify({ + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + }), + { + status: 500, + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + } + ); + } +});