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

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));

Related

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

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!

Flutter How to Link Multiple Auth Providers to an Firebase Account?

I am using firebase in my flutter application. In my app user can sign in or register using 3 ways.
Email id and password.
Google
Facebook.
This is my Firebase setting.
This is my user collection.
Now my doubt is that how do I link multiple auth providers for a user having same email id ?
I am storing user's info into User collection with Uid. If I enable multiple account to multiple providers how can I store user's data in same document ?
I have search a lot on google but didn't find proper solution.
For registration with email ID I am using this.
final newUser = await _auth.createUserWithEmailAndPassword(
email: widget.email, password: widget.pass);
Then I am storing user's data into User collection. (In this I have one more form, from where I am getting other data).
Google sign in code
Future<User> 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
final UserCredential authResult = await _firebaseAuth.signInWithCredential(credential);
final User user = authResult.user;
print('authResult');
print(authResult);
return user;
}
Users are actually allowed to sign in to your app using multiple authentication providers by linking auth provider credentials to an existing user account.
You can follow the documentation here for the process of linking auth provider credentials to an existing user account:
I suggest to also visit the official documentation of Firebase for Flutter.

How to get Facebook Email from flutter_facebook_login in Firebase?

I am using flutter_facebook_login plugin(3.0.0). But my Facebook email doesn't appear inside my Firebase user identifier column; instead, I see this "--". Please help!
Future loginWithFacebook() async {
FacebookLogin facebookLogin = FacebookLogin();
final result = await facebookLogin.logIn(['email', 'public_profile']);
//result.accessToken.
final token = result.accessToken.token;
print('Facebook token userID : ${result.accessToken.permissions}');
final graphResponse = await http.get(
'https://graph.facebook.com/v2.12/me?fields=name,first_name,last_name,email&access_token=${token}');
final profile = jsonDecode(graphResponse.body);
print(profile);
if (result.status == FacebookLoginStatus.loggedIn) {
final credential = FacebookAuthProvider.getCredential(accessToken: token);
FirebaseUser fbUser = (await _auth.signInWithCredential(credential)).user;
//print('Our credential is : $credential');
print('Facebook firebase user ${fbUser.}');
}
return _userFromFacebookLogin(profile);
}
}
Possible Points.
Some Facebook accounts are created using Mobile numbers, so whenever we request for email address we get an empty string.
Email was set to "--" on firebase auth due to missing permission to read email, which fixed by:
final FacebookLoginResult facebookLoginResult = await facebook_Login.logIn(['email', 'public_profile']);
After reading post in firebase-talk google group here https://groups.google.com/forum/#!topic/firebase-talk/gPGNq-IkTLo, I found out the answer. The issue was happened because I'm using "Allow creation of multiple accounts with the same email address" in Firebase Auth sign-in method.
So I change the option into: "Prevent creation of multiple accounts with the same email address" can it's working properly now. It's simple as that. It's true I need more logic to merge accounts having the same email address, but it's okay.
Maybe everyone else having the same issue, can also try this, and hopefully it's solved as well.

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 has incorrect "aud" (audience) claim

I have a frontend, which is hosted via Firebase. The code uses Firebase authentication and retrieves the token via user.getIdToken(). According to answers to similar questions that's the way to go.
The backend is written in Python, expects the token and verifies it using the firebase_admin SDK. On my local machine, I set FIREBASE_CONFIG to the path to firebase-auth.json that I exported from my project. Everything works as expected.
Now I deployed my backend via Google AppEngine. Here I configure FIREBASE_CONFIG as JSON string in the app.yaml. The code looks like this:
runtime: python37
env_variables:
FIREBASE_CONFIG: '{
"type": "service_account",
"project_id": "[firebase-project-name]",
...
The backend logs the value of FIREBASE_CONFIG at startup. In the logs I can see the JSON string is there and { is the first character. So everything looks good to me. But if I retrieve the token from the client and try to validate it (same code, that is working locally) it get this error:
Firebase ID token has incorrect "aud" (audience) claim. Expected
"[backend-appengine-project-name]" but got "[firebase-project-name]". Make sure the ID token
comes from the same Firebase project as the service account used to
authenticate this SDK.
Can somebody explain, what I'm missing and how to solve it?
The error message makes it sound like the user of your client app is signed into a different Firebase project than your backend is working with. Taking the error message literally, the client is using "backend-appengine-project-name", but your backend is using "firebase-project-name". Make sure they are both configured to use the same project using the same project ID.
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
final FirebaseUser user = (await _auth.signInWithCredential(credential)).user;
IdTokenResult idTokenResult = await user.getIdToken(refresh: true);
print("userIdToken:" + idTokenResult.token);

Resources