NodeJS can new users can signup while couldnt login - node.js

I am a linux server administrator so I don't have nodejs programming experience.
The problem is that I installed nodejs for a specific laravel script for a mobile chat.
Now that I've configured everything and nodejs is running and web socket is also opened the script works but when new users signup they do it successfully while when trying to login an error (cat error) comes up.
I have verified that new signed up users added to db and that web socket is opened but I don't know why when users try to login it throws an error.
As server administrator I have checked all log files but I get nothing related to the problem.
Also enabled mysql query log and found that a sql query is already sent to mysql when trying to sign in with new created account
But I don't know why users can't sign in, that's why I suspect the backend server is not working.
Here are the lines of codes in "server.js" that is related to the problem
this.handleDisconnection(), this.store.revealNames.register(this.user).catch(e => {}), this.handler(o.enc({
publicMessageId: s,
error: !1,
grantedSettings: this.user.grantedSettings(),
posts: this.store.wall.all(),
rooms: this.store.rooms.all().map(e => e.parse(this.user, this.store.users)),
users: this.store.users.filter(e => e.canBeSeenBy(this.user)).map(e => e.parse()),
gifts: this.store.gifts.all(),
faces: this.store.faces.all(),
user: this.user.parse()
})), this.store.radio.startListening(this.user)
}).catch(e => {
this.user.isLogged = !1;
const t = {
error: !0,
message: "حدث خطأ أثناء تسجيل الدخول, الرجاء إعادة المحاولة"
};
try {
if (e.response.data.name) return t.message = e.response.data.name, this.handler(o.enc(t));
if (e.response.data.is_friendly_message && e.response.data.message) return t.message = e.response.data.message, this.handler(o.enc(t))
} catch (e) {
return this.user.isLogged = !1, this.handler(o.enc(t))
}
t.data.user.adminToken && this.store.adminTokens.push(t.data.user.adminToken), this.handleDisconnection(), this.user.isVirtual ? (this.store.virtualUserIds.push(this.user.reg_id), this.handler(t.data.user.defaultRoom)) : (this.store.revealNames.register(this.user).catch(e => {}), this.handler(o.enc({
publicMessageId: r,
error: !1,
cookie: e,
grantedSettings: this.user.grantedSettings(),
posts: this.store.wall.all(),
rooms: this.store.rooms.all().map(e => e.parse(this.user, this.store.users)),
users: this.store.users.filter(e => e.canBeSeenBy(this.user)).map(e => e.parse()),
gifts: this.store.gifts.all(),
faces: this.store.faces.all(),
user: this.user.parse()
})), this.store.radio.startListening(this.user))
}).catch(e => {
this.user.isLogged = !1;
const t = {
error: !0,
message: "حدث خطأ أثناء تسجيل الدخول, الرجاء إعادة المحاولة"
};
try {
if (e.response.data.name) return t.message = e.response.data.name, this.handler(o.enc(t));
if (e.response.data.password) return t.message = e.response.data.password, this.handler(o.enc(t));
if (e.response.data.is_friendly_message && e.response.data.message) return t.message = e.response.data.message, this.handler(o.enc(t))
} catch (e) {
return this.user.isLogged = !1, this.handler(o.enc(t))
}
})
I get the following error
حدث خطأ أثناء تسجيل الدخول, الرجاء إعادة المحاولة
In the above code there are two blocks, I get the error when trying to log in with the registered user.
why new signed up users cannot login?

Related

Identity Platform / Firebase Error (auth/invalid-refresh-token)

I am in the process of upgrading an existing working Firebase Auth project to Identity Platform to benefit from the goodness of tenants.
I am currently testing this against the local emulator and am facing the following issues:
My users no longer show up in the emulator. I reckon, however, that
the behaviour is expected since I am creating users against a tenant
and no longer the default project users "pool"
The users do not show
up in the GCP console either. Yet, the getUserByEmail() method
in a Cloud Function returns the registered users. I therefore have no clue where these users are currently created...
Authenticating users via generateSignInWithEmailLink() works fine.
However, a few steps in the funnel after this, when using the await user?.getIdToken(true) method, I am getting the following error: Uncaught (in promise) FirebaseError: Firebase: Error (auth/invalid-refresh-token) and can't figure out why.
Interestingly, the user.getIdTokenResult() method works fine and does not yield any error.
My entire snippet:
const getCurrentUser = async (): Promise<Auth["currentUser"]> => {
return new Promise((resolve, reject) => {
const unsubscribe = onAuthStateChanged(
auth,
async (user) => {
if (user) {
if (document.referrer.includes("stripe")) {
// console.log({ user });
await user?.getIdToken(true);
console.log({ after: user });
}
state.isAuthenticated.value = true;
state.user.value = user;
try {
const { claims } = await user.getIdTokenResult();
state.claims.value = claims;
if (typeof claims.roles === "string") {
if (claims.active && claims.roles.includes("account_owner")) {
state.isActive.value = true;
}
}
} catch (e) {
console.log(e);
if (e instanceof Error) {
throw new Error();
}
}
}
unsubscribe();
resolve(user);
},
(e) => {
if (e instanceof Error) {
state.error.value = e.message;
logClientError(e as Error);
}
reject(e);
}
);
});
};
For reference, I am working with a Vue 3 / Vite repo.
Any suggestion would be welcome,
Thanks,
S.
Just a quick follow-up here for anyone looking for an answer to this.
I raised a bug report on the firebase-tools Github and:
Users not appearing in the Emulator UI: behaviour confirmed by the firebase team. The emulator does not not support multi-tenancy at the moment. In my experience, however, working with the emulator with multi-tenants, the basic functionalities seem to work: creating users, retrieving them. Impossible however to visualise them or export them.
Refresh token error: bug confirmed by the firebase team and in the process of being triaged. Will likely take some time before being fixed (if ever?). So for now, even if far from being ideal, I have added conditional checks to my code to skip the force refresh of the token on localhost. Instead, I log out and log back in with the users I am expecting to see some changes in the claims for, as this process does not error. Another solution would be to use an actual Firebase Auth instance and not the local emulator, but it feels a bit messy to combine localhost/emulator resources and actual ones, even with a dev account.
The GH issue: here.

is it okay if I intentionally make my Google Cloud Functions has multiple errors?

I have collection and sub-collection like this
users/{userID}/followers/{followerID}
everytime a follower document is deleted in followers sub-collection, then it will trigger this firestore trigger below to decrease the numberOfFollowers field in user document. this is triggered when a user click unfollow button
exports.onDeleteFollower = functions
.firestore.document("users/{userID}/followers/{followerID}")
.onDelete((snapshot, context) => {
// normally triggered after a user push unfollow button
// then update the user document
const userID = context.params.userID;
const updatedData = {
numberOfFollowers: admin.firestore.FieldValue.increment(-1),
};
return db.doc(`users/${userID}`).update(updatedData);
});
now I have a case like this ....
if a user deletes their account, then I will delete the user document ( users/{userID} ), but if I delete a user document, it will not automatically delete all documents inside its sub-collection, right
so after I delete the user document, I have another function to delete all documents inside the followers sub-collection.
but the problem is, the onDeleteFollower triggers function above will be executed multiple times, and it will throw error multiple times, because the user document has been deleted ( the function above will be used to a update a field in deleted user doc)
I will have this error in functions emulator
⚠ functions: Error: 5 NOT_FOUND: no entity to update: app: "myApp"
path <
Element {
type: "users"
name: "u1-HuWQ5hoCQnOAwh0zRQM0nOe96K03"
}
>
⚠ Your function was killed because it raised an unhandled error.
I actually can write a logic to check if a user document still exist or not. if exist then update numberOfFollowers field
but deleting a user document is very rare if compared to a user click the unfollow button, I think it is not very efficient.
I have a plan like this, I will intentionally let the errors happened. say a user has 1000 followers, then it will trigger the onDeleteFollower function above, then I will have 1000 function errors
my question is .....
is it okay if I have multiple errors in a short time like that? will Google Cloud Function terminates my function, or .... I don't know, I am worried something bad will happen that I don't know
as far as I know, cloud functions will automatically run the function again after it is killed, will my function always ready again after an error like that?
I can't let the follower update the organizer (user) document directly from the client app, because it is not safe. creating security rules to facilitate this is complicated and it seems error prone
Have you considered instead of setting/removing users/{userID}/followers/{followerID} directly, that you create a "follow request" system?
"users/{userID}/followRequests/{requestID}": { // requestID would be auto-generated
user: "{followerID}",
follow: true // true = add user as follower, false = remove user as follower
}
This then allows you to use a single onCreate trigger to update your followers list eliminating the need for your current onCreate and onDelete triggers on users/{userID}/followers/{followerID}. From this function you can implement restrictions on following other users like follow limits or denying follow requests for blocked users.
export const newFollowRequest = functions.firestore
.document('users/{userId}/followRequests/{requestId}')
.onCreate(async (snap, context) => {
const request = snap.data();
const followingUserId = request.user;
const followedUserId = context.params.userId;
const db = admin.firestore();
const userDocRef = db.doc(`users/${followedUserId}`);
const followerDocRef = userDocRef.child(`followers/${followingUserId}`);
// /users/${followingUserId}/following/${followedUserId} ?
try {
if (request.follow) {
// Example restriction: Is the user who is attempting to follow
// blocked by followedUserId?
// await assertUserIDHasNotBlockedUserID(followedUserId, followingUserId);
// following
db.update(userDocRef, {
numberOfFollowers: admin.firestore.FieldValue.increment(1),
});
db.set(followerDocRef, {
/* ... */
});
} else {
// unfollowing
db.update(userDocRef, {
numberOfFollowers: admin.firestore.FieldValue.increment(-1),
});
db.delete(followerDocRef);
}
// delete this request when successful
db.delete(snap.ref);
// commit database changes
await db.commit();
console.log(`#${followingUserId} ${request.follow ? "followed" : "unfollowed"} #${followedUserId} successfully`);
} catch (err) {
// something went wrong, update this document with a failure reason (to show on the client);
let failureReason = undefined;
switch (err.message) {
case "other user is blocked":
failureReason = "You are blocked by #otherUser";
break;
case "user is blocked":
failureReason = "You have blocked #otherUser";
break;
}
return db.ref(snap.ref)
.update({
failureReason: failureReason || "Unknown server error";
})
.then(() => {
if (failureReason) {
console.log("REQUEST REJECTED: " + failureReason);
} else {
console.error("UNEXPECTED ERROR:", err)
}
},
(err) => {
console.error("UNEXPECTED FIRESTORE ERROR:", err);
});
}
});

Twilio Programmable Chat getChannelByUniqueName throws "Forbidden" Error

After creating a channel in my backend service, I am trying to join the channel in my react frontend in an useEffect hook like this:
const initChat = async () => {
const chatClient = await Client.create(props.token);
const channel = await chatClient.getChannelByUniqueName(props.room);
if (channel) {
if (channel.status !== "joined") {
chatClient.on("channelJoined", () => {
registerChannelListeners(channel);
});
await channel.join();
} else {
registerChannelListeners(channel);
}
setChannel(channel);
setMessages((await channel?.getMessages()).items);
}
};
}
When initially navigating to the page, the error
upstream.js?a850:136 Uncaught (in promise) Error: Forbidden
at Upstream.actualSend (upstream.js?a850:136)
gets thrown sporadically.
On reloading the page, everything works fine.
The problematic line seems to be:
const channel = await chatClient.getChannelByUniqueName(props.room);
As no further code gets executed. token and room are both assigned with valid values.
In the decoded socket messages, this error message is sent from twilio:
{"method":"reply"...,"http_status":{"code":403,"status":"Forbidden"}}
{"status":403,"message":"User not member of channel","code":50400}
although both participants are invited via the backend with this function:
inviteParticipantToChannel(participant: Participant, channelSid: string) {
this.logger.log(
`Inviting paricipant ${participant.getIdentifier()} to channel ${channelSid}`,
);
return this.twilioClient.chat
.services(process.env.TWILIO_CHAT_SERVICE_ID)
.channels(channelSid)
.invites.create({ identity: participant.getIdentifier() });
}
Is there more that I need to do to enable the participant to find/join the channel?
Have you tried setting a short timeout? The channel resource at twilio might need some time.

Firebase auth email - prevent fake/invalid accounts

I am using Firebase auth email accounts to sign up users to a site.
What I have noticed lately is the below cases.
Users sign up using a valid email address and then never verify the
email address
Users attempt to sign up using a fake email address
For the first case we can search all accounts that have not been verified within a time span and delete them.
admin.auth().getUser(uid).then(user => {
const creationTime = user.metadata.creationTime
const isVerified = user.emailVerified
const lastSignInTime = user.lastSignInTime
if(!isVerified){
// Process and delete unverified accounts after x days
...
}
})
How can we handle accounts where the email address is fake or misspelled? I am not seeing any property on the firebase.User object that indicates an invalid email address. We do however receive a mail delivery failure message for each user that has signed up using a invalid email address - this is not enough to automatically purge fake / invalid accounts.
What are best practices on preventing fake signups?
Kind regards /K
You can't stop someone from using any string that looks like an email address, and the system doesn't have a way of telling you that the verification email was successfully sent.
The usual way to deal with this is to create some database record for each user account that tracks their validation status. You can query the database to find out which users have not validated after some amount of time. Your app should be sending your backend ID tokens from the user that can be used to check if they are validated, and if so, update the database to show that it happened.
So this is the code I came up with to purge unverified accounts.
May not be the most elegant solution, but works.
exports.scheduledUserCleanup = functions
.region('europe-west1')
.pubsub
.schedule('0 3 * * *')
.timeZone('Europe/Stockholm')
.onRun(async (event) => {
const today = moment()
const inactivityThresholdDays = 7 //Get purge threshold days
let myPromises = [] //Collect all promises to carry out
//Search for users that have NOT validated email
database.ref('user-signups').once('value', (usersSnapshots) => {
usersSnapshots.forEach((snapshot) => {
const uid = snapshot.key
// Get user from firebase auth
admin.auth().getUser(uid)
.then((firebaseAuthUser) => {
const creationTimeStr = firebaseAuthUser.metadata.creationTime
const isVerified = firebaseAuthUser.emailVerified
const lastSignInTimeStr = firebaseAuthUser.metadata.lastSignInTime
const neverSignedIn = (creationTimeStr === lastSignInTimeStr) ? true : false
if(!isVerified && neverSignedIn){
// Process and delete unverified accounts after 7 days
const creationTime = moment(creationTimeStr)
const daysSinceCreation = today.diff(creationTime, 'days')
if(daysSinceCreation > inactivityThresholdDays){
console.log('Remove user from db and Firebase auth', uid)
myPromises.push( admin.auth().deleteUser(firebaseAuthUser.uid) )
myPromises.push( database.ref(`user-signups/${uid}`).remove() )
}else{
console.log(`Keep for ${inactivityThresholdDays} days before deleting`, uid)
}
}
return true
})
.catch((error) => {
// Remove if not found in Firebase Auth
const notFoundInFirebaseAuth = (error.code) ? error.code === 'auth/user-not-found' : false
if(notFoundInFirebaseAuth){
console.log('Remove user from db', uid)
myPromises.push( database.ref(`user-signups/${uid}`).remove() )
}
return false
})
})
})
// Execute promises
return Promise.all(myPromises)
.then(() => Promise.resolve(true))
.catch((err) => {
console.error('Error', err)
return Promise.reject(err)
})
})

Firebase Re-authentication & email Linking for Phone-Authenticated Users

When users sign up, they use Phone Auth. After using the app for a while, they are advised to link an (email & password) to their existing account.
The linking process fails because of the error (auth/requires-recent-login.) My code follows.
// The following generates the error: [auth/requires-recent-login] This operation is sensitive and requires recent authentication. Log in again before retrying this request.
const emailCredential = firebase.auth.EmailAuthProvider.credential(state.email, state.password);
const newCredential = await firebase.auth().currentUser.linkWithCredential(emailCredential);
To fix this error, I understand that I need to call reauthenticateWithCredential() before linking. However, I don't want to ask the user to log in again (receive & enter a verification code.) Is this at all possible?
I tried passing the result of currentUser.getIdToken(true) to PhoneAuthProvider.credential() I am not sure if this is right. Anyway, it generated an error (Cannot create PhoneAuthCredntial without either verificationProof, sessionInfo, temporary proof, or enrollment ID.).
My code follows.
// The following works:
const accessToken = await firebase.auth().currentUser.getIdToken(true);
// The following works:
const currentCredential = firebase.auth.PhoneAuthProvider.credential(accessToken);
// The following generates the error: Cannot create PhoneAuthCredential without either verificationProof, sessionInfo, temporary proof, or enrollment ID.
const abc = await firebase.auth().currentUser.reauthenticateWithCredential(currentCredential);
// The following is never reached:
const emailCredential = firebase.auth.EmailAuthProvider.credential(state.email, state.password);
const newCredential = await firebase.auth().currentUser.linkWithCredential(emailCredential);
Thank you for your effort and time to help me...
Important Information:
firebase.auth().currentUser.reauthenticateWithCredential(credential) requires the attribute credential
For users, who logged in using a Phone Number, I could not find a way to get this credential when required. By the way, it is possible to get it for users, who logged in using other providers, e.g. Facebook.
However, for users, who log in using a phone number, it is possible to get this credential during the login process. Check https://firebase.google.com/docs/auth/web/phone-auth.
So, I decided to save the credential for the user on their device during the login process. I am using Redux and Redux-Persist for that.
My code after fixing it.
// This is an extract from the login script:
firebase.auth().signInWithPhoneNumber(phoneNo)
.then(confirmResult => {
dispatch({ type: "PhoneNo_accepted", payload: { confirmResult: confirmResult } });
})
.catch(error => {
dispatch({ type: "display_message", payload: { messageText: `Phone Number Error: ${error.message}` } });
});
// Change#1. The following statement is a NEW step, which I added to get the credential during the login process.
const credential = firebase.auth.PhoneAuthProvider.credential(state.confirmResult.verificationId, state.codeInput);
state.confirmResult.confirm(state.codeInput)
.then( (user) => {
// Change#2. The following function would save the credential to the app's state, e.g. using Redux
_onAuthComplete(user, credential);
})
.catch( error => {
dispatch({ type: "display_message", payload: { messageText: `Verification Code Error: ${error.message}` } });
});
// // //
// This is an extract from the linking script:
// Change#3. props.credential is the credential, which was saved to the app's state.
await firebase.auth().currentUser.reauthenticateWithCredential(props.credential);
const emailCredential = firebase.auth.EmailAuthProvider.credential(state.email, state.password);
const newCredential = await firebase.auth().currentUser.linkWithCredential(emailCredential);

Resources