How to generate firebase custom tokens in multi tenant setup - firebase

I have been trying to add support for multi tenants to my application.
I initialize like so
const app = firebase.initializeApp();
const tenantManager = app.auth().tenantManager();
const tenant = await tenantManager.createTenant({ displayName: `test- tenant` });
const auth = tenantManager.authForTenant(tenantId);
Part of my application then uses the auth.createCustomToken(uid) in order to create a token that can then be exchanged for a standard id token (Using the rest endpoint /accounts:signInWithCustomToken.
When trying to create the custom token I get the following error
Error: This operation is not supported in a multi-tenant context
Additionally to this when manually creating a token (using jsonwebtoken and the service account key) the error
Specified tenant ID does not match the custom token
Comes up when attempting to verify the token (through the REST API)
Has anyone else encountered this error, or is anyone aware of another way to generate and verify custom tokens in a multi tenant environment (or, alternatively, know of some way to log a user in given only a uid)?

Instead of using the API to generate the custom token, generate a JWT using the private_key from the service account for signing and making sure you have the values defined below
const jwt = require(`jsonwebtoken`);
const payload = {
uid,
sub: serviceAccount.client_email,
tenant_id: tenantId
};
jwt.sign(payload, serviceAccount.private_key, {
audience: `https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit`,
issuer: serviceAccount.client_email,
algorithm: `RS256`,
expiresIn: 0
});
Note: The tenant_id in the payload.
Now, when exchanging the custom token for a firebase issued token by POSTing to
`https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=${encodeURIComponent(webApiKey)}`
Ensure that tenantId is as a property in the JSON body of the request and matches the tenant_id found in the token.
{
tenantId, // Make sure this matches the "tenant_id" claim in the idToken
token: idToken,
returnSecureToken: true
}
The second part of this is documented at https://cloud.google.com/identity-platform/docs/reference/rest/client/#section-verify-custom-token (But not in the original firebase auth documentation at the time of this writing)

This question is old, and hence the previous answers are kinda obsolete.
Now, you can create a token by setting the tenant-id in the Auth object, both on the Admin SDK and the Firebase Auth Client.
On Admin SDK:
const auth = admin.auth().tenantManager().authForTenant(<tenant-id-value>);
const firebaseToken = await auth.createCustomToken(uid);
return firebaseToken; // send firebaseToken to client
On Firebase Auth Client:
const auth = firebase.auth();
auth.tenantId = <tenant-id-value>;
auth.signInWithCustomToken(firebaseToken);
As long as the Tenant-Id matches, you should not see any issues. You don't need to use any third-party library anymore unless your language is not supported.

Currently custom token authentication is not supported in a multi-tenancy context. This feature is still under construction. You can check the full list of supported capabilities here.

Related

Can I use Firebase's authentication to protect data on my own private server?

Due to some constraints of my project I need to manage my api server and database separately from firebase, however there is nothing stoping me from using their other tools like analytics and in this particular case authentication.
I wanted to ask if following scenario is possible:
I authenticate user on my client app using their SDK's
I send relevant jwt data in headers of my API
Can I then somehow validate this on my server, so in essence avoid complex auth service and use firebase for this instead? Are there any examples, preferably for NodeJS server.
Yes you can, it's very common use case and firebase auth has been made with such usage in mind. As you said, send jwt data on headers, then on the server, you verify the token with firebase service:
var admin = require('firebase-admin');
admin.initializeApp(); //may require other steps, see last link
// idToken comes from the client app
admin.auth().verifyIdToken(idToken, true)//second argument is optional, checks whether the ID token was revoked
.then(function(decodedToken) {
let uid = decodedToken.uid;
// ...
}).catch(function(error) {
// Handle error
});
https://firebase.google.com/docs/auth/admin/verify-id-tokens
https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#verifyidtoken
https://firebase.google.com/docs/admin/setup

RN Firebase Realtime DB auth token payload

Using react-native-firebase:4.3.x
I'm able to connect to RealtimeDB, us requiring security we've set up rules.
But looking through the docs I can't find where how to setup Auth token payload when connecting to firebase. All it has is to connect to DB call the following:
db = firebase.database();
no parameters or anything. Am I to use, the firebase.auth()?
Short answer: Our whole approach on Firebase RealtimeDB's ruleset was incorrect from the beginning; we had done the rules without understanding Firebase Auth and its tie in with RealtimeDB. We had setup, rules based on uid and RealtimeDB only, storing some random token on RealtimeDB hoping we could somehow pass the token on auth payload to the user.
Long answer:
As stated on Firebase's own Database Security docs Database Rules directly uses Firebase Authentication.
From then on, implemented Custom authentication from Authentication RNFirebase.io
client side:
let postLogin = (userCredentials) => {
db = firebase.database();
//...do stuff
}
firebase
.auth()
.signInAndRetrieveDataWithCustomToken(token)
.then(postLogin);
firebase console:
Project Settings -> Service Accounts -> Generate new private key.
which generates json formatted firebase private key and some identifier values. Import that in whatever library you're using on server-side in our case kreait/firebase-php
Do not enable anonymous authentication, that would defeat the purpose.
php using kreait/firebase.php:
use Kreait\Firebase\Factory;
use Kreait\Firebase\ServiceAccount;
$serviceAccount = ServiceAccount::fromJsonFile($pathToJson);
$firebase = (new Factory())
->withServiceAccount($serviceAccount)
->create();
$token = (string) $firebase->getAuth()->createCustomToken($uid, $payload)
I did not need to be aware of payload on client side. It is passed through client side in the signed JWToken.

Is there a way to determine if a Firebase user's UID is valid?

I am building a server route that I wish to restrict for use only by authenticated users. I plan to send a user.uid with a POST to this route, and I want to validate the UID is one that exists in Firebase. I know I can add UIDs manually in Firebase and check against this data, but is it possible to see what UIDs Firebase authentication is tracking? I think this approach would be better then checking against my own list.
The purpose for this is to ensure that these routes are not accessed with a phony UID (e.g. for malicious purposes).
Validating a UID is not enough to block malicious users: 1) the attackers could pretend to be other users by sending other user's UID, and 2) UID never changes or expires, which means there is no way to enforce the users (or attackers) to re-authenticate.
What you need is to pass the Firebase token from client app to your server, and validate the token before accepting it.
The token is securely signed by Firebase private key. No other party can issue a valid Firebase token.
The token is valid for only one hour. Firebase server will check the account status (e.g. password change event) before issuing a new token.
The token payload contains UID and audience. You should verify audience is your own application.
You can use Firebase Admin SDK or third party libraries to verify a Firebase token. See Firebase doc for details.
You can check whether a specific UID corresponds to a Firebase Authentication user in your project by using the Firebase Admin SDK on your server. From the Firebase documentation on retrieving user data:
admin.auth().getUser(uid)
.then(function(userRecord) {
// See the UserRecord reference doc for the contents of userRecord.
console.log("Successfully fetched user data:", userRecord.toJSON());
})
.catch(function(error) {
console.log("Error fetching user data:", error);
});
A UID is part of the payload when a Firebase user is authenticated to Firebase and is null when a user is not authenticated. You can get the UID upon user authentication. The syntax is different depending on what framework you are working in.
Check out the Firebase API Reference for specific syntax and examples: https://firebase.google.com/docs/reference/
create a token in your client app
private String getAuthTokenAndPost(){
mAuth.getCurrentUser().getIdToken(false).addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
#Override
public void onComplete(#NonNull Task<GetTokenResult> task) {
if(task.isSuccessful()){
String idToken = task.getResult().getToken();
sendReqToServer(idToken);
}else{
Toast.makeText(CartActivity.this, "couldn't generate Token", Toast.LENGTH_SHORT).show();
}
}
});
return "";
}
then use firebase admin SDK on your server side, here is an example of a Node server
getAuth()
.verifyIdToken(idToken)
.then((decodedToken) => {
const uid = decodedToken.uid;
// ...
})
.catch((error) => {
// Handle error
});
ID token verification requires a project ID. The Firebase Admin SDK attempts to obtain a project ID via one of the following methods:
If the SDK was initialized with an explicit projectId app option, the SDK uses the value of that option.
If the SDK was initialized with service account credentials, the SDK uses the project_id field of the service account JSON object.
If the GOOGLE_CLOUD_PROJECT environment variable is set, the SDK uses its value as the project ID. This environment variable is available for code running on Google infrastructure such as App Engine and Compute Engine.
check the document

Firebase 3.0 Tokens : [Error: Firebase Auth ID token has no "kid" claim]

I'm currently developing a node.js service with firebase 3.0 that is called by a web application that uses firebase 2.4.
I'm sending the current user Firebase ID token (Auth.$getAuth().token) in my header call and trying to validade this token with
var idToken = req.headers["x-access-token"];
auth.verifyIdToken(idToken).then(function(decodedToken) {
var uid = decodedToken.sub;
console.log(decodedToken);
}, function(error){
console.log(error);
});
But I'm getting:
[Error: Firebase Auth ID token has no "kid" claim]
getAuth():
UPDATE
I'have just tested generating and validating the token on the server side and I'm getting the same problem.
var auth = firebase.auth();
var token = auth.createCustomToken(userId, {"premium_account": true});
console.log(token);
auth.verifyIdToken(token).then(function(decodedToken) {
console.log(decodedToken);
}, function(error){
console.log(error);
});
Any suggestions?
UPDATE 2: [SOLUTION]
The problem in my case was that the Tokens generated with AngularFire 2.X.X are not compatible with the Firebase 3.X.X that is running in my server. So after digging into some thoughts that people wrote here and in this google group topic the workaround was to use jsonwebtoken as follows:
var jwt = require('jsonwebtoken');
jwt.verify(idToken, fbKey, function(err, decoded) {
if (!err){ console.log(decoded); }
});
You can find the fbKey accessing the new firebase console and going into Settings -> Project Settings -> Database.
The documentation states that a Firebase ID Token is not the same as a Custom Token, and that verifyIdToken() is not intended for verifying tokens generated with generateCustomToken().
Old style custom tokens still seem to work (signed with a database secret instead of a service account private key). You can generate and verify these yourself using firebase-token-generator.js and/or jsonwebtoken.js.
Copied from Firebase Project > Settings > Database > Secrets
Create custom database authentication tokens using a legacy Firebase
token generator. At least one secret must exist at all times.
Seems like there is no way to use firebase's createCustomToken and verifyIdToken in a couple.
Method createCustomToken uses method sign from jsonwebtoken module which does not put "kid" claim in header section of jwt by default.
And createCustomToken does not put it itself.
I suppose at this time you can use jsonwebtoken module directly to generate token with own key id.
firebase#3.0.2
--jsonwebtoken#5.7.0
The token you pass to your server is not a JWT token, and verifyIdToken need a JWT Token.
To obtain a JWT token in your web application, run firebase.app().auth().currentUser.getToken().
Personnally I removed angularfire, the basic firebase framework made angularfire pretty useless in my opinion. Plus, a compatible version with firebase 3.0 of angularfire is not yet released, probably in the next week according to the firebase team.

authenticated access to Firebase REST API within cloud functions

I am trying to access the firebase REST API from a cloud function, using the built-in functions.config().firebase object (my use case is a shallow query).
const { credential } = functions.config().firebase;
credential.getAccessToken().then( ({ access_token }) => {
// use access_token as auth param to query the REST API
});
When I use the returned access_token as the auth parameter in the REST API, I get the could not parse auth token error. Is there a way of generating there a valid auth token without exposing in the config the database secret ?
Many thanks for your help.
Turns out this is a correct way of generating an authentication token, but that with this token, one need to use an access_token param instead of the documented auth one.

Resources