Firebase authentication using google/facebook with custom nodejs server - javascript

I am trying to integrate firebase authentication with my custom nodejs server. The email/password strategy is pretty straightforward as the admin sdk supports all the operations needed. However in the case of providers, the documentation instructs us to Handle the sign-in flow manually
and get some token from either google or facebook and then send it to our nodejs server.
So when the token arrives to our nodejs server the documentation provides some code that comes from the client sdk.
This is a sample from the firebase documentation
import { getAuth, signInWithCredential, GoogleAuthProvider } from "firebase/auth";
// Build Firebase credential with the Google ID token.
const credential = GoogleAuthProvider.credential(id_token);
// Sign in with credential from the Google user.
const auth = getAuth();
signInWithCredential(auth, credential).catch((error) => {
// Handle Errors here.
const errorCode = error.code;
const errorMessage = error.message;
// The email of the user's account used.
const email = error.customData.email;
// The AuthCredential type that was used.
const credential = GoogleAuthProvider.credentialFromError(error);
// ...
});
where the id_token (probably) comes from the request body.
This flow works just fine but it is using the client sdk in a nodejs server environment because the admin sdk that is inteded for such an environment does not support this kind of operation. So, is it ok to use both SDKs, where it is needed of course, in a nodejs server?
EDIT:
What I am trying to achieve is that when the google id_token (or facebook access token) arrives in my nodejs server I need to have a way to create an account in firebase auth module with the respective provider. So with the signInWithCredential if the user does not exist then will be created and then I will issue a custom token just like I do with the email/password strategy. The only difference here is that with email/password strategy I can use the admin SDK's createUser() for the user creation.
Is this approach right?

Your approach is absolutely right :
1- You recieve google ID token from client (OAuth or etc...)
2- Sign in user to firebase using google ID:
//1 import firebase/auth
import { getAuth, signInWithCredential, GoogleAuthProvider } from "firebase/auth";
//2 configue user credentials
const credential = GoogleAuthProvider.credential(id_token);
//3 run signInWithCredential function
let userData = await signInWithCredential(auth, credential)

Related

Why Firebase (onAuthStateChanged) does not see a Google signup user (with signInWithCredential)?

So I followed this tutorial on how to sign in a user with rnfirebase and google signup. And it works fine. Here is the code:
const googleSignUp = async () => {
// Get the users ID token
const { idToken } = await GoogleSignin.signIn();
// Create a Google credential with the token
const googleCredential = auth.GoogleAuthProvider.credential(idToken);
// Sign-in the user with the credential
const user = auth().signInWithCredential(googleCredential);
return { idToken, user };
};
(Let me note here, that the app has already a sign in with email and password way, with Firebase).
Then I realized that the user cannot change his name, email or delete his account.
Looking deeper, I found out that the onAuthStateChanged(firebase.auth, async (user) => ... returns null for the user.
I've seen in some older answers that if you use Google sign up, you need to sign up the user with signInWithCredential, which I use, so this in not the issue.
Could it be a problem that for email/password sign in, I use code from Firebase web and not from rnfirebase? Although I already had a combination of those, using the push notifications from rnfirebase.
Can someone explain why I get this behavior, and how to fix it?
Thanks!
If I understand correctly, you use both the react-native-firebase library (which wraps the native iOS and Android SDKs) and the JavaScript Web SDK for Firebase in your app.
If that is the case, both indeed have a separate sign-in state, and signing into one won't fire onAuthStateChanged listeners on the other.
You'll have to pick one SDK to authenticate with Firebase, and then use that for both providers.

Firebase Storage emulator does't support getSignedUrl

I have the line
onst [url] = await blob.getSignedUrl({ action: 'read', expires: Date.now() + 60 * 1000, contentType: mimetype })
When I run my unit-tests with the Firebase storage emulator I got the error:
Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information
How can I use getSignedUrl with Firebase emulator?
When using a blob signed url, use service account credentials instead of the default ADC. Having been said that, you have two options:
You can create a service account that will use the command using the Cloud SDK: gcloud iam service-accounts keys create FILE_NAME.json --iam-account=NAME#PROJECT_ID.iam.gserviceaccount.com; which you can use to call Firebase server APIs from your app server or trusted environment. After creating your service account, you must initialize with a service account key file.
Here's an example java code for initializing:
FileInputStream serviceAccount = new FileInputStream("path/to/serviceAccountKey.json");
FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.setDatabaseUrl("https://<DATABASE_NAME>.firebaseio.com/")
.build();
FirebaseApp.initializeApp(options);
You can also check the Firebase Service Accounts to help you identify which service account you will use in your project.
Another option is to set the service account key in an environment variables.
For Linux or macOS:
export GOOGLE_APPLICATION_CREDENTIALS="KEY_PATH"
Example is:
export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/service-account-file.json"
For Windows (using powershell):
$env:GOOGLE_APPLICATION_CREDENTIALS="KEY_PATH"
Example is:
$env:GOOGLE_APPLICATION_CREDENTIALS="C:\Users\username\Downloads\service-account-file.json"
Just note that this variable only applies to your current shell session, so if you open a new session, set the variable again.
Update:
In Google Cloud Platform environments, such as Cloud Functions and App Engine, you usually don't provide a keyFilename or credentials during instantiation. In those environments, we call the signBlob API to create a signed URL. As was stated here. In that case, the service account used must have Service Account Token Creator Role.
The Service Account Token Creator Role enables impersonation of service accounts to create OAuth2 access tokens, sign blobs, or sign JWTs. Provide your service account when initializing the client. If using default credentials, then make sure that the Cloud Functions service account must have Service Account Token Creator Role, as it is required when calling the signBlob API if the app is deployed within GCP.
You can further check this github issues comment.

REST API with Google Firebase Authentication & Functions using Bearer Token

Quick Background: I'm programming an API that is thought to be used "standalone" i.e. there is no frontend involved. API access should be possible directly from e.g. Postman or Curl with a Bearer token in the Authentication Header.
I was looking at Google Firebase and thought it is probably a really good fit because all of the authentication is already "builtin" and directly compatible with Google Cloud Functions.
However after a weekend of experimenting I can not seem to figure out how to implement an REST API (With Google Cloud Functions) where the User can (In an web-interface) request an API token to interact with the API.
I don't want to handle authentication myself. I really would love to use the Firebase authentication for the API.
Here is what the final process should look like:
User logs into an web-interface with the standard Firebase Authentication process.
User clicks on something like "Request API Key" and gets a key shown in the web-interface (e.g. abc...). that is generated by Firebase Authentication.
User can make requests with e.g. curl to the API Hosted in Google Cloud Functions and just has to set the Authorization Header (Bearer abc...) and the "validation" of that token is handled by Firebase Authentication.
Here is what I already tried to generate the token:
admin.auth().createCustomToken(uid)
.then(function(customToken) {
console.log(customToken);
})
.catch(function(error) {
console.log('Error creating custom token:', error);
})
And then set the Token logged to the console in Postman as Bearer Token, and then use the following function to verify the token:
const authenticate = async (req, res, next) => {
if (!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) {
res.status(403).send('Unauthorized');
return;
}
const idToken = req.headers.authorization.split('Bearer ')[1];
try {
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
req.user = decodedIdToken;
next();
return;
} catch(e) {
console.log(e);
res.status(403).send('Unauthorized');
return;
}
}
Then I get this error
message: 'verifyIdToken() expects an ID token, but was given a custom token. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.'
I understand that if I would implement an web-interface I could grab the ID token from the devtools (?), but the token is then only valid 1 hour... What I need is a token that is valid "indefinitely" and can be generated and shown to the user.
I think I know that I have to use Custom Tokens somehow but can not figure out how to get them working... (https://firebase.google.com/docs/auth/admin/create-custom-tokens).
Thanks very much in advance everybody!
Best
Rick
You're trying to build an API management solution on top of Firebase and Cloud Functions. Custom tokens and ID tokens are not suitable for this purpose. Custom tokens are only meant to be used as a user authentication credential on end user devices, and ID tokens represent a successful auth response. Both types of tokens expire after an hour.
If you need long-lived, managed API keys, then you will have to implement them yourself. There's nothing built into Firebase that you can use out of the box. I once implemented such a solution as a prototype, where I generated a Firestore document each time a user signed in and requested an API key. Then I used the document ID as the API key, which I could validate in the Cloud Function.
const apiKey = req.headers.authorization.split('Bearer ')[1];
const doc = await admin.firestore().collection('apiKeys').doc(apiKey).get();
if (doc.exists) {
next();
}
I also had to implement some local API key caching to make this work efficiently.
You might be able to avoid some of this work by using a solution like Google Cloud Endpoints (https://cloud.google.com/endpoints), although I don't have any personal experience with that. Finally, also look at open source solutions like https://wso2.com/api-management/ that enable you to set up your own API key management and gateway.

Firebase authentication using NodeJS

So far I was working with Mongodb and Express. There my whole authentication was done by checking req.user object. From what I saw, Firebase authentication is mostly done in the front end. How can I get req.user to work with Firebase in the back end? I saw a couple of tutorials, but they just showed a couple of methods and went on. I mean to ask more about the logic, but some code examples would probably help.
Firebase authentication is mostly done in the front end
Correct. User auth is entirely done client-side when using the provided SDKs from Firebase.
However, if you need to do some special auth, such as integrating with LDAP/AD or some other enterprise shenanigans, then you would need to do custom token creation that client-side SDKs would use to authenticate the user.
How can I get req.user to work with Firebase in the back end?
This is something you will need to implement yourself. The flow client-side would go something like:
User performs auth client-side.
Firebase will set auth state in localstorage by default. See Authentication State Persistence
When a user attempts to access your Express API, you will need to retrieve the token from localstorage and send it with your API request.
Let's assume you attach the token on the request header: FIREBASE_AUTH_TOKEN: abc. See Firebase retrieve the user data stored in local storage as firebase:authUser:
So on the server side, using the Firebase Admin SDK, you will retrieve that token and verify it via verifyIdToken. Quick dirty example below of middleware:
const {auth} = require('firebase-admin');
const authService = auth();
exports.requiresAuth = async (req, res, next) => {
const idToken = req.header('FIREBASE_AUTH_TOKEN');
// https://firebase.google.com/docs/reference/admin/node/admin.auth.DecodedIdToken
let decodedIdToken;
try {
decodedIdToken = await authService.verifyIdToken(idToken);
} catch (error) {
next(error);
return;
}
req.user = decodedIdToken;
next();
}
You would then use this middleware like so:
const express = require('express');
const router = express.Router();
const {requiresLogin} = require('./my-middleware.js');
router.get('/example', requiresLogin, async (req, res) => {
console.log(req.user)
})
I hope this gives you an idea of what to do. I haven't worked with Firebase for a while and the information above is what I gathered from looking at the documentation.
If you plan to have server side sessions only, you should consider using Firebase session cookies: https://firebase.google.com/docs/auth/admin/manage-sessions.
An example is available to show how to use httpOnly cookies at: https://github.com/firebase/quickstart-nodejs/tree/master/auth-sessions

how to modify laravel middleware to work with firebase authentication

I am working in laravel and firebase as a backend for authentication and data storage. I am using firebase provided js code to signin the user
firebase.auth().signInWithEmailAndPassword(email, password).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
});
The problem is that I want to protect my routes from non-authenticated users. How would I check this in my middleware that user is logged in or not because js stores current user's uid in localStorage. Do I have to use a separate database for this purpose? Please help me to go in right direction.
You have to call:
auth.currentUser.getIdToken().then(function(token) {
// The Firebase id token is returned here.
// You will have to send that along your requests to your server.
// Keep in mind this is a short lived token
// You have to call getToken each time you need it in case
// it is auto refreshed underneath.
});
Currently, Firebase provides node.js and java backend libraries to verify the token (to check the user is logged in).
https://firebase.google.com/docs/auth/server
A php library is in the works. When it is ready, it should provide similar functionality for minting custom tokens and verifying Firebase id tokens.
You can simply use this https://github.com/vinkas0/firebase-auth-laravel laravel package to authenticate via firebase

Categories

Resources