UNPKG

nuxt-supabase-team-auth

Version:

Drop-in Nuxt 3 module for team-based authentication with Supabase

217 lines (193 loc) 6.12 kB
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts' import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' interface RevokeInvitationRequest { userId: string teamId: string } serve(async (req) => { console.log('=== REVOKE INVITATION EDGE FUNCTION CALLED ===') console.log('Method:', req.method) // Handle OPTIONS requests if (req.method === 'OPTIONS') { return new Response('ok') } try { // Get user from request const authHeader = req.headers.get('Authorization') if (!authHeader) { return new Response( JSON.stringify({ error: 'Missing authorization header' }), { status: 401, headers: { 'Content-Type': 'application/json' }, }, ) } // Initialize Supabase clients const supabaseAdmin = createClient( Deno.env.get('SUPABASE_URL') ?? '', Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? '', { auth: { autoRefreshToken: false, persistSession: false, }, }, ) const supabaseUser = createClient( Deno.env.get('SUPABASE_URL') ?? '', Deno.env.get('SUPABASE_ANON_KEY') ?? '', { auth: { autoRefreshToken: false, persistSession: false, }, global: { headers: { Authorization: authHeader, }, }, }, ) // Get current user const { data: { user }, error: userError } = await supabaseUser.auth.getUser() if (userError || !user) { return new Response( JSON.stringify({ error: 'Invalid or expired token' }), { status: 401, headers: { 'Content-Type': 'application/json' }, }, ) } // Parse request body const { userId, teamId }: RevokeInvitationRequest = await req.json() console.log('Revoke invitation request:', { userId, teamId }) if (!userId || !teamId) { console.log('Missing required fields:', { userId: !!userId, teamId: !!teamId }) return new Response( JSON.stringify({ error: 'Missing required fields: userId, teamId', debug: { userId: !!userId, teamId: !!teamId, receivedData: { userId, teamId } }, }), { status: 400, headers: { 'Content-Type': 'application/json' }, }, ) } // Verify user has permission to revoke invitations (admin or owner) const { data: membership, error: membershipError } = await supabaseAdmin .from('team_members') .select('role') .eq('team_id', teamId) .eq('user_id', user.id) .single() if (membershipError || !membership) { return new Response( JSON.stringify({ error: 'User is not a member of this team' }), { status: 403, headers: { 'Content-Type': 'application/json' }, }, ) } if (!['owner', 'admin', 'super_admin'].includes(membership.role)) { return new Response( JSON.stringify({ error: 'Only owners, admins, and super_admins can revoke invitations' }), { status: 403, headers: { 'Content-Type': 'application/json' }, }, ) } // Get the user to verify it's an unconfirmed invitation for this team const { data: targetUser, error: getUserError } = await supabaseAdmin.auth.admin.getUserById(userId) if (getUserError || !targetUser.user) { return new Response( JSON.stringify({ error: 'User not found' }), { status: 404, headers: { 'Content-Type': 'application/json' }, }, ) } // Verify this is an unconfirmed invitation for the specified team console.log('=== FULL USER OBJECT DEBUG ===') console.log('targetUser structure:', JSON.stringify(targetUser, null, 2)) console.log('targetUser.user:', JSON.stringify(targetUser.user, null, 2)) // Check if user is actually a member of the team const { data: targetMembership, error: _targetMembershipError } = await supabaseAdmin .from('team_members') .select('id') .eq('team_id', teamId) .eq('user_id', userId) .single() if (targetMembership) { console.log('User is already a team member') return new Response( JSON.stringify({ error: 'User is already a team member. Remove them from the team instead.', }), { status: 400, headers: { 'Content-Type': 'application/json' }, }, ) } if (targetUser.user.user_metadata?.team_id !== teamId) { console.log('Team ID mismatch:', { userTeamId: targetUser.user.user_metadata?.team_id, requestedTeamId: teamId, }) return new Response( JSON.stringify({ error: 'Invitation is not for this team' }), { status: 400, headers: { 'Content-Type': 'application/json' }, }, ) } // Delete the unconfirmed user (this revokes the invitation) const { error: deleteError } = await supabaseAdmin.auth.admin.deleteUser(userId) if (deleteError) { return new Response( JSON.stringify({ error: 'Failed to revoke invitation', details: deleteError.message, }), { status: 500, headers: { 'Content-Type': 'application/json' }, }, ) } // Return success response return new Response( JSON.stringify({ success: true, message: 'Invitation revoked successfully', revoked_user: { id: targetUser.user.id, email: targetUser.user.email, }, }), { headers: { 'Content-Type': 'application/json' }, }, ) } catch (error) { console.error('Unexpected error in revoke-invitation:', error) return new Response( JSON.stringify({ error: 'Internal server error', message: error instanceof Error ? error.message : 'Unknown error', }), { status: 500, headers: { 'Content-Type': 'application/json' }, }, ) } })