Social Login
Sign In with Google & Apple
Open interactive version (quiz + challenge)Real-world analogy
Social login is like using your library card at a partner bookstore. Instead of creating a brand new account at the bookstore (your app), you show your library card (Google/Apple account). The bookstore calls the library to confirm you are real (token verification), then gives you a bookstore loyalty card (your app's tokens). One less password to remember!
What is it?
Social login lets users sign in with their Google or Apple accounts instead of creating a new username and password. In team_mvp_kit, the flow is: user taps Google/Apple button -> Firebase Auth authenticates them -> app gets a Firebase ID token -> sends it to the backend -> backend verifies and returns app-specific JWT tokens -> tokens are saved locally.
Real-world relevance
team_mvp_kit's FirebaseSocialAuthService orchestrates the entire social login flow. Google Sign-In and Apple Sign-In are the two most popular providers. Apple Sign-In is mandatory on iOS if you offer any other social login. The backend token exchange pattern lets you use Firebase for the easy social auth part while keeping your own user database and token system.
Key points
- Why Social Login? — Social login reduces friction -- users tap one button instead of filling out a registration form. It reduces abandoned signups by up to 50%. Users trust Google and Apple with their credentials. team_mvp_kit supports both Google Sign-In and Apple Sign-In through Firebase Auth.
- Google Sign-In Flow — The Google Sign-In flow: 1) User taps the Google button, 2) Google SDK shows account picker, 3) User selects their Google account, 4) SDK returns a GoogleSignInAuthentication with an ID token, 5) You create a Firebase credential from it, 6) Sign in to Firebase with that credential.
- Apple Sign-In Flow — Apple Sign-In is required on iOS if you offer any other social login. The flow is similar to Google but uses the sign_in_with_apple package. Apple returns an authorization with an identityToken that you use to create a Firebase OAuthCredential.
- Backend Token Exchange — After Firebase sign-in, your app gets a Firebase ID token. Send this to YOUR backend server. The backend verifies it with Firebase Admin SDK, creates or finds the user in its database, and returns your app's own access and refresh tokens. This is the pattern team_mvp_kit uses.
- FirebaseSocialAuthService in team_mvp_kit — team_mvp_kit has a dedicated FirebaseSocialAuthService that handles both Google and Apple sign-in, gets the Firebase ID token, exchanges it with the backend, and saves the resulting tokens. It is injected as a @lazySingleton and used by the AuthRepository.
- Handling Sign-In Errors — Social sign-in can fail in many ways: user cancels, network error, account already linked to another provider, or Firebase configuration issues. Handle each case gracefully and show clear messages. team_mvp_kit wraps these in typed Failure objects using the Result pattern.
- Apple Sign-In Requirements — Apple requires that any iOS app offering third-party login (Google, Facebook) must also offer Apple Sign-In. Apple Sign-In is not available on Android devices, so show the Apple button only on iOS. Use Platform.isIOS or the TargetPlatform check to conditionally display it.
- Sign Out from All Providers — When the user logs out, you must sign out from Firebase AND from the social provider (Google). If you only sign out from Firebase, the next Google sign-in will auto-select the same account without showing the account picker. team_mvp_kit signs out from all providers in the LogoutUseCase.
Code example
import 'package:google_sign_in/google_sign_in.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:injectable/injectable.dart';
// FirebaseSocialAuthService (team_mvp_kit pattern)
@lazySingleton
class FirebaseSocialAuthService {
final FirebaseAuth _auth;
final GoogleSignIn _googleSignIn;
final ApiService _api;
final TokenStorage _tokenStorage;
FirebaseSocialAuthService(
this._auth,
this._googleSignIn,
this._api,
this._tokenStorage,
);
// Google Sign-In
Future<AppUser> signInWithGoogle() async {
// Step 1: Google account picker
final googleUser = await _googleSignIn.signIn();
if (googleUser == null) {
throw AuthCancelledException();
}
// Step 2: Get Google auth tokens
final googleAuth = await googleUser.authentication;
// Step 3: Create Firebase credential
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
// Step 4: Sign in to Firebase
final userCredential =
await _auth.signInWithCredential(credential);
// Step 5: Exchange Firebase token with backend
return _exchangeWithBackend(
userCredential.user!,
'google',
);
}
// Apple Sign-In
Future<AppUser> signInWithApple() async {
// Step 1: Apple authorization
final appleCredential =
await SignInWithApple.getAppleIDCredential(
scopes: [
AppleIDAuthorizationScopes.email,
AppleIDAuthorizationScopes.fullName,
],
);
// Step 2: Create Firebase credential
final oauthCredential =
OAuthProvider('apple.com').credential(
idToken: appleCredential.identityToken,
accessToken: appleCredential.authorizationCode,
);
// Step 3: Sign in to Firebase
final userCredential =
await _auth.signInWithCredential(oauthCredential);
// Step 4: Exchange with backend
return _exchangeWithBackend(
userCredential.user!,
'apple',
);
}
// Exchange Firebase ID token for app tokens
Future<AppUser> _exchangeWithBackend(
User firebaseUser,
String provider,
) async {
final idToken = await firebaseUser.getIdToken();
final response = await _api.post('/auth/social', data: {
'firebase_token': idToken,
'provider': provider,
});
// Save app tokens
final tokens = TokensDto.fromJson(response['tokens']);
await _tokenStorage.saveTokens(tokens);
// Return app user
return AppUser.fromJson(response['user']);
}
// Sign out from everything
Future<void> signOut() async {
await _googleSignIn.signOut();
await _auth.signOut();
await _tokenStorage.clearTokens();
}
}Line-by-line walkthrough
- 1. Import google_sign_in package for Google authentication
- 2. Import sign_in_with_apple for Apple authentication
- 3. Import firebase_auth for Firebase credential handling
- 4. Import injectable for dependency injection
- 5.
- 6. Comment: This is the team_mvp_kit pattern for social auth
- 7. @lazySingleton registers one instance for the app
- 8. Class declaration for FirebaseSocialAuthService
- 9. Private field for FirebaseAuth instance
- 10. Private field for GoogleSignIn instance
- 11. Private field for ApiService to call the backend
- 12. Private field for TokenStorage to save tokens
- 13.
- 14. Constructor receives all four dependencies via DI
- 15. Closing the constructor
- 16.
- 17. Comment: Google Sign-In method
- 18. Method returns an AppUser (your domain entity)
- 19. Comment: Step 1
- 20. Trigger the Google account picker UI
- 21. If user cancelled (returned null), throw exception
- 22. The AuthCancelledException signals the BLoC to handle it
- 23. Closing the cancel check
- 24.
- 25. Comment: Step 2
- 26. Get the Google auth tokens from the selected account
- 27.
- 28. Comment: Step 3
- 29. Create a Firebase credential from Google tokens
- 30. Pass the Google access token
- 31. Pass the Google ID token
- 32. Closing the credential creation
- 33.
- 34. Comment: Step 4
- 35. Sign in to Firebase with the Google credential
- 36. Closing the Firebase sign-in
- 37.
- 38. Comment: Step 5
- 39. Exchange the Firebase token with the backend
- 40. Pass the Firebase user and provider name
- 41. Closing signInWithGoogle
- 42.
- 43. Comment: Apple Sign-In method
- 44. Method returns an AppUser
- 45. Comment: Step 1
- 46. Request Apple authorization with scopes
- 47. Request email scope
- 48. Request full name scope
- 49. Closing the scopes and credential request
- 50.
- 51. Comment: Step 2
- 52. Create a Firebase OAuth credential for Apple
- 53. Set the Apple identity token as idToken
- 54. Set the authorization code as accessToken
- 55. Closing the credential creation
- 56.
- 57. Comment: Step 3
- 58. Sign in to Firebase with the Apple credential
- 59. Closing the Firebase sign-in
- 60.
- 61. Comment: Step 4
- 62. Exchange with backend (same flow as Google)
- 63. Pass the Firebase user and 'apple' as provider
- 64. Closing signInWithApple
- 65.
- 66. Comment: Private method to exchange Firebase token for app tokens
- 67. Method takes a Firebase User and provider string
- 68. Get the Firebase ID token from the user
- 69.
- 70. Send the ID token to the backend /auth/social endpoint
- 71. Include the Firebase token in the request body
- 72. Include which provider was used (google or apple)
- 73. Closing the API call
- 74.
- 75. Comment: Save the app tokens received from backend
- 76. Parse the tokens from the response
- 77. Save them to encrypted storage
- 78.
- 79. Comment: Return the app user
- 80. Parse and return the user data from the response
- 81. Closing _exchangeWithBackend
- 82.
- 83. Comment: Sign out from all providers
- 84. signOut method clears everything
- 85. Sign out from Google (clears cached account selection)
- 86. Sign out from Firebase
- 87. Clear app-specific tokens from storage
- 88. Closing signOut and FirebaseSocialAuthService
Spot the bug
Future<UserCredential> signInWithGoogle() async {
final googleAuth = await GoogleSignIn().signIn();
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
return FirebaseAuth.instance.signInWithCredential(credential);
}Need a hint?
GoogleSignIn().signIn() returns a GoogleSignInAccount, not the auth tokens directly. There is a missing step...
Show answer
GoogleSignIn().signIn() returns a GoogleSignInAccount (not GoogleSignInAuthentication). You need to call .authentication on the account to get the tokens. Also, signIn() can return null if cancelled. Fix: add a null check, then call 'final googleAuth = await googleUser.authentication;' before creating the credential.
Explain like I'm 5
Imagine you want to join a new sports club. Instead of filling out a long form with your name, address, and photo, you say 'I already have a library card!' The sports club calls the library to check if you are real. The library says 'Yep, that is Alice, age 10, she is a real person!' Then the sports club gives you their own membership card. That is social login -- using an account you already have (Google or Apple) to join a new app without creating a new password!
Fun fact
Apple made Sign in with Apple mandatory in 2020 for any iOS app that offers third-party login. This was partly for privacy -- Apple generates a unique relay email address for each app, so apps never see your real email. Google Sign-In, on the other hand, has been available since 2014 and is used by over 2 million apps!
Hands-on challenge
Implement a complete Google Sign-In flow: create a FirebaseSocialAuthService, handle the sign-in, get the Firebase ID token, and simulate a backend token exchange. Handle the case where the user cancels. Add an Apple Sign-In method that is only shown on iOS.
More resources
- Google Sign-In for Flutter (pub.dev)
- Sign In with Apple for Flutter (pub.dev)
- Firebase Auth Social Login (FlutterFire)