How to authenticate a firebase user from server without client side auth?
Asked Answered
W

3

19

I have an API which uses the node admin sdk to connect and call to firebase. My clients hit my api for all the things they need. I do not want them to have to call out to firebase to authenticate directly because I want the client code decoupled from the api and backend.

How can the server authenticate them? Based on current docs even at minimum clients must provide their uid to the api (which assumes they authenticated on their own, right?).

Ideally, the clients would provide username and password in the body of a POST over ssl to my api and the api would log them in and send back their id token. What would be the recommended way to do this?

Westing answered 8/10, 2017 at 1:0 Comment(0)
W
10

Just wanted to provide an update: an answer to this with undocumented REST API's can be found here: Firebase REST auth when creating token with node.js admin sdk

If I could I would mark this as the answer.

Westing answered 12/10, 2017 at 23:22 Comment(0)
T
11

If you want to use Firebase for authentication, it is best handled on the client side by a client SDK. This is because authentication is rate-limited based on IP address and it also allows you to skip the process of coding in session management and persistence.

However, you can achieve what you want if you expect a low number of logins/users by hosting the client SDK on your server and hand-balling the request off to Firebase.

// app.js

const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const express = require('express');
const firebase = require('firebase'); // client SDK

firebase.initializeApp({
  apiKey: "<API_KEY>",
  authDomain: "<PROJECT_ID>.firebaseapp.com"
});

const app = express();
app.use(bodyParser.json());
app.use(cookieParser(['array', 'of', 'secrets']));

// on future requests, the UID can be found using `req.cookies['__session'].uid`

app.post('/login', function (req, res, next) {
  if (!req.body.email) return res.status(400).json({error: 'missing email'});
  if (!req.body.password) return res.status(400).json({error: 'missing password'});

  firebase.auth().setPersistence(firebase.auth.Auth.Persistence.NONE) // don't persist auth session
  .then(function() {
    return firebase.auth().signInWithEmailAndPassword(req.body.email, req.body.password)
  });
  .then((user) => { // https://firebase.google.com/docs/reference/js/firebase.User
    let uid = user.uid;

    // set cookie with UID or some other form of persistence
    // such as the Authorization header
    res.cookie('__session', { uid: uid }, { signed: true, maxAge: 3600 });
    res.set('cache-control', 'max-age=0, private') // may not be needed. Good to have if behind a CDN.
    res.send('You have successfully logged in');

    return firebase.auth().signOut(); //clears session from memory
  })
  .catch((err) => {
    next(err);
  });
});

module.exports = app;

Note: You may also consider co-locating your API using Cloud Functions. Depending on your use case, this may be the cost effective option.

Tebet answered 8/10, 2017 at 15:17 Comment(5)
Thanks, cool that we can run the client library on the server, if needed. I am going to keep the session management in the clients for now and see how far I get with that setup.Westing
is this still true now?Ferruginous
@Ferruginous To my knowledge, the rate-limiting applied by the authentication API still applies as an abuse prevention measure. Aside from that, the code would need to be adjusted for the new version of the Firebase SDK, modern JavaScript standards and to remove the bug where two concurrent login requests causes a conflict. Brian's other answer covers a way to bypass the SDK and avoid that problem.Tebet
@Tebet thanks for the response. If I am understanding correctly you're saying using the rest api on the server avoids rate-limiting? However that doesn't make sense to me because why did they implement rate limiting for client api but not rest api?Ferruginous
@Ferruginous Apologies for the confusion, reading back my comment, I can see the ambiguity. The rate-limiting always applies either from client or server when exchanging credentials for an ID token. My "avoid that problem" comment was referring to the "bug where two concurrent login requests causes a conflict".Tebet
W
10

Just wanted to provide an update: an answer to this with undocumented REST API's can be found here: Firebase REST auth when creating token with node.js admin sdk

If I could I would mark this as the answer.

Westing answered 12/10, 2017 at 23:22 Comment(0)
H
0

Its 2024, on my end I needed to create test users from the server, then get the resulting jwt to authenticate the test user on the server.

Heres how to do it according to docs

function createTestUser() {
  const { uid } = await firebaseAdmin.auth.createUser({
    email: "[email protected]"
    password: "password",
  });
  
  const token = await firebaseAdmin.auth.createCustomToken(uid);
  
  const url = `https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=${env.FIREBASE_CLIENT_API_KEY}`;
  const data = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      token,
      returnSecureToken: true,
    }),
  });
  
  const result = await data.json()
  const jwt = result.idToken

  return jwt
}
Hylophagous answered 11/2, 2024 at 18:8 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.