Create user data for new Firebase auth accounts in Firestore - html

I would like to create user data (name, email, phone number) in Firestore. This should be triggered with on create an authenticated user.
at functions-> src-> index.ts
// Sends email to user after signup
export { welcomeEmail } from './send_email';
// Saves user after signup
export { createUserDoc } from './save_user';
at functions-> src-> save_user.ts
// Firebase Config
import * as functions from "firebase-functions";
import * as firebase from "firebase-admin";
import {MD5} from "crypto-js";
export const createUserDoc = functions.auth.user().onCreate(event => {
const firebaseUser = event.data;
// Use gravatar as default if photoUrl isn't specified in user data
let fileEnding = "jpg";
let photoURL = `https://www.gravatar.com/avatar/${MD5(firebaseUser.email).toString().toLowerCase()}.jpg?s=1024&d=robohash`;
if (firebaseUser.photoURL) {
fileEnding = firebaseUser.photoURL.substr(firebaseUser.photoURL.lastIndexOf(".") + 1);
photoURL = firebaseUser.photoURL;
}
const fileName = `users/${firebaseUser.uid}/profile.${fileEnding}`;
const profilePhotoStorageOpts = {
destination: fileName,
metadata: {
contentType: `image/${fileEnding}`
}
};
const user = {
name: firebaseUser.displayName || "No Name",
email: firebaseUser.email,
photoUrl: `gs://${firebase.storage().bucket().name}/${fileName}`
};
return Promise.all([
firebase.storage().bucket().upload(photoURL, profilePhotoStorageOpts),
firebase.firestore().collection("users").doc(firebaseUser.uid).set(user)
]);
});
The goal was, for each created account I would now find a corresponding user document in Firestore and a profile image in the cloud storage.
instead I'm getting:
Property 'data' does not exist on type 'UserRecord'.ts(2339)
'Promise' only refers to a type, but is being used as a value here. Do you need to change your target library? Try changing the lib compiler option to es2015 or later.ts(2585)
Help would be appreciated. Thanks

As you will see in the documentation for the onCreate method, the first parameter of the handler function is a UserRecord which does not have a data property.
So the first error you get is normal.
In your case, if you want, for example, to get the user's photoURL, you should do event.photoURL (Since event is of type UserRecord). Similarly, you will do event.uid to get the user's uid.
For the second error, you may have a look at https://stackoverflow.com/a/43122423/3371862 or How to resolve 'Build:'Promise' only refers to a type, but is being used as a value here.'

Related

Firestore data() method does not exist on JSON parsed document after JSON stringified

I am building a FlashCard website using NextJs and firebase. I have a homepage which I want to render server side and so I am using getServerSideProps. InsidegetServerSideProps function, I am fetching all the documents of the current user from firestore and is stored in an array and is returned as props as below:
export const getServerSideProps = async(ctx: GetServerSidePropsContext) {
let notes: DocumentData[];
// fetch documents here and populate notes array like so [doc,doc,doc,..]
// data() method works here and returns document fields
console.log(notes[0].data());
// NextJs throws error "`object` ("[object Object]") cannot be serialized as JSON. Please only return JSON serializable data types.", so I have to JSON.stringify() the notes
return {
props: {
notes: JSON.stringify(notes),
}
}
}
Below, I have my homepage where I parse the JSON string and have access to the notes, but now the data() method on the document does not exist/works and throws method does not exist errors. If I have to access the document fields, I have to use the dot operator on every property of the document till I reach the fields property which is nested deep down in the object as follows:
export default function Home({ notes }) {
let docs = JSON.parse(notes); // can access notes
// data() method throws function does not exist error
console.log(docs[0].data());
// I am only able to access document fields as below
console.log(docs[0]._document.data.value.mapValue.fields);
return (
<Layout>
<HomeContent notes={docs}/>
</Layout>
);
}
I have searched everywhere and found nothing that helped me why data() method won't work. If I directly fetch the documents inside the page component on client side, the data() method on the document returns its' fields. I don't know how using JSON serializations affect it. I would always prefer to use data() method to access fields than to go that deep plus I am planning to fetch data on server on other pages as well.
I would really appreciate if you can shed some light on it. It took all of my days time.
EDIT: The code that gets notes from firestore:
// inside getServerSideProps
let notes: DocumentData[] = null;
const getNotes = async(ref: DocumentReference < DocumentData > , uid: string) => {
let tempNotes = [];
const categoriesSnapshot = await getDoc < DocumentData > (ref);
const categoriesObject = categoriesSnapshot.data();
// return if user doesn't have any documents
if (!categoriesObject) {
return;
}
const promises: [] = categoriesObject.categories.map((category: string) => {
const userCategoriesRef = collection(database, 'CategoryCollection', uid, category);
return getDocs(userCategoriesRef); // returns a promise
});
const allQuerySnapshots = await Promise.all < DocumentData > (promises);
allQuerySnapshots.forEach((querySnapshot) => {
tempNotes.push(...querySnapshot.docs);
});
notes = tempNotes;
}
const categoryDocRef = doc(database, "CategoryCollection", uid);
await getNotes(categoryDocRef, uid);

google-apps-script ContactsApp.getContact(email) migration to People API

How to write this simple line :
ContactsApp.getContact(email);
with the new People API ?
Thanks a lot !
Workflow:
Use people.connections.list to get the list of contacts of the requested user, specified via resourceName (people/me refers to the authenticated user).
Find the contact who has an email address like the one you are looking for.
Important notes:
Naturally, you won't get exactly the same information as in ContactsApp.getContact, since they are different API's. In this case, you'll get an instance of the Person resource.
You can choose which information should be populated on the retrieved person, using personFields (see the list of available fields here). Request multiple fields by providing a comma-separated string of fields (in the example below, emailAddresses and biographies are requested).
Code sample:
function getPerson(email) {
const resourceName = "people/me";
const optionalArgs = {
personFields: 'emailAddresses,biographies' // Add the person fields you want
}
const response = People.People.Connections.list(resourceName, optionalArgs);
const { connections } = response;
const person = connections.find(connection => {
return connection["emailAddresses"] && connection["emailAddresses"].some(emailAddress => emailAddress["value"] === email);
});
return person;
}

Firebase Updating User Data With Custom Fields After Creating User

I want to update the newly created User's data. The returned JSON is:
{
"user":{
"uid":"test123",
"displayName":null,
"photoURL":null,
"email":"test12#test.com",
"emailVerified":false,
"phoneNumber":null,
"isAnonymous":false,
"tenantId":null,
"providerData":[
{
"uid":"test12#test.com",
"displayName":null,
"photoURL":null,
"email":"test12#test.com",
"phoneNumber":null,
"providerId":"password"
}
],
"apiKey":"test123",
"appName":"[DEFAULT]",
"authDomain":"test123.firebaseapp.com",
"stsTokenManager":{
"apiKey":"test123",
"refreshToken":"test123",
"accessToken":"test123",
"expirationTime":1571238989357
},
"redirectEventId":null,
"lastLoginAt":"1571235389108",
"createdAt":"1571235389108"
},
"credential":null,
"additionalUserInfo":{
"providerId":"password",
"isNewUser":true
},
"operationType":"signIn"
}
This is my callout and update:
createUser = async (userData) => {
return await firebase.auth().createUserWithEmailAndPassword(userData.get('userName'), userData.get('password'))
.then((authData) => {
firebase.database().ref('users/' + authData.user.uid + '/').set({
fullName: userData.get('fullName'),
pictures: userData.get('pictures'),
phoneNumber: userData.get('phoneNumber')
});
})
};
Is it possible to add to the User table custom fields?
A few things are happening. It appears that userData can not be seen in the .then statement. So to solve this I attempted to pass in the userData JSON as a param. This did not work. I then broke out each value out of userData, saved it into a const and passed that value. This did not work.
I can see that userData has values in it before the .then statement. I am able to successfully create a new user with the right userName and password. This means to me either:
A - I am not passing the userData JSON correctly or
B - I am not allowed to pass data to firebase like I am doing
My end goal is to sign up a user and then take all of the data they input from a registration form (aka userData) and update the user table with it.
Articles I am using are:
https://firebase.google.com/docs/auth/web/manage-users
https://medium.com/mindorks/firebase-realtime-database-with-react-native-5f357c6ee13b
Main class that calls the createUser function:
const signUp = (dispatch) => {
return async (userData)=>{
try{
const response = await config.createUser(userData);
console.log('sign up resonse1: ' + response); //coming back as undefined
//todo:: figure out how to parse out the apikey out of response
await AsyncStorage.setItem('token', '123mockToken');
dispatch({type: 'sign_up', payload: '123mockToken'});
navigate('mainFlow');
} catch(e){
dispatch({type: 'add_error', payload: '' + e}); //we call dispatch anytime we want to update our state
}
}
};
I understand that the parameter userData holds all the data you want to use for creating the user ("all of the data they input from a registration form").
The following should work:
createUser = async userData => {
try {
const userCredential = await firebase
.auth()
.createUserWithEmailAndPassword(
userData.get('userName'),
userData.get('password')
);
const userId = userCredential.user.uid;
await firebase
.database()
.ref('users/' + userId + '/')
.set({
fullName: userData.get('fullName'),
pictures: userData.get('pictures'),
phoneNumber: userData.get('phoneNumber')
});
return userId; //As per your comment below
} catch (error) {
return error;
}
};
The createUserWithEmailAndPassword() method returns a UserCredential which contains a User.

How can I set the `sub` claim of a JWT in FeathersJS?

The sub claim for JWTs is optional, but the feathersjs-authentication won't let me set it to a blank string or remove it.
I was able to add a new value to the payload in the authentication before hook but changing sub or trying to remove it doesn't work.
app.service('/api/auth').hooks({
before: {
create: [
// You can chain multiple strategies
auth.hooks.authenticate(['jwt', 'local']),
hook => {
// I can add a new `SUB` value but this method doesn't work for `sub`
Object.assign(hook.params.payload, {SUB: hook.params.payload.userId})
}
],
...
I tried adding the same change to the after hook, but that didn't work either. Having the sub value as anonymous doesn't seem right to me. Their docs even say:
subject: 'anonymous', // Typically the entity id associated with the JWT
Yet there does not seem to be a straight-forward way to make the sub JWT claim a dynamic value.
The subject or sub is set in the authentication options and - like any other JWT specific option - can not be set through the payload.
Looking at the code you can see that valid JWT option keys can be set through params (which other than params.query is outside of a malicious client reach so it can't be easily tampered with):
app.service('/api/auth').hooks({
before: {
create: [
// You can chain multiple strategies
auth.hooks.authenticate(['jwt', 'local']),
hook => {
hook.params.jwt.subject = hook.params.payload.userId;
}
],
You can define your own authenticationStrategy by extending authenticationService and using getTokenOptions() to overwrite the payload sub for that you need to work on also how will you verify it.
Scenario : I wanted to encrypt my sub for that I created a new AuthClass extending AuthService and overriding
async getTokenOptions(
authResult: AuthenticationResult,
params: Params
): Promise<any> {
const secret = this.app.get('authentication').secret;
const { user } = authResult;
const { id } = user;
const payload = await super.getTokenOptions(authResult, params);
if (user && user.id) {
payload.subject = encrypt(id, secret);
}
return payload;
}
and also I need to decrypt at the time of adding it params.user, so I created another jwtDecryptStrategy extending given JWTStrategy and override its getEntityId() to return the decrypted id
Like this:
export class JwtDecryptStrategy extends JWTStrategy {
async getEntityId(
authResult: AuthenticationResult,
_params: Params
): Promise<any> {
return decrypt(
authResult.authentication.payload.sub,
this.app?.get('authentication').secret
);
}
}

What type of data files should be used with Protractor

Is there a recommended way to handle data from data file in protractor scripts?
If I want to keep all the test data (like login details, user input values) in separate data file then what type of file should I use and how should I import them to my protractor scripts?
If suppose you need to work with json then:
Suppose your json for username and password of a login page looks like:
Example of JSON:
[
{
"username": "kishan",
"password": "patel"
}
]
Then you can simply import this to your code and access it as below.
describe ('Login Page Data Driven' , function() {
browser.ignoreSynchronization = true;
beforeEach(function(){
browser.get('your url');
browser.driver.manage().window().maximize();
});
it('To verify Login, using Data Driven Technique from Json file', function()
{
var testData = require('D:/json path'); //this is the path where your json is stored
var user= element(by.id("username"));
var password = element(by.id("password"));
user.sendKeys(testData[0].username);
password.sendKeys(testData[0].password);
});
This Is just an Example. I hope you can Relate and apply.
Try at your end & Let me know for more concerns.
I typically create a separate data file, and require it as needed in my specs. I have a working example on my github protractor-examples repo. Here's the jist:
// userData.js
var UserData = {
testUser : {'username': 'test', 'password': 'test'},
};
module.exports = UserData;
then in my spec...
// nonAngularLoginSpec.js
it('should goto friend pages on successful login', function() {
loginPage.loginAs(userData.testUser);
expect(friendPage.at()).toBeTruthy();
});