There's a point in almost every Flutter project where authentication comes into the picture.
And more often than not, the default answer is:
"Just use Firebase."
We've done that too. It works well.
But in one of our recent projects, we already had a backend handling authentication. We didn't need Firebase - we just needed Google Sign-In as a login option.
So we went with a direct approach using Google OAuth.
What we didn't expect?
Most tutorials online were outdated.
If you're using the latest google_sign_in (7.2.0), things have changed quite a bit.
This guide is based on what actually worked for us in production.
Why We Didn't Use Firebase
In our case, Firebase would have added unnecessary complexity.
We already had:
- A backend handling users
- Our own JWT/session system
- APIs ready for auth
All we needed was:
👉 A way to let users log in with Google
👉 A secure way to verify them on our backend
That's it.
What the Flow Looks Like (High-Level)
The flow is still simple:
- User taps "Continue with Google"
- Google account picker opens
- User selects an account
- We receive an ID Token
- We send that token to our backend
- Backend verifies it with Google
- User is logged in / created
Nothing Firebase-specific here.
Step 1: Google Cloud Setup (Don't Skip This)
Before writing code, make sure this part is correct:
- Create a project in Google Cloud Console
- Configure OAuth Consent Screen
- Create OAuth Client IDs:
- Android
- iOS
- Web (important for backend verification)
Things we double-check every time:
- Correct package name / bundle ID
- SHA-1 fingerprint added (Android)
- Using the correct client ID in the app
If anything is wrong here, your code will look fine… but sign-in will fail silently.
Step 2: Add Dependency
google_sign_in: ^7.2.0
Step 3: Implementation
New Working Version (7.2.0)
import 'package:google_sign_in/google_sign_in.dart';
final GoogleSignIn _googleSignIn = GoogleSignIn.instance;
Future<void> signInWithGoogle() async {
try {
// Step 1: Initialize (Required in v7+)
await _googleSignIn.initialize(
serverClientId: 'YOUR_WEB_CLIENT_ID', // Get this from Google Cloud Console
);
// Step 2: Trigger Sign-In
final GoogleSignInAccount account = await _googleSignIn.authenticate();
// Step 3: Fetch Tokens
final GoogleSignInAuthentication auth = await account.authentication;
final String? idToken = auth.idToken;
final String? accessToken = auth.accessToken;
// Step 4: Validate Token
if (idToken == null) {
throw Exception("ID Token is null");
}
// Step 5: Send token to backend
print("ID Token: $idToken");
print("Access Token: $accessToken");
} catch (error) {
print("Google Sign-In Error: $error");
}
}
Step 3: What We Send to Backend
We only send: idToken
Not accessToken. Not profile data.
Just the ID token.
Step 4: Backend Verification (Where Security Actually Happens)
This step is not optional.
Here's a simple Node.js example:
const { OAuth2Client } = require('google-auth-library');
const client = new OAuth2Client(CLIENT_ID);
async function verifyGoogleToken(idToken) {
const ticket = await client.verifyIdToken({
idToken,
audience: CLIENT_ID,
});
const payload = ticket.getPayload();
return payload;
}
This ensures:
- Token is issued by Google
- Token is not tampered
- User identity is valid
Mistakes We've Seen
This is the part that saves hours of debugging.
1. Forgetting initialize()
In v7+, if you skip this:
👉 Authentication will fail
👉 Or behave inconsistently
2. Using Wrong Client ID
You must use:
👉 Web Client ID in serverClientId
Not Android / iOS.
3. Skipping Backend Verification
This is a security flaw.
Anyone can fake a token if you trust it directly from the app.
4. Not Handling Errors Properly
Don't just print(error).
At least:
- Show a snackbar
- Handle cancellation gracefully
5. Expecting Old Code to Work
A lot of developers copy older snippets and assume something is wrong with Flutter.
It's not.
The API changed.
6. Confusing ID Token vs Access Token
- idToken → authentication (you need this)
- accessToken → Google APIs (optional)
What We Added in Production
The basic flow works, but in real apps we also add:
- Silent login (check if user already signed in)
- Proper loading states
- Retry handling for network issues
- Backend session management (JWT)
When This Approach Makes Sense
We use this method when:
- We already have a backend
- We want full control over authentication
- Firebase would be overkill
Final Thoughts
Google Sign-In without Firebase is not a workaround - it's actually the more direct and flexible approach.
But with google_sign_in 7.2.0, things have clearly evolved.
Once you understand the new flow:
- Initialize
- Authenticate
- Get token
- Verify on backend
it becomes straightforward again.
If these are right, your implementation is solid.
One Last Thing
Most tutorials haven't caught up with the 7.2.0 changes yet.
So if you've been stuck trying to make old code work - it's not you.
It's the docs lagging behind real-world usage.
Top comments (0)