Email validation with zod - reactjs

I have an email input and i want to validate that the user entered a specific email "abcd#fg.com" and if not to show specific error message "This email is not in our database". I am using zod validation to do that, but how can it be done?
const LoginSchema = z.object({
email: z
.string()
.min(1, { message: "This field has to be filled." })
.email("This is not a valid email.")
})
});

I can show how to do this, but I don't think this should be done (more later).
You can use refine to check if the string is exactly some expected value. For example:
const LoginSchema = z.object({
email: z
.string()
.min(1, { message: "This field has to be filled." })
.email("This is not a valid email.")
.refine((e) => e === "abcd#fg.com", "This email is not in our database")
});
Then, later if you were going to pull down emails so you can write a validation on the frontend you would use an async refinement with parseAsync like:
const login2 = z.object({
email: z
.string()
.min(1, { message: "This field has to be filled." })
.email("This is not a valid email.")
.refine(async (e) => {
const emails = await fetchEmails();
return emails.includes(e);
}, "This email is not in our database")
});
Opinion Warning
I would not recommend doing this for 2 reasons:
The number of emails is likely to be very large if you have any meaningful number of users. Pulling all of those down just to make this check would be a pretty big waste of resources and time.
Security wise, sharing emails of all your users publicly over the API strikes me as a dangerous thing to do. Anyone would hit that API to get real email addresses for all of your users.
I would recommend not validating this as part of the data validation. This validation should happen on the backend and return a 4XX error response when logging in.
Edit
A comment on this post mentioned that you could instead provide an API to validate an email address. This could be safely used from an async refinement and avoids the issues described above.
That would look like:
const login2 = z.object({
email: z
.string()
.min(1, { message: "This field has to be filled." })
.email("This is not a valid email.")
.refine(async (e) => {
// Where checkIfEmailIsValid makes a request to the backend
// to see if the email is valid.
return await checkIfEmailIsValid(e);
}, "This email is not in our database")
});

Related

how secure is context in react?

How secure is the context in react, I want to created a user from front end and set his indicial status to demo . later on when the user make a payment his status will change , bottom line is how secure this logic is could someone come and modify the initial set status to 'premium' ? so he could skip the payment part ?
const Signup = async (email: string, password: string, userType: string) => {
await createUserWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
setDoc(doc(db, 'Data', userCredential.user.uid), {
user: userType,
verifiedEmail: false,
status: 'demo',
createdAt: Timestamp.now(),
});
As a rule of thumb and especially for single page applications which are rendered on the client side: Data from and (with)in clients can't and shouldn't be trusted as a security measurement.
So consider the context as "not secure" and always rely on proper permission management on the server side - so the worst thing which can happen is that your client may see the "premium" menus, but can't get anything out of it since the server denies any action.

Extracting emails from a string containing multiple emails?

I have created a component which has email, subject and message input fields. My objective is to add and send email to multiple recipients which is separated by a comma. But it's not working.
In Console, I could see that it is taking multiple recipient into one string like this
"abc#yahoo.com, ert#gmail.com, werty#yahoo.com"
But i want the string to be formatted this way
"abc#yahoo.com", "ert#gmail.com", "werty#yahoo.com"
for example: i want to send an email to 3 recipients:
abc#yahoo.com, ert#gmail.com, werty#yahoo.com
Anyone can help me in this please. Thanks in advance
The problem you intend to solve is that you have a field for email, subject and message which the user enters data to send the email with subject subject and message message to all the emails provided in the field email.
There are a number of ways to solve this problem depending on how you format your user input for the email field. The simplest way would be to accept emails(s) separated by a space. E.g. if user needs to send the email to the 3 emails abc#abc.com, bcd#bcd.com and cde#cde.com then you accept them all in one string where each email is separated by a space.
"abc#abc.com bcd#bcd.com cde#cde.com"
Now you can extract all the emails in your email string by simply calling email.split(" ")
const email = "abc#abc.com bcd#bcd.com cde#cde.com";
const emails = email.split(" ");
console.log(emails);
// ["abc#abc.com","bcd#bcd.com","cde#cde.com"]
So your modified handleSubmit method would be something like this
handleSubmit = e => {
e.preventDefault();
const { email, subject, message } = this.state;
const email_list = email.split(" ");
const data = {
email: email_list,
subject,
message
};
axios
.post(`/api/email`, data, {
headers: { "Content-Type": "application/json" }
})
.then(res => {
console.log(res);
console.log(res.data);
})
.catch(err => {
console.log(err);
});
};
You also need to change your changeHandle method like this
changeHandle = e => this.setState({[e.target.name]: e.target.value });
This is the simplest way to accept multiple emails. You can devise any method you feel works for your app. The method described above is probably the most straightforward way I could think of. Feel free to drop a comment if you need further clarifications

AWS Amplify phone number input validation - most practical method?

import Auth from '#aws-amplify/auth';
await Auth.signUp({
username,
password,
attributes: {email, phone_number: phoneNumber}
})
assume 'phoneNumber' is a user-dependent input.
many (most) inputs will cause failure.
What is the most practical method for validation of 'phoneNumber' prior to Auth.signUp ?
Additionally, why does failure manifest as such in React Native?:

yupError.inner is undefined while integrating schema with Formik

I tried integrating the Yup's Schema validation with formik. But receiving error as yupError.inner is undefined
Here's a link to codesandbox!
I have'nt tried much. But found this bug report. which was later realized to be resolved. But still i'm recieving the same. Link to issue #1486!.
// VALIDATION SCHEMA
const formSchema = Yup.object().shape({
emailId: Yup.string("Enter a valid string")
.email("Please enter a valid Email ID")
.required("Need your Email ID, we won't spam you!"),
confirmMail: Yup.string("Enter a valid string")
.matches(Yup.ref("emailId"), "Email ID's are not matching")
.required("Please enter a valid mailid"),
mobileNo: Yup.number("Please enter number")
.max(10, "You've entered more than 10 numbers")
.min(10, "You've entered less than 10 numbers")
.required("Password is required"),
password: Yup.string("Enter a valid password").required(
"Password field is required"
),
confirmPassword: Yup.string("Enter a valid password").required(
"Password fields are not matching"
)
});
//Integration of Validation
<Formik
validate
initialValues={this.initialValues}
validationSchema={this.formSchema}
onSubmit={this.handleSubmit}
>
{props => this.renderForm(props)}
</Formik>
Recieving the error yupError.inner is undefined
Bumpup yup to latest and use mixed().test() instead of string().test()
example :
passwordConfirm: Yup.mixed().test('is-same', 'Passwords not match.', value => value === values.newPassword)
The issue is the custom validation for matching the e-mail fields. I made a fork here which I fixed using the method from this Github issue to add a custom validation method to Yup for comparing equality of fields, a feature which is apparently not well-supported.

Getting permission-denied when trying to validate that Firestore user can only post comments under their own UID

I have this code but don't really know where the insufficient security error coming from. I can't seems to figure it out. Could it be from cloudfirestore?
postComment(getBlogId: string, comment: any): Promise<any> {
if (this.currentUser) {
return this.afs.collection('blogs').doc(getBlogId).collection('comments')
.add({
author: {
'_id': this.currentUser.uid,
'firstname': this.currentUser.displayName ? this.currentUser.displayName : this.currentUser.email
},
comment: comment.comment,
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
updatedAt: firebase.firestore.FieldValue.serverTimestamp()
});
} else {
return Promise.reject(new Error('LogIn to Continue!'));
}
}
This is in the services, but I am getting error when I submit a comment.
And in my component, when iI submit the console, it is giving me the form value and the value of the blog ID.
onSubmit() {
console.log(this.commentForm.value);
console.log(this.blog._id);
this.blogservices.postComment(this.blog._id, this.commentForm.value)
.then(() => {
this.blogservices.getComments(this.blog._id)
.subscribe(comments => this.blog.comments = comments);
});
}
I can't really place it where I am getting the
ERROR Error Uncaught (in promise) FirebaseError [code=permission-denied]: Missing or insufficient permissions FirebaseError: Missing or insufficient permissions.
And this is my firestore rules, which I think is correct because request should match user uid and the resouce attached to the data to be submitted.
match /comments/{comment}{
allow delete: if request.data.user_uid == request.auth.uid;
allow write: if request.resource.data.user_uid == request.auth.uid;
allow read;
}
I have tried posting directly without the user info but getting same error.
You're comparing the UID of the user posting (request.auth.uid), against request.resource.data.user_uid. That means that you'll need to have a field user_uid in the root of your document.
When adding the document however, you do it as this:
.add({
author: {
'_id': this.currentUser.uid,
'firstname': this.currentUser.displayName ? this.currentUser.displayName : this.currentUser.email
},
So the current user's UID is in author._id, which is a different path from what your security rules are looking at. The fix is to make sure both are using the same path. To do that in the security rules would look something like this:
allow write: if request.resource.data.author._id == request.auth.uid;

Resources