How to get Twitter username from Firebase Authentication - Javascript - javascript

I'm using firebase twitter authentication for my project. The auth variable returning the credentials does not contain the account's twitter username but everything else.
I need to work with the username, is there a way to work around this?
Users shown in the console look like thisFirebase Console
How do I get the respective identifier of a uid?
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.1.2/firebase-app.js";
import { getAuth, signInWithPopup, TwitterAuthProvider } from "https://www.gstatic.com/firebasejs/9.1.2/firebase-auth.js";
const app = initializeApp(firebaseConfig);
const provider = new TwitterAuthProvider();
const auth = getAuth();
document.querySelector('button').addEventListener('click', authenticate);
function authenticate() {
signInWithPopup(auth, provider)
.then((result) => {
const credential = TwitterAuthProvider.credentialFromResult(result);
const token = credential.accessToken;
const secret = credential.secret;
const user = result.user;
console.log(result)
}).catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
const email = error.email;
const credential = TwitterAuthProvider.credentialFromError(error);
console.log(error);
});
}
Is there a way to get the username from this 'auth' variable, check below code for ref
import { getAuth } from "https://www.gstatic.com/firebasejs/9.1.2/firebase-auth.js"
const auth = getAuth();
auth.onAuthStateChanged(user => {
if(user){
// window.location.href = "/home/index.html"
}else{
}
})

I think you mean Twitter handle (also called screen name) by "username"
const provider = new TwitterAuthProvider();
const userInfo = await signInWithPopup(auth, provider);
console.log(userInfo._tokenResponse.screenName) // twitter handle

Hello there you can try this:
firebase.auth().signInWithPopup(new firebase.auth.TwitterAuthProvider())
.then((userCredential) => {
// here you get the username
console.log(userCredential.additionalUserInfo.username);
})
.catch((error) => {
console.log("error occurred");
});
or else you can get the info using this if you are having id :
let url = `https://api.twitter.com/1.1/users/show.json?user_id=${the_uid_from_provider_data}`;
fetch(url)
.then(response => {
let data = response.json();
console.log(data);
})
.catch(error => {
// handle the error
});

For me it works like that
const res = await signInWithPopup(auth, provider);
const user = res.user;
const username = user.reloadUserInfo.screenName;
console.log(`username #${username}`);

Related

While running my next.js app that is using solidity smart contracts, I am getting "Cannot read properties of undefined" error

I was running my next.js app and trying to fetch user I am getting "cannot read properties of undefined" error
And following error in the console
Below is the code I was using
import Ewitter from './Ewitter.json';
import ethers from 'ethers';
import { useState, useEffect } from 'react';
const ContractABI = Ewitter.abi;
const ContractAddress = '0x5FbDB2315678afecb367f032d93F642f64180aa3';
const Ethereum = typeof window !== 'undefined' && (window as any).ethereum;
const getEwitterContract = () => {
const provider = new ethers.providers.Web3Provider(Ethereum);
const signer = provider.getSigner();
const EwitterContract = new ethers.Contract(
ContractAddress,
ContractABI,
signer
);
return EwitterContract;
};
const useEwitter = () => {
// const Ewitter = getEwitterContract();
const [currentAccount, setCurrentAccount] = useState<string>('');
const [currentUser, setCurrentUser] = useState<string>('');
const connect = async () => {
try {
if (!Ethereum) {
alert('Please install MetaMask');
return;
}
const accounts = await Ethereum.request({
method: 'eth_requestAccounts',
});
if (accounts.length === 0) {
alert('Please unlock MetaMask');
return;
}
const account = accounts[0];
console.log('connected to account: ', account);
setCurrentAccount(account);
} catch (errors) {
console.log(errors);
}
};
useEffect(() => {
if(!Ethereum){
console.log("No ethereum wallet found, please install metamask")
return ;
}
connect();
}, []);
useEffect(() =>{
if(currentAccount){
getUser();
}
}, [currentAccount])
const getUser = async ()=>{
const contract = getEwitterContract();
const user = await contract.getUser(currentAccount);
const {avatar, bio, name, username, wallet} = user;
console.log(user);
return user;
}
return { connect, account: currentAccount };
};
export default useEwitter;
#Update1
I've changed import ethers from 'ethers' to import {ethers} from 'ethers' and now I'm facing this error
If unable to understand properly or if you want to see the whole codebase then this is the link to the github repo
https://github.com/ChiragDogra/ewitter/blob/userIssue/dapp/hooks/useEwitter.ts
believe or not I just had that issue.
the problem is how you are importing ethers. Should be
import { ethers } from "ethers";

Using Firebase v9, how can I add the user to the user collection upon logging in with gmail?

How can I add a user to the users collection logging in with Gmail?
I tried the addUser but it does not work. I'm quite new to Firebase v9
//firebase
import { signInWithPopup, GoogleAuthProvider } from "firebase/auth";
import { auth, signInWithGoogle, db } from "../../Firebase/utils";
import { doc, setDoc, collection } from "firebase/firestore";
const Login = (props) => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const addUser = async () => {
const userRef = doc(db, "users", auth.currentUser);
setDoc(userRef);
};
useEffect(() => {
addUser();
}, []);
const googleHandler = async () => {
signInWithGoogle.setCustomParameters({ prompt: "select_account" });
signInWithPopup(auth, signInWithGoogle)
.then((result) => {
// This gives you a Google Access Token. You can use it to access the Google API.
const credential = GoogleAuthProvider.credentialFromResult(result);
const token = credential.accessToken;
// The signed-in user info.
const user = result.user;
// redux action? --> dispatch({ type: SET_USER, user });
addUser();
console.log(auth.currentUser, "login page");
})
.catch((error) => {
// Handle Errors here.
const errorCode = error.code;
const errorMessage = error.message;
// The email of the user's account used.
const email = error.email;
// The AuthCredential type that was used.
const credential = GoogleAuthProvider.credentialFromError(error);
// ...
});
};
return (
<>
<form>
<Button onClick={googleHandler}>Login with Gmail</Button>
</form>
</>
);
};
export default Login;
These are my package.json just to be sure:
This is what the console.log(auth.currentUser) shows:
UPDATE:
const addUser = async (userId) => {
const userRef = doc(db, "users", userId);
return await setDoc(userRef, { ...data });
};
useEffect(() => {
addUser();
}, []);
const googleHandler = async () => {
signInWithGoogle.setCustomParameters({ prompt: "select_account" });
signInWithPopup(auth, signInWithGoogle)
.then(async (result) => {
// This gives you a Google Access Token. You can use it to access the Google API.
const credential = GoogleAuthProvider.credentialFromResult(result);
const token = credential.accessToken;
// The signed-in user info.
const user = result.user;
// redux action? --> dispatch({ type: SET_USER, user });
// addUser();
const { isNewUser } = getAdditionalUserInfo(result);
if (isNewUser) {
await addUser(user.uid);
} else {
console.log("User already exists");
}
})
.catch((error) => {
// Handle Errors here.
const errorCode = error.code;
const errorMessage = error.message;
// The email of the user's account used.
const email = error.email;
// The AuthCredential type that was used.
const credential = GoogleAuthProvider.credentialFromError(error);
// ...
});
};
The doc() function takes Firestore instance as first argument and the rest are path segments (strings) so you cannot pass currentUser object there. Also there might be a chance that auth.currentUser. You can use isNewUser property to check if the user has just signed up or is logging in again and then add the document. Try refactoring the code as shown below:
signInWithPopup(auth, signInWithGoogle)
.then(async (result) => {
const user = result.user;
const { isNewUser } = getAdditionalUserInfo(result)
if (isNewUser) {
await addUser(user.uid);
} else {
console.log("User already exists")
}
})
const addUser = async (userId) => {
const userRef = doc(db, "users", userId);
return await setDoc(userRef, {...data});
};

Firebase Auth Multifactor - user object not returning multiFactor property

I'm trying to add MFA inside my web app and the multiFactor property is missing.
Check the code:
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.6.2/firebase-app.js";
import { getAuth, RecaptchaVerifier, PhoneAuthProvider, signInWithEmailAndPassword }
from "https://www.gstatic.com/firebasejs/9.6.2/firebase-auth.js";
const firebaseConfig = {
...
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
auth.onAuthStateChanged((user) => {
const userEl = document.getElementById('user');
if (user) {
userEl.innerHTML = `${user.email} logged in. ${JSON.stringify(
user.multiFactor.enrolledFactors
)}`;
} else {
userEl.innerHTML = 'signed out';
}
});
window.recaptchaVerifier = new RecaptchaVerifier('recaptcha-container', {
'size': 'invisible',
'callback': (response) => {
console.log('captcha solved!');
}
}, auth);
const enrollBtn = document.getElementById('enroll-button');
enrollBtn.onclick = () => {
signInWithEmailAndPassword(auth, 'blabla#gmail.com', 'foobar').then(() => {
const user = auth.currentUser;
if (!user) {
return alert('User not logged!');
}
const phoneNumber = document.getElementById('enroll-phone').value;
console.log(user);
user.multiFactor.getSession().then((session) => {
const phoneOpts = {
phoneNumber,
session,
};
const phoneAuthProvider = new PhoneAuthProvider();
phoneAuthProvider.verifyPhoneNumber(
phoneOpts,
window.recaptchaVerifier
).then((verificationId) => {
window.verificationId = verificationId;
alert('sms text sent!');
});
});
});
};
In the code above the user.multiFactor is undefined. The signIn is returning the user normally, but without this property.
error on console:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'getSession')
The Firebase project have MFA enabled:
enter image description here
**************** UPDATE *******************
Apparently change the code to this worked:
const mfaUser = multiFactor(user);
mfaUser.getSession().then((session) => {
But now I'm getting this error when I call verifyPhoneNumber:
VM21778 index.html:315 TypeError: Cannot read properties of undefined (reading 'tenantId')
at _addTidIfNecessary (firebase-auth.js:1934:14)
at startEnrollPhoneMfa (firebase-auth.js:6778:125)
at _verifyPhoneNumber (firebase-auth.js:8500:40)
However I'm not using Multi-Tenancy option, this is disabled in my project.
Changed to:
const mfaUser = multiFactor(user);
mfaUser.getSession().then((session) => {
and:
const phoneAuthProvider = new PhoneAuthProvider(auth);
I don't know if Firebase Auth docs is deprecated or I'm doing something different. XD

Firebase callable function for Twilio token creation with React

I am trying to use a Firebase callable function to create a Twilio token for a React project. The project should allow video calls using Twilio's webRTC service. The code is based on the example here https://www.twilio.com/blog/video-chat-react-hooks. I am attempting to move the server code to obtain the Twilio token for the video service into the Firebase callable function.
My console gives the following output and errors:
error check 1: handleSubmit called
error check 2: getToken function retrieved from functions ƒ (r){return n.call(e,r,t||{})}
error check 4: token set
POST https://us-central1-vid-chat-app.cloudfunctions.net/getToken 500
Uncaught (in promise) Error: INTERNAL
at new m (error.ts:66)
at b (error.ts:175)
at P.<anonymous> (service.ts:244)
at tslib.es6.js:100
at Object.next (tslib.es6.js:82)
at a (tslib.es6.js:71)
Here is the error from the Firebase console
Unhandled error Error: accountSid is required
at new AccessToken (/srv/node_modules/twilio/lib/jwt/AccessToken.js:213:28)
at generateToken (/srv/index.js:9:12)
at videoToken (/srv/index.js:23:19)
at exports.getToken.functions.https.onCall (/srv/index.js:42:19)
at func (/srv/node_modules/firebase-functions/lib/providers/https.js:273:32)
at corsHandler (/srv/node_modules/firebase-functions/lib/providers/https.js:293:44)
at cors (/srv/node_modules/cors/lib/index.js:188:7)
at /srv/node_modules/cors/lib/index.js:224:17
at originCallback (/srv/node_modules/cors/lib/index.js:214:15)
at /srv/node_modules/cors/lib/index.js:219:13
These are the two main files I believe are relevant
React file
import React, { useState, useCallback } from 'react';
import firebase from './firebase'
import Lobby from './Lobby';
import Room from './Room';
const VideoChat = () => {
const [username, setUsername] = useState('');
const [roomName, setRoomName] = useState('');
const [token, setToken] = useState(null);
const handleUsernameChange = useCallback(event => {
setUsername(event.target.value);
}, []);
const handleRoomNameChange = useCallback(event => {
setRoomName(event.target.value);
}, []);
const handleSubmit = useCallback(async event => {
console.log("error check 1: handleSubmit called")
event.preventDefault();
const getToken = firebase.functions().httpsCallable('getToken')
console.log("error check 2: getToken function retrieved from functions", getToken)
getToken({ identity: username, room: roomName })
.then(result => {
console.log("error check 3: calling .then")
setToken(result.data)
})
console.log("error check 4: token set")
}, [username, roomName]);
const handleLogout = useCallback(event => {
setToken(null);
}, []);
let render;
if (token) {
render = (
<Room roomName={roomName} token={token} handleLogout={handleLogout} />
);
} else {
render = (
<Lobby
username={username}
roomName={roomName}
handleUsernameChange={handleUsernameChange}
handleRoomNameChange={handleRoomNameChange}
handleSubmit={handleSubmit}
/>
);
}
return render;
};
export default VideoChat;
Firebase function
const twilio = require("twilio");
const functions = require('firebase-functions');
const config = require('./config');
const AccessToken = twilio.jwt.AccessToken;
const { VideoGrant } = AccessToken;
const generateToken = config => {
return new AccessToken(
config.twilio.accountSid,
config.twilio.apiKey,
config.twilio.apiSecret
);
};
const videoToken = (identity, room, config) => {
let videoGrant;
if (typeof room !== "undefined") {
videoGrant = new VideoGrant({ room });
} else {
videoGrant = new VideoGrant();
}
const token = generateToken(config);
token.addGrant(videoGrant);
token.identity = identity;
return token;
};
const sendTokenResponse = (token, res) => {
res.set('Content-Type', 'application/json');
res.send(
JSON.stringify({
token: token.toJwt()
})
);
};
exports.getToken = functions.https.onCall((data, context)=>{
console.log('Peter error check getToken function called')
const identity = data.identity;
const room = data.room;
const token = videoToken(identity, room, config);
sendTokenResponse(token, res);
console.log('here is the token', token)
return token
});
Config file also contained in Firebase cloud function folder
module.exports = {
twilio: {
accountSid: process.env.TWILIO_ACCOUNT_SID,
apiKey: process.env.TWILIO_API_KEY,
apiSecret: process.env.TWILIO_API_SECRET,
chatService: process.env.TWILIO_CHAT_SERVICE_SID,
outgoingApplicationSid: process.env.TWILIO_TWIML_APP_SID,
incomingAllow: process.env.TWILIO_ALLOW_INCOMING_CALLS === "true"
}
};
my approach to do this is:
1) Environment Configuration: store your environment data.
firebase functions:config:set twilio.sid="The Account SID" twilio.apikey="The api key" twilio.apisecret="The api secret"
2) Firebase Function: use https.onCall
const functions = require('firebase-functions');
const AccessToken = require('twilio').jwt.AccessToken;
const VideoGrant = AccessToken.VideoGrant;
*//call environment data*
const accountSid = functions.config().twilio.accountsid;
const apiKey = functions.config().twilio.apikey;
const apiSecret = functions.config().twilio.secret;
*//function to generate token*
const generateToken = () => {
return new AccessToken(accountSid, apiKey, apiSecret);
};
*//function to generate video grant*
const videoToken = (identity, room) => {
videoGrant = new VideoGrant({ room });
const token = generateToken();
token.addGrant(videoGrant);
token.identity = identity;
return token;
};
*//firebase function*
exports.twilio = functions.https.onCall((data, context) => {
const identity = data.identity;
const room = data.room;
const token = videoToken(identity, room);
return token.toJwt();
}});
3) React code:
const handleSubmit = useCallback( async () => {
try {
const twilioRequest = firebase
.functions()
.httpsCallable('twilio');
const response = await twilioRequest({
identity: username,
room: roomName,
});
setToken(response.data);
} catch (err) {
console.log(err);
}
},[username, roomName]);
4) Allowing unauthenticated HTTP function invocation
Be sure you have permissions granted https://cloud.google.com/functions/docs/securing/managing-access-iam#allowing_unauthenticated_function_invocation

DisplayName not being set when using Firebase

I am trying to get Firebase to assign users a name based off of what they put into a field. However it appears that the name isnt being updated doesnt do anything.
btnSignUp.addEventListener('click', e => {
//Get Email and Password
const acEmail = email.value;
const acPass = password.value;
const acName = name.value;
const auth = firebase.auth();
//Sign Up
const promise = auth.createUserWithEmailAndPassword(acEmail, acPass);
promise.catch(e => console.log(e.message));
then(function(user) {
user.updateProfile({
displayName: acName
})
}).catch(function(error) {
console.log(error);
});
});
Any help is Appreciated!

Categories

Resources