Server side rendered javascript app - client auth

334 views
Skip to first unread message

Conor Dockry

unread,
May 8, 2017, 10:18:38 PM5/8/17
to Firebase Google Group
Hey everyone hows it going.. thanks in advance for your help....

I want to make client api calls on the server AND in the browser (using react, and next.js)

I don't want to use admin SDK to retrieve data which negates security rules.

I don't need to mint a custom token (do I ?) as I simply want to assume the role of users created by the javascript SDK.

If there were an admin method such as

admin.auth().signInWithVerifiedToken(decodedToken)

OR 

.signInWithIdToken(unverifiedToken)

which verified & signed in at the same time, this would be exactly what I need.

In any case, will this work:

admin.auth().verifyIdToken(idTokenSentFromClient)
 
.then(function(decodedToken) {
   
var uid = decodedToken.uid;
    admin.auth().createCustomToken(uid)
     .then(function(customToken) {

       firebase
.auth().signInWithCustomToken(token).then(() => {
         
// can now get user data..
       
})
     
})
  })


Probably not on the right track... but trying to reason my way through this example: https://github.com/zeit/next.js/blob/master/examples/with-firebase/pages/index.js

and how to restrict server side calls to the specific user

-- Conor Dockry

Kato Richardson

unread,
May 9, 2017, 12:52:16 PM5/9/17
to Firebase Google Group
Hi Conor,

It sounds like you do want to use the admin SDK. You just want to limit privileges.

That's done by specifying setDatabaseAuthVariableOverride, which allows you to take an action as if it was done by a given uid.

☼, Kato

--
You received this message because you are subscribed to the Google Groups "Firebase Google Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to firebase-talk+unsubscribe@googlegroups.com.
To post to this group, send email to fireba...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/firebase-talk/1a6308bc-ce2f-4133-9edd-c489b1a884d4%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--

Kato Richardson | Developer Programs Eng | kato...@google.com | 775-235-8398

Conor Dockry

unread,
May 9, 2017, 4:38:35 PM5/9/17
to Firebase Google Group
Thanks Kato!

With the js SDK I only see the ability to set that parameter within

admin.initializeApp(..., databaseAuthVariableOverride: 'someUid' )

which I would nee to call on every user request correct?

But I need to use the admin SDK to verify the user's token before I assume their identity, so it needs initialized prior to this...

To unsubscribe from this group and stop receiving emails from it, send an email to firebase-tal...@googlegroups.com.

To post to this group, send email to fireba...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/firebase-talk/1a6308bc-ce2f-4133-9edd-c489b1a884d4%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Kato Richardson

unread,
May 10, 2017, 3:26:24 PM5/10/17
to Firebase Google Group
const config = { ... };

const admin = firebase.initializeApp(config);

admin.auth().verifyToken(...).then(function(decodedToken) {
    var uid = decodedToken.uid;
     const appAsUser = firebase.initializeApp(getUserCreds(uid), `as_user_${uid}`);
     appAsUser.database().ref('foo').set('bar');
 }

function getUserCreds(uid) {
    return {
        credential: config.credential,
        databaseURL: config.databaseURL,
        databaseAuthVariableOverride: {
           uid: "my-service-worker"
        }
    }
}

Covered here, here, and here. I hope that helps!

☼, Kato


To unsubscribe from this group and stop receiving emails from it, send an email to firebase-talk+unsubscribe@googlegroups.com.

To post to this group, send email to fireba...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Kato Richardson

unread,
May 10, 2017, 3:27:36 PM5/10/17
to Firebase Google Group

Oops, where "my-service-worker" should have been uid. Missed one : )

Conor Dockry

unread,
May 10, 2017, 9:54:54 PM5/10/17
to Firebase Google Group
Thanks I had followed that pattern, but got error saying I need to specify a second name argument.

From there, I used the 'uid' as unique name for each user request. 
But then I'm get error because of course on subsequent requests by same user, I've already used that 'uid':

Error: Firebase app named "dxxxxxxxxxxxxxxxxxxx" already exists. 
This means you called initializeApp() more than once with the same app name as the second argument.
Make sure you provide a unique name every time you call initializeApp()


Should I use a unique identifier for every request? Does this pose problems creating new "app" objects every request somehow cached inside the SDK?
Or, should I do this and cache the app object myself, one for each unique 'uid' ?

const clientMap = new Map()

function getScopedClient(uid) {
if (clientMap.has(uid)) {
return clientMap.get(uid)
}
const client = admin.initializeApp({ ...,  databaseAuthVariableOverride: { uid }, uid)
clientMap.set(uid, client)
return client
}

Thanks for your help Kato, sorry to keep pushing on this question.. :/



--

Kato Richardson | Developer Programs Eng | kato...@google.com | 775-235-8398

Kato Richardson

unread,
May 11, 2017, 1:59:38 PM5/11/17
to Firebase Google Group
It should be fine to create a new app instance for each uid/task, assuming we're not doing thousands per second.

Hard to answer these questions without knowing what the use case is and why you're using Firebase in this way and why you're not using Functions or a queue system instead (i.e. talking through Firebase and having the server read queued events and write them back into the DB). 

Generally speaking, you shouldn't need to do a lot of RESTful work with Firebase, nor spend a lot of time impersonating users, since most of the logic can be pushed to the client and security rules can be used to enforce access; pushing the work back to the server takes a lot of the magic away from using Firebase as your BaaS.

☼, Kato



To unsubscribe from this group and stop receiving emails from it, send an email to firebase-talk+unsubscribe@googlegroups.com.

To post to this group, send email to fireba...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Conor Dockry

unread,
May 12, 2017, 5:56:16 PM5/12/17
to Firebase Google Group
Use case is a client side web app, (javascript / react.js) but that is server rendered on top level requests from the browser.


Routes are organized into "pages" or "screens". Initial top level requests for a route are server rendered. From that point on, navigation to different routes is rendered client side.
Data is fetched for a route on server if server rendered. So the use-case would be requesting user specific data for initial page render, then subscribing to updates in  componentDidMount

Perhaps this is not a good use for firebase, or not worth the effort to get server rendering for client data and just use componentDidMount and request all data client side...

Conor Dockry

unread,
May 26, 2017, 1:21:35 PM5/26/17
to Firebase Google Group
My use case seems to map quite clearly with the proposed new feature from I/O of "Serving dynamic content with Cloud functions" on firebase hosting, no?

https://firebase.google.com/docs/hosting/functions

And so then, it would seem obvious that one would want to impersonate users to render client data, but ensure security rules still apply, therefore: impersonate users.

Kato Richardson

unread,
May 27, 2017, 12:40:42 PM5/27/17
to Firebase Google Group

If you’re working with cloud functions, you can access event.data.ref, which is already authenticated as the user who triggered the function call. That might be ideal for your use case.


To unsubscribe from this group and stop receiving emails from it, send an email to firebase-talk+unsubscribe@googlegroups.com.

To post to this group, send email to fireba...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Conor Dockry

unread,
May 28, 2017, 2:32:09 PM5/28/17
to Firebase Google Group
The corresponding cloud function for my use case would be 

functions.https.onRequest((req, res) => {... }) 


Which doesn't have an `event` context because it is just a regular http request. The `user` context could only be present inside of a cookie, if it was a request from a browser for example.

Think of a SPA but simply rendered on the server for performance, SEO, etc.

I'll let this go I don't want to bog you down in one thread. But there seems to be a disconnect, or we are talking past each other.
The I/O feature I linked to led me to believe Firebase team understands the need for dynamic server rendering of web applications.
From there, the use case of the particular dynamic data needing to be *user specific data* doesn't seem like such a stretch.

Therefore the need to get the same authorization guarantees on the server that we get on the client. Which would mean impersonating users, not simply using the open-ended admin SDK.
But maybe I'm overthinking it, I don't know. I appreciate your help.

Kato Richardson

unread,
May 31, 2017, 3:09:34 PM5/31/17
to Firebase Google Group

In that case, you can just use limited privileges via setDatabaseVariableOverride(). Pretty sure this is answered on Stack Overflow already, but for the sake of convenience, I’ll just answer in simplified form here:

const extend = require('node.extend');
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const config = functions.config().firebase;

// later inside your function
let userConf = extend({databaseAuthVariableOverride: userId}, config);
const ref = admin.initializeApp(userConf, userId).database.ref();

☼, Kato

To unsubscribe from this group and stop receiving emails from it, send an email to firebase-talk+unsubscribe@googlegroups.com.

To post to this group, send email to fireba...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages