RN Firebase Realtime DB auth token payload - react-native-firebase

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.

Related

Vue Firebase, MySQL & Express

I am looking to build an app using Vue with Firebase for auth and SQL Server as the db behind Express.
I understand the db workflow will be:
Vue => Express => MySQL
However, when performing the authentication should the workflow be:
Vue => Express => Firebase
or
Vue => Firebase
There are 2 things involved here. Firebase SDKs are meant to be used on client side so you'll have to first sign in user with Firebase Authentication. This way you can easily use security rules for other Firebase services like Firestore, Cloud storage, etc.
To authenticate users on your server (Express application), you can pass current user's token in your API requests as shown below:
const idToken = await auth.currentUser.getIdToken()
await fetch("URL", { headers: { authorization: idToken } })
Then verify the ID Tokens on the backend that should give you the user ID. You can then query your database for user's data and return it.
Alternatively, you can use session cookies so you won't have to pass the token in headers for all requests. Checkout the documentation for more information.

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

How to generate firebase custom tokens in multi tenant setup

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.

Reusing the firebase-server-sdk credentials with the REST API

In the old version of Firebase, my server app written in Java would authenticate with my backend using the secret and the JWT token generator. Then, at anytime, I could call Firebase.getAuth().getToken() and reuse that token in an HTTP call as the auth parameter.
With the new firebase-server-sdk, how would I reuse my limited service account credentials / token with the REST API?
Map<String, Object> auth = new HashMap<String, Object>();
auth.put("uid", "server-app");
FirebaseOptions options = new FirebaseOptions.Builder()
.setDatabaseUrl(Environment.FIREBASE_URL)
.setServiceAccount(MyClass.class.getResourceAsStream("/keys/dev.json"))
.setDatabaseAuthVariableOverride(auth)
.build();
FirebaseApp.initializeApp(options);
That all works great when I use the SDK to subscribe / write to certain locations - specifically locations that require a server-app uid. But I use REST in conjunction in my server app, because I want my server app to make synchronous reads, something Firebase only supports through the REST API.
FirebaseRestClient firebaseRest = new RestAdapter.Builder()
.setEndpoint(Environment.FIREBASE_URL)
.setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(final RequestFacade request) {
request.addQueryParam("access_token", FirebaseAuth.getInstance().createCustomToken("server-app"));
}
})
.build().create(FirebaseRestClient.class);
I've tried adding both the access_token and auth param. It seems like that createCustomToken method produces a valid JWT, but the REST API isn't responding.
When I pass in that createCustomToken return value as the auth param, I get the following message:
"error" : "Missing claim 'kid' in auth header."
When I pass in that createCustomToken return value as the access_token param, I get the basic Permission denied API response.
Is there an easy way to reuse my existing firebase-server-sdk credentials in a REST API call?
The token you're attempting to use is a Firebase Authentication ID token - the type which is designed to be passed to the Firebase SDK on a client. The REST API accepts a Firebase access token (just like the ones in previous Firebase clients).
Your authentication is failing because normally the Firebase SDK takes care of turning your ID token into an access token. Your server can not do this transition or generate an access token using the Firebase SDK so I recommend using the original Firebase Token Generator library with your Firebase Secret to create access tokens for the REST API. This will work fine even for new Firebase projects created since the I/O release.
Note: In the Console your Database Secret can be found under (Gear Icon) > Project Settings > Database.
I'm sure this is not advised, given the name of the undocumented property, but...
If your SDK has logged in with a serviceAccount, you can use firebase.auth().INTERNAL.getToken(), which returns a promised accessToken (and expirationTime) which then works with the ?access_token parameter.

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.

Resources