How to create a session cookie for persistent login in Firebase? - firebase

I've been going over Manage Session Cookies to get a better understanding of how firebase auth works. However I have run into some trouble when it comes to implementing cookies. I have been able to successfully follow the example up to the section labeled sign-in but have been unable to follow the next step where I generate my session cookie.
I would like to use my cookie as a jwt in order to do checks on users making request to my endpoints and trying to access my site. However I am unable to actually generate a cookie and am unsure why. The syntax is a bit confusing for me because I am using express while they are not.
So to clarify I believe that I have correctly set up my code but due to the fact that I have not been able to create a token I must not have. Also note that I am not doing the csrf step and will come back for that later and am mainly concerned with generating my token. If anyone can point out why my token might not be generating or point me in the right direction I would be grateful.
Code:
import functions from 'firebase-functions'
import { initializeApp } from 'firebase/app';
import { getAuth, getIdToken, signInWithEmailAndPassword } from "firebase/auth";
import express from 'express';
import cors from 'cors';
import pool from './db.js';
import cookies from "cookie-parser";
const firebaseConfig = {
apiKey: process.env.FB_APIKEY,
authDomain: process.env.FB_AUTHDOMAIN,
projectId: process.env.FB_PROJECTID,
storageBucket: process.env.FB_STORAGEBUCKET,
messagingSenderId: process.env.FB_MESSAGINGSENDERID,
appId: process.env.FB_APPID,
measurementId: process.env.FB_MEASUREMENTID,
};
const app = express();
const firebaseApp = initializeApp(firebaseConfig)
const auth = getAuth()
app.use(cors({origin:true}));
app.use(express.json());
app.use(cookies());
app.get('/firebaseLogin/:body',async(req,res)=>{
let {body} = req.params
body = JSON.parse(body)
const expiresIn = 60 * 60 * 24 * 5 * 1000;
try{
let signInAttempt = await signInWithEmailAndPassword(auth,body.user,body.pass)
let idToken = await getIdToken(signInAttempt.user) // Grabs user token after sign in is approved
getAuth()
.createSessionCookie(idToken, { expiresIn })
.then(
(sessionCookie) => {
// Set cookie policy for session cookie.
const options = { maxAge: expiresIn, httpOnly: true, secure: true };
res.cookie('session', sessionCookie, options);
res.end(JSON.stringify({ status: 'success' }));
},
(error) => {
res.status(401).send('UNAUTHORIZED REQUEST!');
}
);
}catch(error){
res.json(error)
}
})

Related

How to retrieve auth cookie on serverInit in Pinia store?

I have recently started learning Nuxt 3 by trying to implement basic user auth logic with firebase and now I would need to retrieve the cookie from the req to initialise the user auth state inside Pinia store but it seems like nuxtServerInit is not supported by Pinia. After realising this I proceeded to a different solution where I'm using server middleware to pass the req object to an action called nuxtServerInit() but then I ran into another problem which is that I cannot call the auth().verifyIdToken(token) from there because the firebase is initialised inside a plugin which runs afterwards.
After this I also tried to initialise firebase inside server middleware and pass auth object to nuxtServerInit() action and set it to the state but then I could not call an action from there I guess because Pinia has not initialised yet?
How could I overcome the original problem? What could be a better approach?
Heres the firebase init plugin:
import { initializeApp } from 'firebase/app'
import { getAuth } from "firebase/auth"
import { getFirestore } from 'firebase/firestore'
import { useStore } from '~/store/index.js'
export default defineNuxtPlugin(nuxtApp => {
const firebaseConfig = {
apiKey: "API_KEY",
authDomain: "AUTH_DOMAIN",
projectId: "PROJECT_ID",
storageBucket: "STORAGE_BUCKET",
messagingSenderId: "MESSAGING_SENDER_ID",
appId: "APP_ID"
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app)
const firestore = getFirestore(app)
nuxtApp.vueApp.provide('auth', auth)
nuxtApp.provide('auth', auth)
nuxtApp.vueApp.provide('firestore', firestore)
nuxtApp.provide('firestore', firestore)
const store = useStore()
console.log('AUTH', auth)
auth.onAuthStateChanged((user) => {
store.setAuth(user)
})
})

Firebase authentication not working in NuxtJs application

In am using Firebase Authentication in my Nuxt App. I am following the Firebase Documentation for setting up Authentication using GoogleAuthProvider. In my case, onAuthStateChanged() listener returns null even after successfull redirect.
When the user enter my web application, Nuxt will execute the plugin in the client side. There the firebase configuration is initialized. Then the initUser() function defined in useFirebaseAuth.ts composable is called. If user successfully logged In, log the user details.
Note : After I encountered this error, I replaced signInWithRedirect to signInWithPopup, it works very well.
// plugins/firebase.client.ts
import { initializeApp } from "firebase/app";
import { initUser } from "~~/composables/useFirebaseAuth";
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig();
const firebaseConfig = {
apiKey: "...",
authDomain: "...",
databaseURL: "...",
projectId: "...",
appId: "...",
messagingSenderId: "...",
};
// Initialize Firebase
const firebaseApp = initializeApp(firebaseConfig);
initUser(firebaseApp);
})
// composables/useFirebaseAuth.ts
import { FirebaseApp } from "firebase/app";
import { getAuth,signInWithPopup, GoogleAuthProvider, onAuthStateChanged, signInWithRedirect, signOut, User } from "firebase/auth";
import { equalTo, getDatabase, onValue, orderByChild, query, ref as dbRef } from "firebase/database";
export const signInUser = async () => {
const provider = new GoogleAuthProvider();
const auth = getAuth();
await signInWithRedirect(auth, provider)
}
export const initUser = async (firebaseApp: FirebaseApp) => {
const auth = getAuth(firebaseApp)
const db = getDatabase()
onAuthStateChanged(auth, user => {
if (user) {
console.log(user)
} else {
console.log("user not logged in"
return navigateTo('/login')
}
});
}
export const signOutUser = async () => {
const auth = getAuth();
await signOut(auth)
}
In the above code, I always get the user value as null. I am working on the project for 2 months. The authentication works fine almost one month ago. But it is not working now. Please help me to resolve the issue.
# node --version
v19.5.0
# npm version
{
npm: '9.3.1',
node: '19.5.0',
v8: '10.8.168.25-node.11',
uv: '1.44.2',
zlib: '1.2.13',
brotli: '1.0.9',
ares: '1.18.1',
modules: '111',
nghttp2: '1.51.0',
napi: '8',
llhttp: '8.1.0',
uvwasi: '0.0.14',
acorn: '8.8.1',
simdutf: '3.1.0',
undici: '5.14.0',
openssl: '3.0.7+quic',
cldr: '42.0',
icu: '72.1',
tz: '2022g',
unicode: '15.0',
ngtcp2: '0.8.1',
nghttp3: '0.7.0'
}
I have gone through many answers posted for same question in the stackoverflow website. But those answers didn't solved my issues.

Firebase OAuth with Google sends me to a blank page

I am trying to do a simple sign in/register with google using firebase auth for a Vue.js project I am working on. Unfortunately, both signInWithRedirect and signInWithPopup are sending me to blank pages with no error! Here is the situation:
I have configured the firebase app within the firebase console and set up google as a sign-in method. Signing in with email is working, so I know the firebase initialization is working. Here is that stuff anyway:
from main.js:
import { initializeApp } from "firebase/app";
const firebaseConfig = {
apiKey: "****",
authDomain: "date-night-mevn.firebaseapp.com",
projectId: "date-night-mevn",
storageBucket: "date-night-mevn.appspot.com",
messagingSenderId: "1054265433143",
appId: "1:1054265433143:web:9fc18db10198531a3573b2"
};
initializeApp(firebaseConfig);
From App.vue:
import { onMounted, ref } from 'vue';
import { getAuth, onAuthStateChanged, signOut } from '#firebase/auth';
import router from './router';
const isLoggedIn = ref(false);
let auth;
onMounted (() => {
auth = getAuth();
onAuthStateChanged(auth, (user) => {
if(user) {
isLoggedIn.value = true;
} else {
isLoggedIn.value = false;
}
});
});
const handleSignOut = () => {
signOut(auth).then(() => {
router.push("sign-in");
});
};
From Sign Up page:
const signUpWithGoogle = () => {
const provider = new GoogleAuthProvider();
signInWithPopup(getAuth(), provider)
.then((result) => {
console.log(result);
router.push("/");
})
.catch((error) => {
console.log(error)
alert(error.message)
});
}
On the above section, I have tried assigning auth to a variable outside the sign in function call as well as adding an event param to the function and running
event.preventDefault() before trying to sign in, but neither did anything for me.
It doesn't matter if I use signInWithRedirect or signInWithPopup, I always get a blank page. Here is the url it is trying to load: "https://date-night-mevn.firebaseapp.com/__/auth/handler?apiKey=******&appName=%5BDEFAULT%5D&authType=signInViaRedirect&redirectUrl=http%3A%2F%2Flocalhost%3A8080%2Fregister&v=9.9.2&providerId=google.com&scopes=profile" The only thing that looks strange to me here is "appName=%5BDEFAULT%5D", but that very well may be correct. I have seen a few other posts on this topic, but many are old and none yielded a solution for me. I look forward to any help I can get, cause it sure seems like I need it!
UPDATE: Today I tried the sign in with google button from my work computer (macbook) and it worked... I am also able to sign in with google on my iPhone, just not with my windows desktop. I have tried multiple browsers and turning vpn on/off, doesn't seem to make a difference.

How can I use ApolloClient to pass fresh authorization headers to the server when using the Firebase Admin SDK?

I am using the Firebase Admin SDK for my server — in my case Apollo Server running in google cloud functions. Upon login, the server generates a token and returns it to the client. The client puts it into local storage and uses it for future requests. This works fine until the token expires 1 hour later.
How can I refresh the token so that the user doesn't need to keep logging in every hour?
On the server, login returns a token; the client will put it in local storage
exports.login = async (email, password) => {
const data = await firebase
.auth()
.signInWithEmailAndPassword(email, password);
const token = await data.user.getIdToken();
return token;
}
On the client, ApolloClient includes the token with auth headers
I'm using Gatsby in this example, but I've had the same trouble doing this with create-react-app.
import fetch from 'isomorphic-fetch';
import {ApolloClient, HttpLink, InMemoryCache} from '#apollo/client';
import {setContext} from 'apollo-link-context';
const httpLink = new HttpLink({
uri: `${process.env.GATSBY_API_URL}`,
fetch,
});
const authLink = setContext((_, {headers}) => {
const token = localStorage.getItem('myUserToken');
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
};
});
export const client = new ApolloClient({
cache: new InMemoryCache(),
link: authLink.concat(httpLink),
connectToDevTools: true,
});
One potential solution might be, if I didn't use Graphql and ApolloClient, to import firebase.auth on the client and generate a refreshed auth token before every request. For example:
// firebase_service.js
import * as firebase from 'firebase/app';
import 'firebase/auth';
import { config } from './firebase-config';
firebase.initializeApp(config);
export const auth = firebase.auth;
// getData.js
import { auth } from '../firebase_service';
import axios from 'axios';
const getData = () => {
const token = await auth().currentUser.getIdToken(true);
return axios
.get(FIREBASE_API_URL, {
headers: { authorization: `Bearer ${token}` },
})
.then((res) => res.data);
}
Would there be a way to do something like this but with ApolloClient, so that the headers include a fresh token when appropriate?

Facebook login with Firebase in React Native error 400

I am having trouble to register a user in Firebase with his Facebook credentials in RN. The Facebook and the Firebase apps are setup correctly, since everything is working as expected in Swift. The OAuth redirect URI is also setup correctly.
However when I try to do the same process from RN it fails. My setup is the following, when the user taps my login button I call FB's LoginManager.logInWithReadPermissions(['public_profile', 'email']) and on its success I call FirebaseManager's (which is my custom class for managing Firebase) signUpWithFacebook(). I get a FB access token correctly and I can see that a credential object is created.
However, Firebase always returns the error:
{"error":{"errors":[{"domain":"global","reason":"invalid","message":"A system error has occurred"}],"code":400,"message":"A system error has occurred"}}
The FirebaseManager looks like this:
import { AsyncStorage, NativeModules } from "react-native";
import * as firebase from 'firebase';
const firebaseConfig = {
apiKey: "key",
authDomain: "domain",
databaseURL: "db_url",
projectId: "project_id",
storageBucket: "storage_bucket"
};
const firebaseApp = firebase.initializeApp(firebaseConfig);
const FBSDK = require('react-native-fbsdk');
const {
AccessToken
} = FBSDK;
export default class FirebaseManager {
constructor() {
// Some logic...
}
signUpWithFacebook() {
AccessToken.getCurrentAccessToken().then((data) => {
let accessToken = data.accessToken
console.log('FB accessToken: ' + accessToken)
const provider = new firebase.auth.FacebookAuthProvider();
const credential = provider.credential(accessToken);
console.log('FB credential: ' + credential)
firebase.auth().signInWithCredential(credential)
.then()
.catch((error) => {
console.log('Failed to sign in Firebase with Facebook. ' + error)
})
})
}
}
I would also like to note that Firebase's anonymous & email/password authentication are working with no problem.
My workaround for the moment is to do the Facebook login from Swift and return the user object in RN with a bridge and RN's NativeModules.
Some more info about my project:
react-native: 0.44.2
firebase: 4.0.0
react-native-fbsdk: 0.6.0
Any help would be appreciated!
Might be a long shoot but this looks VERY similar to a an issue in firebase JS SDK. The solution was to not make an instance of FacebookAuthProvider, by changing these lines:
const provider = new firebase.auth.FacebookAuthProvider();
const credential = provider.credential(accessToken);
to this:
var credential = firebase.auth.FacebookAuthProvider.credential(accessToken);

Resources