Skip to content

Auth.deleteUser() always fails: DELETE /v1/me is sent unauthenticated (401 signed_out) #451

Description

@jordivilaga

Steps to reproduce

  1. In the Clerk dashboard, enable Allow users to delete their account so env.user.actions.deleteSelf is true.
  2. Build an Auth with a publishable key and a persistor, and await auth.initialize().
  3. Sign in with auth.attemptSignIn(strategy: Strategy.password, identifier: ..., password: ...), and confirm auth.isSignedIn is true.
  4. Call await auth.deleteUser().
  5. Sign in again with the same credentials.

Expected results

The account is deleted. Step 5 fails because the user no longer exists.

Actual results

deleteUser() returns without throwing, but the account is untouched and step 5 signs in normally.

The request reaches Clerk with no credentials. Captured from the SDK's own logSevere (visible by attaching a listener to Logger.root):

HTTP error on DELETE /me: 401
{
"errors": [
{ "message": "Signed out", "long_message": "You are signed out", "code": "signed_out" }
],
"clerk_trace_id": "..."
}

Cause: Api._delete clears the token cache before building the request, so the two conditions that attach the credentials are both false
by the time they are evaluated — _headers adds Authorization only if (_tokenCache.hasClientToken), and _queryParams adds
_clerk_session_id only if (withSession && _multiSessionMode && sessionId.isNotEmpty).

Clearing the cache after a successful response instead of before the request should fix it.

Code sample

Code sample
  final auth = Auth(                                                                                                                       
    config: AuthConfig(publishableKey: '<pk_test_...>', persistor: persistor),                                                             
  );

await auth.initialize();                                                                                                                 
                                                                                                                                           
  await auth.attemptSignIn(                                                                                                                
    strategy: Strategy.password,                                                                                                           
    identifier: 'ada@example.com',                                                                                                         
    password: '<password>',                                                                                                                
  );                                                                                                                                       
  assert(auth.isSignedIn);                                                                                                                 
                                                                                                                                           
  await auth.deleteUser();   // returns normally

// The account is still there:                                                                                                           
  await auth.attemptSignIn(                                                                                                                
    strategy: Strategy.password,                                                                                                           
    identifier: 'ada@example.com',                                                                                                         
    password: '<password>',                                                                                                                
  );                                                                                                                                       
  assert(auth.isSignedIn);   // passes, but should not                                                                                     
                                                                                                                                           
  The offending order, in lib/src/clerk_api/api.dart:

Future<bool> _delete(String path, {bool requiresSessionId = false}) async {                                                              
    _tokenCache.clear();                                  // clientToken, sessionId, clientId                                              
    try {                                                                                                                                  
      final headers = _headers(method: HttpMethod.delete);                                                                                 
      final resp = await _fetch(                                                                                                           
        method: HttpMethod.delete,                                                                                                         
        path: path,                                                                                                                        
        headers: headers,                                                                                                                  
        withSession: requiresSessionId,                                                                                                    
      );  

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[Paste your output here]

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions