Firebase ID token has incorrect "aud" (audience) claim - firebase

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

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!

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.

What is the logic behind this authentication in Firebase for Flutter?

I'm following the Firebase for Flutter Codelab and on the 8th step there's this _ensureLoggedIn() function:
final _googleSignIn = new GoogleSignIn();
final _auth = FirebaseAuth.instance;
Future<Null> _ensureLoggedIn() async {
GoogleSignInAccount user = _googleSignIn.currentUser;
if (user == null)
user = await _googleSignIn.signInSilently();
if (user == null) {
await _googleSignIn.signIn();
analytics.logLogin();
}
if (await auth.currentUser() == null) {
GoogleSignInAuthentication credentials =
await _googleSignIn.currentUser.authentication;
await auth.signInWithGoogle(
idToken: credentials.idToken,
accessToken: credentials.accessToken,
);
}
}
As a newbie to both Flutter and Firebase frameworks, i'm really struggling to understand the logic behind it: First we attempt to log the user with the GoogleSignIn package, and then regardless of what we will have in user we try to auth the user again with FirebaseAuth, which in turn will also use the GoogleSignIn.
Could you explain me why we do both?
My target is to have two separate screens for users who open my app - one for unauthorized/anonymous (which will have Log In and Register options), and one for authorized users that will see the normal app interface.
The login example in that Codelab seems poorly written, as the user can potentially cancel the non-silent signIn() and then googleSignIn.currentUser will be null when they try to access googleSignIn.currentUser.authentication. I think a better way to handle it is trigger the Google sign in and handle the Firebase auth in in the googleSignIn.onAuthStateChanged listener.
As for why both are used in that example: If you want to authenticate a user on Firebase with a Google account you have to provide an idToken and accessToken, which must be obtained from a valid Google login. So first, you have them sign in to their Google account (via googleSignIn) and use the tokens from that to authenticate with Firebase (via auth).
Using googleSignIn is only required if you want to authenticate with Firebase using a Google account; you can also use Firebase Auth with a username/password combination (which requires that the account be created on Firebase first) or with a token from a Facebook login, or some other OAuth token.

Firebase Auth across multiple projects with different Providers

I am looking for the Firebase to be authenticated seamlessly across multiple projects with different auth providers.
Example.
Suppose I have a website with 10 pages, with different data coming from 2 firebase projects ( project-1, project-2)
project-1 contains all the Users accounts
When I sign up/Login into firebase project (project-1). I am using Google/Phone/Password/Facebook. this creates users account if not exists.
Now I can see the data coming from the firebase (project-1).
Now few of my pages website want to access data from the project-2.
How to maintain the authentication state across the 2 projects so that, project-2 will get authenticated seamlessly with out prompting signup again. Probably with the same auth token which already created for project-1
I read this blog which was created by Ian Barber Working with multiple Firebase projects in an Android app. Which is only discussed about the google and android case only.
Thanks.
You can use a combination of firebase-admin on a server and a custom firebase token as described here.
Essentially, you can get a user's JWT token for your first app using the methods described in this answer. Then you could make a request to your own server that would validate that your JWT token originated from your first project and could then issue you a second JWT token you can use to authenticate with the second project.
I think you can do the following:
Configure an OIDC provider on project-2 that points out to the firebase auth from project-1 .
The issuer should be https://securetoken.google.com/<project-1-id>
The client id its the project-1-id
After you signIn on project-1 you can use the idToken from that app, to login with an OAuthProvider to the second app (project-2):
const provider = new OAuthProvider('oidc.project-1');
const credential = provider.credential({
idToken: 'idTokenFromProject1',
});
signInWithCredential(auth, credential)
idTokenFromProject1 can be fetched through getIdToken() method on the project-1 app
This is now easily possible with OIDC configuration (reference answer by Alenjadro Barone above )
Configure an OIDC provider on project-2 that points out to the firebase auth from project-1 .
The issuer should be https://securetoken.google.com/<project-1-id>
Then You can follow Code below . Its starter but definitely works : -
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
// Create secondary firebase app Instance
FirebaseApp secondaryApp = Firebase.app('secondary');
class Auth {
// default Firebase Auth app instance: PROJECT 1
final firebaseAuth = FirebaseAuth.instance;
// Secondary Firebase App Instance : PROJECT 2
FirebaseAuth secondaryFirebaseAuth =
FirebaseAuth.instanceFor(app: secondaryApp);
signin() async {
// Sign in method of your choice from PTOJECT 1
await firebaseAuth.signInWithEmailAndPassword(
email: "abc#abc.com",//registered user email
password: "password",//registered user password
);
// Declare OAuth Provider for PROJECT 2
// name of OIDC provider as set in Project
final provider = OAuthProvider('oidc.provider');
try {
// get IdToken for signedin user
final userIdToken = await firebaseAuth.currentUser!.getIdToken();
// Get access Token for User using IdTokenResult
final idTokenResult = await firebaseAuth.currentUser!.getIdTokenResult();
final userAccessToken = idTokenResult.token;
// Create OAuthCredentials with idToken & AccessToken
final credential = provider.credential(
idToken: userIdToken,
accessToken: userAccessToken,
);
// Sign in to Secondary app or PROJECT 2
await secondaryFirebaseAuth.signInWithCredential(credential);
} catch (e) {
if (kDebugMode) {
print("This is error $e");
}
}
}
}

Resources