supabase / supabase-flutter

Flutter integration for Supabase. This package makes it simple for developers to build secure and scalable products.
https://supabase.com/
MIT License
742 stars 183 forks source link

sign out fails after user is deleted by edge function #1083

Closed AhmedKhDev closed 1 week ago

AhmedKhDev commented 2 weeks ago

Describe the bug I want to implement a feature in my the allows the user to delete his own profile. So i am using an edge function to delete the user with auth.admin.deleteUser but sign out after the delete fails

To Reproduce Steps to reproduce the behavior:

  1. make an edge function to delete the user
    
    import { createClient } from "jsr:@supabase/supabase-js@2";

Deno.serve(async (req: Request) => { try { // Create instance of SupabaseClient using JSR import const supabaseAdmin = createClient( Deno.env.get("SUPABASE_URL") ?? "", Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? "", { auth: { autoRefreshToken: false, persistSession: false, }, }, );

// Get the authorization header from the request
const authHeader = req.headers.get("Authorization");
if (!authHeader) {
  return new Response(
    JSON.stringify({ error: "No authorization header provided" }),
    {
      headers: { "Content-Type": "application/json" },
      status: 401,
    },
  );
}

// Get JWT from auth header
const jwt = authHeader.replace("Bearer ", "");

// Verify and decode the JWT to get the user ID
const { data: { user }, error: decodeError } = await supabaseAdmin.auth
  .getUser(jwt);

if (decodeError || !user) {
  return new Response(
    JSON.stringify({
      error: decodeError?.message ||
        "Failed to decode user from token",
    }),
    {
      headers: { "Content-Type": "application/json" },
      status: 400,
    },
  );
}

// Delete the user using admin API
const { error: deleteError } = await supabaseAdmin.auth.admin
  .deleteUser(user.id);

if (deleteError) {
  return new Response(
    JSON.stringify({ error: deleteError.message }),
    {
      headers: { "Content-Type": "application/json" },
      status: 400,
    },
  );
}

return new Response(
  JSON.stringify({
    message: "User deleted successfully",
    userId: user.id,
  }),
  {
    headers: { "Content-Type": "application/json" },
    status: 200,
  },
);

} catch (error) { console.error("Error in delete user function:", error); return new Response( JSON.stringify({ error: error instanceof Error ? error.message : "Unknown error occurred", }), { headers: { "Content-Type": "application/json" }, status: 500, }, ); } });

2. deploy to your supabase project
3. call the function on your flutter app 
```dart
Future<void> deleteRemoteUser() async {
    try {
      // Get the current session
      final session = supabaseClient.auth.currentSession;
      if (session == null) {
        throw ServerException(message: 'No active session found');
      }

      // Call the Edge function to delete the user
      final response = await supabaseClient.functions.invoke(
        'delete_user_account',
        method: HttpMethod.post,
        headers: {
          'Authorization': 'Bearer ${session.accessToken}',
          'Content-Type': 'application/json',
        },
      );

      if (response.status != 200) {
        final error = response.data['error'] ?? 'Failed to delete account';
        throw ServerException(message: error);
      }
      await supabaseClient.auth.signOut();
    } on FunctionException catch (e) {
      throw ServerException(
        message:
            'Account deletion failed: ${e.details?['error'] ?? e.toString()}',
      );
    } on AuthException catch (authError) {
      throw ServerException(message: authError.message);
    } catch (error) {
      throw ServerException(message: error.toString());
    }
  }
  1. See error AuthApiException (AuthException(message: User from sub claim in JWT does not exist, statusCode: 403, errorCode: user_not_found))

Expected behavior user should be signed out with no errors.

Version (please complete the following information): macOS ├── supabase_flutter 2.8.0 │ ├── supabase 2.5.0 │ │ ├── functions_client 2.4.0 │ │ ├── gotrue 2.10.0 │ │ ├── postgrest 2.3.0 │ │ ├── realtime_client 2.4.0 │ │ ├── storage_client 2.2.0

Vinzent03 commented 1 week ago

You may try passing SignOutScope.local to the auth.signOut() method to only remove the session locally and not invalidate any other no more existing sessions.

AhmedKhDev commented 1 week ago

You may try passing SignOutScope.local to the auth.signOut() method to only remove the session locally and not invalidate any other no more existing sessions.

gives the same error

Vinzent03 commented 1 week ago

I see now in the code from the signOut method, that the exception should actually be caught. I guess you are using vscode and you have enabled the option to stop on all exceptions in the debugger. Please turn that off and verify that your call to auth.signOut() actually throws.

AhmedKhDev commented 1 week ago

It doesn't throw if i turm off to stop on All Exceptions. thank you