Jadi ceritanya aku lagi implement Facebook Login di Flutter pakai flutter_facebook_auth: ^7.1.5. Login berhasil, token ada, tapi pas di-pass ke Firebase malah error:

PlatformException(invalid-credential, ...)

Udah coba berbagai cara, mulai ganti versi package, reinstall, cek App Secret key di Firebase Console, tetap aja ditolak.

Implementasi Awal (Yang Broken)

Implementasi awal aku ngikutin cara yang aku coba dari beberapa tutorial di internet:

// Trigger login
final LoginResult loginResult = await FacebookAuth.instance.login(
  permissions: ['email', 'public_profile'],
);
final AccessToken? accessToken = loginResult.accessToken;
// Buat credential langsung dari token
final OAuthCredential credential =
    FacebookAuthProvider.credential(accessToken.tokenString);
// Sign in ke Firebase
await _firebaseAuth.signInWithCredential(credential);

Kelihatannya bener. Token ada, credential OAuth juga bisa dibuat, tapi waktu pass ke Firebase tetap ditolak dengan message “invalid-credential”.

MUMET!!!! Penyebabnya?

Ternyata masalahnya ada di tipe token yang dihasilkan.

flutter_facebook_auth ^7.0.+ di iOS secara default menggunakan Limited Login, yang menghasilkan LimitedToken — bukan ClassicToken seperti dulu. Dan LimitedToken tidak bisa dipakai dengan FacebookAuthProvider.credential().

LimitedToken harus dipakai sebagai idToken di dalam OAuthCredential. Dan satu lagi syaratnya yang wajib: nonce.

Tanpa nonce, Firebase nggak bisa memverifikasi token, jadi credential dianggap invalid.

Ngefixnyaa?? Gampang!

Ada dua perubahan dari implementasi lama:

1. Ganti FacebookAuthProvider.credential() ke OAuthCredential secara manual

2. Generate nonce sebelum login, hash -nya dikirim ke Facebook, versi mentahnya dikirim ke Firebase

String _generateNonce([int length = 32]) {
  const charset =
      '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-.';
  final random = Random.secure();
  return List.generate(length, (_) => charset[random.nextInt(charset.length)]).join();
}
String _sha256(String input) {
  final bytes = utf8.encode(input);
  return sha256.convert(bytes).toString();
}

Future<void> signInWithFacebook() async { // Generate nonce pair final String rawNonce = _generateNonce(); final String hashedNonce = _sha256(rawNonce); // Pass hashedNonce ke Facebook waktu login final LoginResult loginResult = await FacebookAuth.instance.login( loginTracking: LoginTracking.limited, nonce: hashedNonce, ); if (loginResult.status == LoginStatus.cancelled) return; if (loginResult.status == LoginStatus.failed) { throw Exception('Facebook login gagal: ${loginResult.message}'); } final AccessToken? accessToken = loginResult.accessToken; if (accessToken == null) throw Exception('Facebook access token null'); if (accessToken is! LimitedToken) { throw Exception('Expected LimitedToken, got ${accessToken.runtimeType}'); } // Buat OAuthCredential - rawNonce, bukan hashedNonce final OAuthCredential credential = OAuthCredential( providerId: 'facebook.com', signInMethod: 'oauth', idToken: accessToken.tokenString, rawNonce: rawNonce, // ← bukan hashedNonce ); await FirebaseAuth.instance.signInWithCredential(credential); }

Kenapa rawNonce ke Firebase, Bukan hashedNonce?

Facebook embed hashedNonce ke dalam token yang dia keluarkan. Firebase kemudian verifikasi dengan cara: hash ulang rawNonce yang kamu kasih, lalu cocokkan dengan yang ada di token.

Kalau kamu kirim hashedNonce ke Firebase sebagai rawNonce, Firebase akan hash lagi jadi sha256(sha256(rawNonce)), yang tentu nggak akancocok dengan sha256(rawNonce) yang ada di token, jadi dianggap invalid lagi.

Sebelum vs Sesudah

// ❌ Sebelum — broken di flutter_facebook_auth ^7.0.0
final LoginResult result = await FacebookAuth.instance.login(
  permissions: ['email', 'public_profile'],
);
final OAuthCredential credential =
    FacebookAuthProvider.credential(accessToken.tokenString);

// ✅ Sesudah - works
final rawNonce = _generateNonce();
final LoginResult result = await FacebookAuth.instance.login(
  loginTracking: LoginTracking.limited,
  nonce: _sha256(rawNonce),
);
final OAuthCredential credential = OAuthCredential(
  providerId: 'facebook.com',
  signInMethod: 'oauth',
  idToken: (accessToken as LimitedToken).tokenString,
  rawNonce: rawNonce,
);

Dependencies

dependencies:
  flutter_facebook_auth: ^7.1.5
  crypto: ^3.0.7

import 'dart:math'; import 'dart:convert'; import 'package:crypto/crypto.dart';

Singkat cerita: di flutter_facebook_auth ^7.0.+, FacebookAuthProvider.credential() udah nggak works buat Limited Login. Harus pakai OAuthCredential manual dengan rawNonce, dan itu yang bikin Firebase mau nerima credential- nya.

Semoga membantu kalau kamu ketemu error yang sama.