Firebase user is authenticated, so why is uploading to storage denied? - javascript

In a react-native project, I'm using both react-native-firebase and the firebase sdk.
react-native-firebase does not allow use of firebase storage to upload image blobs, which is why im using the vanilla Firebase javascript SDK to do this part. For distinction's sake, in my code & this post, im identifying the firebase javascript sdk as 'FIREBASE', and the react-native-firebase as 'firebase'.
I had to initialize my firebase app (even though react-native-firebase doesn't require this for its function, firebase does), App.js constructor & imports:
import * as React from 'react';
import AppNavigation from './src/navigation';
import { Provider } from 'react-redux';
import { store, persistor } from './src/store/index.js';
import firebase from 'firebase/app';
import { PersistGate } from 'redux-persist/integration/react';
export default class App extends React.Component {
constructor (props) {
super(props);
const firebaseConfig = {
apiKey: '{apiKey}',
authDomain: 'project-ID.firebaseapp.com',
databaseURL: 'https://project-ID.firebaseio.com',
projectId: 'project-ID',
storageBucket: 'project-ID.appspot.com',
messagingSenderId: '9999999999'
};
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
}
I implement firebase and FIREBASE in an action (firebase for auth/firestore, and FIREBASE for storage):
import * as types from '../actions/types';
import RNFetchBlob from 'rn-fetch-blob';
import firebase from 'react-native-firebase';
import * as FIREBASE from 'firebase/app';
import 'firebase/storage';
import { Platform } from 'react-native';
const Blob = RNFetchBlob.polyfill.Blob;
const fs = RNFetchBlob.fs;
export const registerUser = (registration) => {
const { email, pass, username, img } = registration;
return (dispatch) => {
dispatch({ type: types.REGISTER_USER });
console.log('starting registration process...');
// check username is unique
firebase
.firestore()
.collection('users')
.where('username', '==', username)
.get()
.then((querySnapshot) => {
if (querySnapshot.empty !== true) {
// back to registration form
registrationFail(dispatch, 'Username already taken. Try again.');
console.log("Registrant's username already exists");
} else {
console.log('Registrants username is unique');
// continue with registration
firebase
.auth()
.createUserWithEmailAndPassword(email, pass)
.then((userCredential) => {
// successful user creation, now authenticated
// write to img storage
uploadImg(dispatch, img, userCredential.user.uid)
.then((imgUrl) => {
// on success, write to firestore
uploadImgSuccess(dispatch, 'Profile image upload successful...');
// write rest of data to firestore
firebase
.firestore()
.collection('users')
.add({
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
username: email,
uid: userCredential.user.uid,
profileImg: imgUrl,
email: email,
})
.catch((err) => {
console.log('Registration failed. Error: ' + err.message);
registrationFail(dispatch, err.message);
});
}
})
.catch((err) => {
// Image Profile NOT Uploaded
uploadImgFail(dispatch, err);
});
})
.catch((err) => {
// unsuccessful user creeation
registrationFail(dispatch, err.message);
});
}
})
.catch((err) => registrationFail(dispatch, err.message));
};
};
const uploadImg = async (dispatch, uri, uid, mime = 'image/png') => {
console.log('Starting image upload...');
dispatch({ type: types.UPLOAD_IMG, info: 'Uploading profile image...' });
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri;
let uploadBlob = null;
// let downloadPath = '';
const imageRef = FIREBASE.storage().ref(uid).child('profileImg');
fs
.readFile(uploadUri, 'base64')
.then((data) => {
return Blob.build(data, { type: `${mime};BASE64` });
})
.then((blob) => {
uploadBlob = blob;
return imageRef.put(blob, { contentType: mime });
})
.then(() => {
uploadBlob.close();
return imageRef.getDownloadURL();
})
.then((url) => {
console.log('Returning Download URL: ' + url);
uploadImgSuccess(dispatch, 'Image upload successful...');
})
.catch((err) => {
uploadImgFail(dispatch, 'Image upload failed: ' + JSON.stringify(err));
});
};
but when I go through uploadImg(), I get an error:
{
"code_": "storage/unauthorized",
"message":"Firebase Storage: User does not have permission to access 'someReference/someChild',
"serverResponse":{"Code":403, "message": "permission denied."}
}
Here is Firestore rule:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
}
Here is Storage rule:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
I don't understand what's going on or why. The user is authenticated during react-native-firebase's createUserWithEmailAndPassword() and is even allowed to upload data to Firestore. My only guess is this possibly has something to do either with using both firebase & FIREBASE, or with the way i've setup firebase with FIREBASE. I've used both together in a previous test project and it worked successfully with the help of a forked project rn-fetch-blob (the maintained version of react-native-fetch-blob), however I had no security rules in place bc of testing, so...
Any ideas for solving this?

Yes, you have guessed it right, your FIREBASE instance is unaware of the auth being done by the firebase since the firebase handles the native aspects and FIREBASE is just a JS thing. so both of the instances have their own lives and their own prospects with own attributes to identify the user and provide authorization.
To resolve this, try authorizing the user either by the vanilla JS SDK or use the rn-firebase for the whole task.
I would recommend using react-native-firebase, it has good support for entire firebase stack.
https://rnfirebase.io/docs/v5.x.x/storage/reference/storage
EDIT:
Vanilla JS firebase SDK should not be used in mobile applications especially when it is native since firebase will treat the client as web client and going forward you will not be able to leverage all possible mobile things from firebase such as dynamic links, push notifications and analytics.
Hope this helps! Cheers!

Related

How to upload document whenever a user signs up in firebase with custom users uid?

I am working with react and firebase ver 9. As firebase offers limited field and I need to send some more data while registering a user. I saw that you can do this while uploading document whenever a user signs up. I have written the code for it but when I run it only users are registered but my document doesn`t get uploaded.
Here`s my code:
import { useState } from "react"
import { auth, db } from "../Authentication/firebaseConfig"
import { createUserWithEmailAndPassword } from "firebase/auth"
import {
collection,
setDoc,
addDoc,doc
} from "firebase/firestore";
export const useSignup = () => {
const [error, setError] = useState(null)
const signup = async (email, password, displayName) => {
setError(null)
const current_user = await createUserWithEmailAndPassword(auth, email, password, displayName);
const uid = current_user.user.uid;
await addDoc(collection(db, "users"), {
uid: uid,
displayName,
email,
})
.then((res) => {
console.log('user signed up:', res.user)
})
.catch((err) => {
setError(err.message)
})
}
return { error, signup }
}
As stated by #Danial, the error was produced by Cloud Firestore Rules that is set to false.
Sample Rule:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
}
}
Sample Firestore rule above will prohibit you to read and write to any collection and document. Changing it to true allows the public to read and write to Firestore.

firebase realtime database undefined function

I'm trying to retrieve from a realtime db I started by init the app with the configuration like so
import {initializeApp} from 'firebase/app';
import {getDatabase} from 'firebase/database';
let config = {
apiKey: 'xxxxxxxxxxxx',
authDomain: 'xxxxxxxxxxxx',
...
};
const app = initializeApp(config);
const database = getDatabase(app);
then when I try using the database variable it crashes
componentDidMount() {
database
.ref(`master/tickets`)
.once('value', snapshot => {
var obj = snapshot.val();
console.log('obj :', obj);
})
.catch(error => {
console.log(error);
});
}
I also have tried firebase.database.ref() it shows error cannot find variable firebase
What I am doing wrong? It was working fine in previous builds
The Firebase Web SDK underwent major breaking changes with the update to V9.0.0.
Please see the upgrade guide for details on how to modify older patterns to the new API.
import { ref, get } from "firebase/database";
import { database } from "...";
/* ... */
componentDidMount() {
get(ref(database, 'master/tickets'))
.then(snapshot => {
const obj = snapshot.val();
console.log('obj :', obj);
})
.catch(error => {
console.log(error);
});
}

verifyIdToken method doesn't exist in Auth type using getAuth from firebase/auth

I'm trying to follow the example given here where it shows the following example to verify an ID token:
// idToken comes from the client app
getAuth()
.verifyIdToken(idToken)
.then((decodedToken) => {
const uid = decodedToken.uid;
// ...
})
.catch((error) => {
// Handle error
});
My code looks like this:
function createFirebaseAdminApp(config: AppOptions) {
if (getApps().length === 0) {
return initializeApp(config);
} else {
return getApp();
}
}
const options: AppOptions = {
credential: cert({
projectId: process.env.FIREBASE_PROJECT_ID,
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
privateKey:
process.env.FIREBASE_PRIVATE_KEY != undefined
? process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, "\n")
: "",
}),
databaseURL: process.env.FIREBASE_DATABASE_URL,
};
const firebaseAdmin = createFirebaseAdminApp(options) as FirebaseApp;
const adminAuth = getAuth(firebaseAdmin);
adminAuth
.verifyIdToken(token)
.then((decodedToken) => {
res.locals.decodedToken = decodedToken;
next();
})
.catch(() => {
next(new HttpError("Invalid token provided", 403));
});
But I keep getting
Property 'verifyIdToken' does not exist on type 'Auth'
I have the latest version of the firebase package, which I assume the example given by the docs is using considering it uses getAuth, so can't tell what I'm doing wrong. Also I'm trying to avoid mixing firebase-admin and firebase, not sure if this is correct, but if I mixed them I can't seem to avoid having to initialize too App instances.
Thanks for any help!
You are mixing up the Admin SDK and the client SDK.
The documentation you linked is for the Admin SDK only. Notice how it is organized in the section for Admin. It is not using getAuth anywhere in that page. The Admin SDK is initialized completely differently than the client SDK, and it does not work at all in browsers. It runs on secure backens only.
The client SDK that you're using doesn't have a function for verifying tokens. That is for secure backends only using the Adminn SDK.

"firebaseApp.auth.RecaptchaVerifier is not a constructor" error when using firebase phone authentication in Reactjs

I am importing 'firebaseApp' from a file where I have done all the setup of firebase and I have implemented email, google and Facebook authentication but when I am implementing the phone number auth the ReCaptcha is not a constructor I am using ReactJs functional component.
Is there any way of implementing phone number auth without ReCaptcha or if not how can I fix the error.
Setup of firebase
import firebase from 'firebase/app';
import 'firebase/auth'
import 'firebase/firestore';
// Web app's Firebase configuration
var firebaseConfig = {
apiKey: "",
authDomain: "",
databaseURL: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: ""
};
// Initialize Firebase
export const firebaseApp = firebase.initializeApp(firebaseConfig);
export const auth = firebase.auth();
export const db = firebase.firestore();
export const google_provider = new firebase.auth.GoogleAuthProvider();
export const facebook_provider = new firebase.auth.FacebookAuthProvider();
This is the place I am taking the number as user input and sending the OTP for verification but the sample code number is hardcoded.
import { firebaseApp, auth } from '../../../firebase/firebasesetup'
function Form() {
const setuprecaptcha = () => {
window.recaptchaVerifier = new firebaseApp.auth.RecaptchaVerifier('recaptcha-container',
{
'size': 'invisible',
'callback': function (response) {
console.log("captcha resolved");
// sendsms();
}
});
}
const sendsms = () => {
setuprecaptcha();
var phoneNumber = '+918220310506';
var appVerifier = window.recaptchaVerifier;
auth.signInWithPhoneNumber(phoneNumber, appVerifier)
.then(function (confirmationResult) {
window.confirmationResult = confirmationResult;
}).catch(function (error) {
alert("not sent")
});
}
return (
<input type="text" placeholder="Mobile" value={mob}
onChange={e => setMob(e.target.value)} />
<div id="recaptcha-container"></div>
<button onClick={sendsms} id='sign-in-button'>Send otp</button>
)
}
export default Form
Ok, so I am answering My own question. This kind of looks Weird but still if anyone of you faces the same problem as mine.
2 things I need to solve in the firebase_setup file and add the main function in React functional component. (Total 3 updates)
firebase_setup file
first
import firebase from 'firebase'; and not import firebase from 'firebase/app';
second
firebase.initializeApp(firebaseConfig); export const firebaseApp = firebase
React functional component
import { firebaseApp} from './firebase_setup';
const sendsms = () => {
//If you want to make the recaptcha invisible
var recaptcha = new firebaseApp.auth.RecaptchaVerifier('recaptcha-container', {
'size': 'invisible'
});
//If you want to make the recaptcha visible
var recaptcha = new firebaseApp.auth.RecaptchaVerifier('recaptcha-container');
//Your phone number with the country code
var number = '+**********';
//actual code begins here
auth.signInWithPhoneNumber(number, recaptcha).then(function (e) {
var code = prompt("enter the code sent to your mobile number");
if (code === null) return;
e.confirm(code).then(function (result) {
alert(result.user + ' verified ')
}).catch(function (error) {
alert('Could not verify,Please try again');
});
}).catch(function (error) {
alert('Please try again.We were unable to reach your phone.Select the correct code and the phone number');
});
}

null is not an object while evaluating current user in firebase

im getting type error null is not an object evaluating firebase.auth().currentUser.uid
I am trying to get the current user id and store it into firebase database using an if statement logic for whether the user is an agent or client.
import * as firebase from "firebase";
// Your web app's Firebase configuration
var firebaseConfig = {
XXX
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const UserId = firebase.auth().currentUser.uid;
export function login({ email, password }) {
firebase.auth().signInWithEmailAndPassword(email, password);
}
export function signup({ email, password, displayName }) {
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then((userInfo) => {
console.log(userInfo);
userInfo.user
.updateProfile({ displayName: displayName.trim() })
.then((user) => {
if (this.state.checked == "first") {
firebase
.database()
.ref("client/" + UserId)
.child("client")
.update({ email, displayName, UserId });
} else {
firebase
.database()
.ref("agent/" + UserId)
.child("agent")
.update({ email, displayName, UserId });
}
});
});
}
export function subscribeToAuthChanges(authStateChanged) {
firebase.auth().onAuthStateChanged((user) => {
authStateChanged(user);
});
}
firebase.auth().currentUser will be null when no user is signed in. The error is telling you that is the case. You shouldn't try to access any properties on it until the sign-in completes successfully.
When sign-in does complete, you will have a user object delivered to your callback, so there is not even a need to use firebase.auth().currentUser at all here.
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then((userInfo) => {
console.log(userInfo);
In your code above, userInfo.user is that current user object. Use it instead of firebase.auth().currentUser.

Categories

Resources