Firebase IdToken generated using FirebaseAuth.currentUser.getIdToken() on flutter gives error "failed to verify token signature" on Go backend - firebase

I am working on a flutter app where after sign-in, I have to validate a user's idToken on a custom backend (written in Go) using the Firebase admin SDK: firebase.google.com/go.
I am using the following snippet to sign-in the user via GoogleSignIn and retrieve the Firebase idToken on the client side:
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
Future<String> signInWithGoogle() async {
await Firebase.initializeApp();
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final UserCredential authResult = await _auth.signInWithCredential(credential);
final User user = authResult.user;
String FirebaseIdToken = await _auth.currentUser.getIdToken();
print("FirebaseIdToken: " + FirebaseIdToken);
if (user != null) {
/* code to validate user and return it */
} return null;
}
I copy the token corresponding to the FirebaseIdToken variable and send it to the backend using Postman with a Authentication: Bearer <token> request header.
At the backend, there is the following:
/* am.cli here is basically the auth.Client in firebase admin SDK and clientToken is the token received from flutter app. */
idToken, err := am.cli.VerifyIDToken(context.Background(), clientToken)
log.Println("ERROR:", err)
I get the following error printed:
ERROR: failed to verify token signature
Based on the documentation for both client & backend, I believe that I'm using the correct methods to retrieve and to verify the token.
I have tried to retrieve the idToken with the following code as well:
IdTokenResult idTokRes = await _auth.currentUser.getIdTokenResult(true);
print("idTokRes: " + idTokRes.token);
But this fails the same way. (And idTokRes.token and the FirebaseIdToken from the previous method are not same.)
I have also tried to verify the token manually on https://jwt.io/ with the public certificate and the private key which fails as well.
Any help would be appreciated!

Thanks to a member of the Flutter community, I was able to solve the problem.
Turns out, for some reason, the FirebaseIdToken printed by
print("FirebaseIdToken: " + FirebaseIdToken);
is not the complete token. Because of being large, the output gets truncated.
(Still not sure why though. Does Dart's print() statement truncate large strings?)
Edit: Apparently, its the terminal window that truncates/wraps a large output by embedding linebreaks to it.
But, by using the following snippet
String firebaseIdToken = await user.getIdToken();
while (firebaseIdToken.length > 0) {
int startTokenLength =
(firebaseIdToken.length >= 500 ? 500 : firebaseIdToken.length);
print("TokenPart: " + firebaseIdToken.substring(0, startTokenLength));
int lastTokenLength = firebaseIdToken.length;
firebaseIdToken =
firebaseIdToken.substring(startTokenLength, lastTokenLength);
}
I was able to print the complete token in 3 broken parts, which I then concatenated, and sent to the backend via Postman and got no errors this time.
Thanks Rexford!

Related

Flutter Firebase Permission Denied

After i sign out, i try to log in again, yes it logs in succesfully but the problem is, i start to get error [cloud_firestore/permission-denied] but when i restart my application, it works again well. What am i missing?
PS: im logging in with Google.
This is my log in code:
Future signInWithGoogle() async {
// Trigger the authentication flow
final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
// Obtain the auth details from the request
final GoogleSignInAuthentication googleAuth =
await googleUser!.authentication;
// Create a new credential
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
// Once signed in, return the UserCredential
await _auth.signInWithCredential(credential);
}
This is my sign out code:
await _googleSignIn.signOut().then((value) async {
await widget.firebaseAuth.signOut().then((value) {
Navigator.pop(context);
});
}).onError((error, stackTrace) {
print(error);
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("ERROR!! TRY AGAIN"),
));
});
The cloud_firestore/permission-denied error comes from Firestore (the database), not from Firebase Authentication.
Most likely you have some realtime listeners in your code that require the user to be signed in, and those get rejected by the database once you sign the user out.
You can either remove the listeners by canceling the subscriptions, or you can ignore the errors, as the listeners are also canceled automatically after this error occurs.
In my case, I forgot to activate "google-sign in" as an authentication method in firebase.

Flutter Firebase - failing to properly delete a Google authenticated user

I'm trying but failing to re-trigger the authentication steps that the user gets taken through when they authenticate themselves using Google sign-in, following deletion of the user. The deleted user simply gets signed in immediately (instead of being taken through the authentication steps), when using Google sign-in the second time. I want to be able to re-trigger the authentication steps for my own testing purposes.
Specifically, I've got a user who I've authenticated and signed in as per the FlutterFire documentation, i.e.
Future<UserCredential> signInWithGoogle() async {
// Trigger the authentication flow
final GoogleSignInAccount googleUser = await GoogleSignIn().signIn();
// Obtain the auth details from the request
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
// Create a new credential
final GoogleAuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
// Once signed in, return the UserCredential
return await FirebaseAuth.instance.signInWithCredential(credential);
}
I then proceed to delete the user; again, as per the FlutterFire documentation, i.e.
try {
await FirebaseAuth.instance.currentUser.delete();
} catch on FirebaseAuthException (e) {
if (e.code == 'requires-recent-login') {
print('The user must reauthenticate before this operation can be executed.');
}
}
That works, insomuch as the user is no longer listed amongst the authenticated users in the Firebase console. However, if I now proceed to call signInWithGoogle() again, then instead of getting taken through the authentication steps again (i.e. being prompted to enter an email, password, etc.), the user simply gets signed in straight away. It's as if the user hasn't been properly deleted. How would I go about re-triggering the authentication steps?
You must also call GoogleSignIn().signOut() after the Firebase sign out or delete.
In my case, I had to reauthenticate firebase user inside the delete functions try-catch as currentUser() always return null AND GoogleSignIn().signOut() didnt work. Maybe a bug.
import 'package:google_sign_in/google_sign_in.dart';
import 'package:firebase_auth/firebase_auth.dart';
final GoogleSignIn _googleSignIn = GoogleSignIn();
final FirebaseAuth _auth = FirebaseAuth.instance;
//will need to sign in to firebase auth again as currentUser always returns null
//this try-catch block should be inside the function that deletes user
try {
//FirebaseUser user = await _auth.currentUser(); //returns null so useless
//signin to google account again
GoogleSignInAccount googleSignInAccount = await _googleSignIn.signIn();
GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
//get google credentials
AuthCredential credential = GoogleAuthProvider.getCredential(
idToken: googleSignInAuthentication.idToken,
accessToken: googleSignInAuthentication.accessToken);
//use credentials to sign in to Firebase
AuthResult authResult = await _auth.signInWithCredential(credential);
//get firebase user
FirebaseUser user = authResult.user;
print(user.email);
//delete user
await user.delete();
//signout from google sign in
await _googleSignIn.signOut();
} catch (e) {
print('Failed to delete user ' + e.toString());
}

Flutter Firebase Storage : no auth token for request

I have been trying to upload an image to the Firebase Storage while using the Image_Picker.
When I want to upload the image(imageFile) to Firebase Storage
Future uploadFile() async {
StorageReference storageReference =
storage.ref().child('profile/${Path.basename(imageFile.path)}}');
print('uploading..');
StorageUploadTask uploadTask = storageReference.putFile(imageFile);
//waiting for the image to upload
await uploadTask.onComplete;
print('File Uploaded');
storageReference.getDownloadURL().then((fileURL) {
setState(() {
imageURL = fileURL;
});
print(imageURL);
});
}
However, during the uploading, there is an error mentioning I do not have auth token request.
I have used Firebase Auth before for storing data to the database and everything is configured there properly(I assume so since Firebase gave me a google.json file).
W/NetworkRequest( 5796): no auth token for request
E/StorageUtil( 5796): error getting token java.util.concurrent.TimeoutException: Timed out waiting for Task
I have also tried to change the rules for the storage from read, write if auth != null to read, write.
Check your firebase storage rules. I think that the default option is to allow only authenticated users. If that is the problem simply change them to what suits your needs best.
Are you using GoogleSignIn for authentication and can you provide your user authentication code for firebase i was having same issue "no auth token request" for googlesignin method i resolved by using the following code.
final GoogleSignIn googleSignIn = GoogleSignIn();
FirebaseUser firebaseUser;
GoogleSignInAccount account = await googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth = await account.authentication;
final AuthCredential _cred = GoogleAuthProvider.getCredential(idToken: googleAuth.idToken, accessToken: googleAuth.accessToken);
final AuthResult _res = await FirebaseAuth.instance.signInWithCredential(_cred);
firebaseUser = _res.user;

Firebase Id Token from Flutter App is not verified on the REST server

I want support social login with firebase auth. But I want to keep user's detailed profiles in my own RDB .
I thought that after user signed in with social account I could get encoded token which contains user's id and email.
And then if I send the token to the REST server (I built it with spring boot), then the server can decode it to load user's detailed profile from RDB.
I followed the guide from this article.
https://blog.codemagic.io/firebase-authentication-google-sign-in-using-flutter/
I can see sign in process successfully done and the id token printed at the console.
the flutter(dart) code is below.
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final AuthResult authResult = await _auth.signInWithCredential(credential);
final FirebaseUser user = authResult.user;
name = user.displayName;
email = user.email;
imageUrl = user.photoUrl;
final FirebaseUser currentUser = await _auth.currentUser();
await currentUser.getIdToken().then((value) => print(value.token));
I copied the token into the server modult to test it could be decoded with no problem.
I added firebase-admin dependency. and run it with java code below.
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredentials(GoogleCredentials.getApplicationDefault())
.setDatabaseUrl("https://test-firebase-auth-token.firebaseio.com")
.build();
FirebaseApp.initializeApp(options);
String firebaseToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImQ1OThkYjVjZ..."; // actually it's long string.
FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(firebaseToken);
System.out.println(decodedToken.getUid());
System.out.println(decodedToken.getEmail());
And I got an error.
Exception in thread "main" com.google.firebase.auth.FirebaseAuthException: Failed to verify the signature of Firebase ID token. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.
at com.google.firebase.auth.FirebaseTokenVerifierImpl.checkSignature(FirebaseTokenVerifierImpl.java:154)
at com.google.firebase.auth.FirebaseTokenVerifierImpl.verifyToken(FirebaseTokenVerifierImpl.java:92)
at com.google.firebase.auth.FirebaseAuth$4.execute(FirebaseAuth.java:426)
at com.google.firebase.auth.FirebaseAuth$4.execute(FirebaseAuth.java:423)
at com.google.firebase.internal.CallableOperation.call(CallableOperation.java:36)
at com.google.firebase.auth.FirebaseAuth.verifyIdToken(FirebaseAuth.java:388)
at com.google.firebase.auth.FirebaseAuth.verifyIdToken(FirebaseAuth.java:362)
at com.example.demo.DemoApplicationTests.main(DemoApplicationTests.java:36)
I tried it again with firebase-server-sdk dependency instead of firebase-admin.
And I got an another error message. (seems that it caused by same reason with above error message)
Caused by: com.google.firebase.auth.FirebaseAuthException: Token isn't signed by a valid public key
at com.google.firebase.auth.internal.FirebaseTokenVerifier.verifyTokenAndSignature(FirebaseTokenVerifier.java:61)
at com.google.firebase.auth.FirebaseAuth$1.call(FirebaseAuth.java:146)
at com.google.firebase.auth.FirebaseAuth$1.call(FirebaseAuth.java:140)
at com.google.firebase.tasks.Tasks$1.run(Tasks.java:63)
I checked the token on this site.
https://jwt.io/
I wonder the token is really signed with a (private) key?
Then how jwt.io site decode it without knowing the key?
It seems like it is just encoded with open algorithm like base64. Isn't it?
If so... the token is not secure any more.
What's the problem with my application. or Do I have miss-conception on security?
Any comment will be welcomed. Thanks...
As answered here, problem on print method. It has limited to ~1024 symbols.
You can print like this:
final idToken = await firebaseCredential.user!.getIdToken(true);
print(idToken.substring(0, 1000));
print(idToken.substring(1000));

Flutter Google Login not Getting Registered in Firebase

1. The Problem
I'm trying to implement simple Facebook and Google Sign-in buttons with Firebase and Flutter, however the Google version is acting very weird. When I print the FirebaseAuth.instance.currentUser() I either get errors or prior Facebook login data.
When I login with Facebook, my account does appear on Firebase, however, the Google login seems to do nothing.
Is this something I messed up in the code below or some incompatibility problem with AndroidX with parts of these libraries? Something else?
Also, it is not very clear to me if I have to put the Project public-facing name somewhere inside my project to make the integration with Firebase work (I had to do something similar to setup the Facebook Login button).
2. The Facebook Login
I had to replace logInWithReadPermissions with signInWithCredential because recent versions have changed their API. I've also tried to use previous versions of the packages, but encountered many errors (probably due to AndroidX):
final _auth = FirebaseAuth.instance;
Future<FirebaseUser> _loginWithFacebook () async {
final facebookLogin = FacebookLogin();
final result = await facebookLogin.logInWithReadPermissions(['email']);
if (result.status == FacebookLoginStatus.loggedIn){
final FacebookAccessToken accessToken = result.accessToken;
AuthCredential credential = FacebookAuthProvider.getCredential(
accessToken: accessToken.token,
);
AuthResult signInResult = await _auth.signInWithCredential(credential);
FirebaseUser fbUser = signInResult.user;
return fbUser;
}
else{
return null;
}
}
3. The Google Login
Again, signInWithCredential seems to be the more recent API:
Future<FirebaseUser> _loginWithGoogle () async{
final GoogleSignIn _googleSignIn = GoogleSignIn();
GoogleSignInAccount googleUser = await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
AuthResult signInResult = await _auth.signInWithCredential(credential);
final FirebaseUser user = signInResult.user;
print(user);
return user;
}
Edit
I've tried it on an Android 9.0 (Pie) emulator and it still doesn't work.
Apparently, my authentication code was correct for both Google and Facebook, considering the most up-to-date API.
However I needed to integrate a SHA1 Fingerprint to my Firebase environment, which is something I didn't realize I needed to do because none of the tutorials I followed mentioned it and it can be easily dismissed when enabling the Google Sign-in method inside Firebase.
Anyway, here are the steps you will need to complete to get the SHA1 Fingerprint:
Open Command Prompt and cd C:\Program Files\Android\Android Studio\jre\bin.
Get the SHA1 key:
keytool -list -v -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore
Go to Firebase Console > Project settings > Add Key and add the key.

Resources