authenticated access to Firebase REST API within cloud functions - firebase

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.

Related

Storing and using the Firebase Auth Token for API calls to my server

I implemented Firebase Phone Auth for SignIn in my ReactNative project. Now I want to use this JWTToken to be passed to the API calls that I make to my server.
And at the server side, I would be validating the token which was passed through the API calls and respond with the proper response. My question is, how can I pass this token in the API calls that I make to my server?
I can store the token (within my first loading screen of the app, where it authenticates the User) in the localStorage and fetch it later in any of my screens to make the API calls
I can access the Token directly my importing the firebase package in each and every screen (from which am planning to do the API calls) like this: https://rnfirebase.io/reference/auth/idtokenresult and pass it in the API calls
But I was thinking about storing the Token (fetched during the loading screen) in a global variable inside my ReactNative project and that can be accessed from any screens. But I couldn't find how this can be done? Or which one would be the more appropriate way to do this?
EDIT:
This is how am getting the Token :
auth().onIdTokenChanged(function(user) {
if (user) {
user.getIdToken().then( token => {
console.log( token )
});
}
});
Storing the token in local storage is not going to work out well for you in the long run. ID tokens expire after 1 hour, and will not successfully verify on the server after that.
Each individual page should set up an ID token listener so it can use the most fresh token provided by the Firebase Auth SDK. The SDK will automatically refresh it and provide you with the latest token in the callback. Every time the token changes, you should use that value in your API calls. Use onIdTokenChanged():
firebase.auth().onIdTokenChanged(function(user) {
if (user) {
// User is signed in or token was refreshed.
}
});

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