Skip to content

Oauth2 Failed - Android 15 #71

Description

@Infinil

Hi, I have been using pocketbase v0.22.19 in my flutter app with dart SDK v0.18.1 and have been relying on authWithOauth2() method. It still works quite well for 'apple' & 'google' both the providers with android 11 or recent iOS versions. But lately I have been noticing "Auth failed" webpage on redirect url if I am using android 15 (whether its physcial device or emulator). Quite often I notice Socket Exceptions which stem from realtime subscribe event for "@oauth2" I believe. Most of the times there are no error logs as well.

Here's my current implementation

class PocketBaseUserAuth extends _$PocketBaseUserAuth {
  late PocketBase pocketbaseInstance;

  @override
  Future<RecordAuth> build() async {
    pocketbaseInstance = ref.watch(pocketBaseInstanceProvider);
    return _authenticateFromStorage();
  }


  // Save auth data to persistent storage
  Future<void> _saveAuthData(PocketBase pb) async {
    final prefs = ref.read(sharedPreferencesProvider);
    await prefs.setString('pb_auth_token', pb.authStore.token);
    await prefs.setString('pb_auth_model', jsonEncode(pb.authStore.model?.toJson() ?? {}));
  }
  
  // Clear stored auth data
  Future<void> _clearStoredAuth() async {
    final prefs = ref.read(sharedPreferencesProvider);
    await prefs.remove('pb_auth_token');
    await prefs.remove('pb_auth_model');
  }


  Future<RecordAuth> _authenticateFromStorage() async {
    final pocketbaseInstance = ref.watch(pocketBaseInstanceProvider);
    final prefs = ref.read(sharedPreferencesProvider);
    
    // Try to load stored auth data
    final storedToken = prefs.getString('pb_auth_token');
    final storedModel = prefs.getString('pb_auth_model');

    if (storedToken != null && storedModel != null) {
      try {
        // Restore the auth store manually
        final modelData = jsonDecode(storedModel);
        pocketbaseInstance.authStore.save(storedToken, modelData);
        // Try to refresh the token to ensure it's still valid
        final authData = await pocketbaseInstance.collection(pocketBaseUsersId).authRefresh();
        
        // Update stored data with refreshed token
        await _saveAuthData(pocketbaseInstance);

        return authData;
      } catch (e) {
        // Token is invalid, clear stored data
        await _clearStoredAuth();
        pocketbaseInstance.authStore.clear();
      }
    }
    
    // No valid auth found
    throw const NotFoundException(message: 'No valid authentication found');
  }


  Future<void> _clearAll() async {
    // Clear stored auth data
    await _clearStoredAuth();
    
    // Clear PocketBase auth
    pocketbaseInstance.authStore.clear();
    
    //Unsub from all realtime connections for no auth mismatch
    pocketbaseInstance.realtime.unsubscribe();
  }

  Future<RecordAuth> _authenticateWithProvider(String providerId) async {
    try {
      print('Starting OAuth authentication...');
      
      final recordAuth = await pocketbaseInstance
          .collection(pocketBaseUsersId)
          .authWithOAuth2(providerId, (url) async {
            print('Launching OAuth URL: $url');
            
            await launchUrl(
              Uri.parse(url.toString()),
              mode: LaunchMode.inAppBrowserView,
            );
          });
      
      print('OAuth completed successfully');
      return recordAuth;
      
    } catch (e) {
      print('OAuth error occurred: $e');
      
      // Wait a moment for any background processes to complete
      await Future.delayed(const Duration(seconds: 3));
      
      // Check if authentication actually succeeded despite the error
      if (pocketbaseInstance.authStore.isValid) {
        print('User is authenticated despite the error!');
        
        // Create a RecordAuth object from the auth store
        return RecordAuth(
          token: pocketbaseInstance.authStore.token,
          record: pocketbaseInstance.authStore.model,
        );
      }
      
      print('Authentication actually failed');
      rethrow;
    }
  }


  Future<void> signIn(AuthProvider authProvider) async {
    // Clear all data
    await _clearAll();

    if (authProvider != AuthProvider.email) {
      final recordAuth = await _authenticateWithProvider(authProvider.name);
      print('reached till here 3');
      _saveAuthData(pocketbaseInstance);
      print('reached till here 4');
      state = AsyncData(recordAuth);
      print('reached till here 5');
    }
  }


  Future<void> signOut() async {
    // Clear all data
    await _clearAll();
    
    // Clear the state
    state = const AsyncValue.error(NotFoundException(), StackTrace.empty);
  }
}

The function in question is _authenticateWithProvider
Any possible solutions or hints on where I am supposed to dig deeper?

Version, etc.

Flutter version 3.29.2 on channel stable
Framework - revision c23637390
Engine - revision 18b71d647a
Tools - Dart 3.7.2 - DevTools 2.42.3
pocketbase: ^0.18.1

Metadata

Metadata

Assignees

No one assigned

    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