I have a web form built with VueJS that allows authenticated users to change their passwords. The backend is using Firebase, and I'm trying to validate the user's current password prior to calling the password-change API.
My code looks like this:
rules: {
...
isPreviousPassword: v => {
var credentials = await firebase.auth().currentUser
.reauthenticateWithCredential(
firebase.auth.EmailAuthProvider.credential(
firebase.auth().currentUser.email,
v)
)
return credentials || 'Your password is incorrect'
}
}
Babel refuses the code above and returns the following error:
Syntax Error: await is a reserved word
I haven't found any ways to get over this error, and none of the code snippets I found online, which are reported to work, are being blocked by Babel, which throws the same error message as above.
What's the best way to solve this?
Try to add async to your function:
isPreviousPassword: async (v) => {
var credentials = await firebase.auth().currentUser
.reauthenticateWithCredential(
firebase.auth.EmailAuthProvider.credential(
firebase.auth().currentUser.email,
v)
)
return credentials || 'Your password is incorrect'
}
Related
I'm trying to set up an email verification flow in my project, but I can't seem to get it right.
How my flow works now is the user enters their credentials (email and password), which are used to create a new firebase user. Then, once that promise is resolved, it sends an email verification link to the new user that was created. The code looks like this:
async createUser(email: string, password: string) {
try {
console.log("Creating user...");
const userCredentials = await createUserWithEmailAndPassword(
auth,
email,
password
);
console.log("Successfully created user");
const { user } = userCredentials;
console.log("Sending email verification link...");
await this.verifyEmail(user);
console.log("EMAIL VERIFICATION LINK SUCCESSFULLY SENT");
return user;
} catch (err) {
throw err;
}
}
async verifyEmail(user: User) {
try {
sendEmailVerification(user);
} catch (err) {
throw err;
}
}
The link is sent through fine, but once I press on it, I'm redirected to a page that says this:
Strangely, the user's email is verified after this, in spite of the error message displayed. Any idea why this is happening?
Update:
I managed to figure it out. The email provider I'm using is my university's, and it seems to be preventing the verification link from working properly. I did try with my personal email to see if that was the case, but I wasn't seeing the verification link appearing there. I eventually realized that it was because it was being stored in the spam folder. It's working on other email providers, though, ideally, I'd want it to work on my university's email provider (the emails that users sign up with are supposed to be exclusively student emails). Any ideas how I could resolve this?
I eventually figured out that the issue was with my email provider. I was using my student email, which the university provides, and I imagine they've placed rigorous measures in place to secure them as much as possible. I have no idea what was preventing it from working, but I managed to figure out a workaround.
In brief, I changed the action URL in the template (which can be found in the console for your Firebase project in the Authentication section, under the Templates tab) to a route on my website titled /authenticate. I created a module to handle email verification. Included in it is a function that parses the URL, extracting the mode (email verification, password reset, etc.), actionCode (this is the important one. It stores the id that Firebase decodes to determine if it's valid), continueURL (optional), and lang (optional).
export const parseUrl = (queryString: string) => {
const urlParams = new URLSearchParams(window.location.search);
const mode = urlParams.get("mode");
const actionCode = urlParams.get("oobCode");
const continueUrl = urlParams.get("continueUrl");
const lang = urlParams.get("lang") ?? "en";
return { mode, actionCode, continueUrl, lang };
};
I created another method that handles the email verification by applying the actionCode from the URL using Firebase's applyActionCode.
export const handleVerifyEmail = async (
actionCode: string,
continueUrl?: string,
lang?: string
) => {
try {
await applyActionCode(auth, actionCode);
return { alreadyVerified: false };
} catch (err) {
if (err instanceof FirebaseError) {
switch (err.code) {
case "auth/invalid-action-code": {
return { alreadyVerified: true };
}
}
}
throw err;
}
};
The auth/invalid-action-code error seems to be thrown when the user is already verified. I don't throw an error for it, because I handle this differently to other errors.
Once the user presses the verification link, they're redirected to the /authenticate page on my website. This page then handles the email verification by parsing the query appended to the route. The URL looks something like this http://localhost:3000/authenticate?mode=verifyEmail&oobCode=FLVl85S-ZI13_am0uwWeb4Jy8DUWC3E6kIiwN2LLFpUAAAGDUJHSwA&apiKey=AIzaSyA_V9nKEZeoTOECWaD7UXuzqCzcptmmHQI&lang=en
Of course, in production, the root path would be the name of the website instead of localhost. I have my development environment running on port 3000.
Once the user lands on the authentication page, I handle the email verification in a useEffect() hook (Note: I'm using Next.js, so if you're using a different framework you might have to handle changing the URL differently):
useEffect(() => {
verifyEmail();
async function verifyEmail() {
const { actionCode } = parseUrl(window.location.search);
if (!actionCode) return;
router.replace("/authenticate", undefined, { shallow: true });
setLoadingState(LoadingState.LOADING);
try {
const response = await handleVerifyEmail(actionCode!);
if (response.alreadyVerified) {
setEmailAlreadyVerified(true);
onEmailAlreadyVerified();
return;
}
setLoadingState(LoadingState.SUCCESS);
onSuccess();
} catch (err) {
console.error(err);
onFailure();
setLoadingState(LoadingState.ERROR);
}
}
}, []);
It first checks if there is an action code in the URL, in case a user tries to access the page manually.
The onSuccess, onFailure, and onEmailAlreadyVerified callbacks just display toasts. loadingState and emailAlreadyVerified are used to conditionally render different responses to the user.
I'm working on React + Meteor application and can't login using accounts-password package via loginWithPassword function.
The official API says that Unrecognized options for login request [400] error pops up when your user or password is undefined (or, i guess, just do not match the API), but i've checked the arguments and everything seems correct. username and password are strings. Meteor has ability to operate with user object, but this is not working too.
Here's the sample of my code.
const submit = useCallback(
(values) => {
const { email, password } = values;
const callback = (err) => {
if (err) {
console.log(err);
notifyError();
return;
}
login();
history.replace(from);
};
Meteor.loginWithPassword(email, password, callback);
},
[from, history, login, notifyError]
);
Any help appreciated.
It looks like that particular error can occur when there are no login handlers registered.
Have you added the accounts-password package ?
meteor add accounts-password
So, as the title suggests, I'm looking for a way to integrate my own custom authentication service into the parse server, which is installed inside a docker container. This authentication is basically an OpenID implementation of KeyCloak.
The point is that I don't (and it would be best for my architecture not to) have parse server served with express on my local machine.
What I've been trying so far, was to search the internet, read the issues, read the parse server documents for JavaScript and the guide and other stuff to find out, how can I achieve it.
It seems that it doesn't matter what I do, at the end of each test, I get a 252 This authentication method is unsupported error! (this happens even if I use facebook, oauth, oauth2, etc).
So right now, the docker-compose service looks like this:
parse-server:
image: parseplatform/parse-server
ports:
- "${SERVER_PORT}:1337"
restart: "always"
volumes:
- ./server/parse/custom-auth:/parse-server/custom-auth
depends_on:
- mongodb
links:
- mongodb:mongo
environment:
- PARSE_SERVER_APPLICATION_ID=${APP_ID}
- PARSE_SERVER_MASTER_KEY=${MASTER_KEY}
- PARSE_SERVER_DATABASE_URI=mongodb://mongo:${MONGO_PORT}/dev
- PARSE_SERVER_START_LIVE_QUERY_SERVER=1
- PARSE_SERVER_LIVE_QUERY={"classNames":${LIVE_QUERY_CLASSES}}
- PARSE_SERVER_MOUNT_GRAPHQL=${GQL_API}
- PARSE_SERVER_MOUNT_PLAYGROUND=${GQL_PLAYGROUND}
- PARSE_SERVER_AUTH_PROVIDERS={"swwwan-mail-auth":{"module":"/parse-server/custom-auth/swwwan-mail-auth/index.js"}}
and the login/signup part:
export const loginWithParse = async (account: IUserColumnTypes) => {
if (account.username === null || account.password === null) {
throw "validation failed";
}
// #ts-ignore
const loggedIn = await Parse.User.logInWith("swwwan.mail-auth", {
authData: {
id: "",
access_token: "",
},
});
console.log({ loggedIn });
//return await Parse.User.logIn(account.username, account.password);
};
another alternative for login/signup:
export const loginWithParse = async (account: IUserColumnTypes) => {
if (account.username === null || account.password === null) {
throw "validation failed";
}
const u = new Parse.User();
u._linkWith("swwwan-mail-auth", {
authData: {
id: "tester",
access_token: "sample_access_token",
},
})
.then(res => console.log(res))
.catch(e => console.log(e));
//return await Parse.User.logIn(account.username, account.password);
};
UPDATE: by using the second alternative, I actually get the error:
error: Parse error: Invalid key name: authData.swwwan-mail-auth.id {"code":105,"stack":"Error: Invalid key name: authData.swwwan-mail-auth.id
Is there a way to make it work? probably I'm missing something here.
tnx :)
note that the 'dangle' in the link functions will be deprecated in the forthcoming 2.9 release of the Parse JS SDK
Sorry that the documentation isn't better yet. Will be getting some more work
What you're attempting is doable!
Your final error is giving one big clue: the name of your adapter can't have any characters that aren't valid for a javascript identifier. In this case, the - is causing a problem since when we save it in the database, the adapter name is used as a key.
The unit tests are often the best documentation and you may find them helpful in this case.
See:
the declaration of a custom adapter
Configuring the server to load the adapter (you're doing this right)
using the adapter
In my Authentication -> Sign-in Method - it's Email & Password set to 'Enabled'.
I have a handler for an onSubmit calling this:
createUser(e){
e.preventDefault();
const email = this.createEmail.value
const password = this.createPassword.value
const confirm = this.confirmPassword.value
if(password === confirm) {
firebase.auth()
.createUserWithEmailAndPassword(email, password)
.then((res) => {
this.showCreate(e)
})
.catch((error) => {
alert(error.message)
})
}
else {
alert('Passwords must match')
}
}
And it shoots this error "The given sign-in provider is disabled for this Firebase project. Enable it in the Firebase console, under the sign-in method tab of the Auth section."
I'm using the firebase npm package. It's a note-taking application and it's successfully communicating with the database.
But I have it Enabled. Is anyone aware of how to fix this, or if there's a setting I seem to be missing?
SOLUTION: I fixed this by removing the environment variable and using the raw API string. Weird.
I fixed this by removing the environment variable and using the raw API string.
While utilizing the Parse service, I'm trying to allow users to skip sign-in fields by logging into the app via Facebook and using the Facebook information to fill in fields. I'm using Parse's Cloud Code to make the swift client-side code as simple as possible. However, I'm getting a little talk-back from Parse.Cloud when trying to use the cloud code to set the Username and Email Address fields of Parse.User. I get the following errors when running it in-app.
[Error]: Can't modify username in the before save trigger (Code: 141, Version: 1.6.1)
And when removing the username function, I receive:
[Error]: Can't modify email in the before save trigger (Code: 141, Version: 1.6.1)
Through the same code, I'm also setting firstName and lastName without any errors using the same method. Below is the code used.
[CLOUD CODE]
function pad(num, size){ return ('000000000' + num).substr(-size); }
Parse.Cloud.beforeSave(Parse.User, function (request, response) {
var token = request.object.get("authData").facebook.access_token
Parse.Cloud.httpRequest({
url: 'https://graph.facebook.com/v2.1/me?fields=first_name,last_name,email&access_token=' + token,
success: function(httpResponse) {
var responseData = httpResponse.data;
request.object.set("email", responseData.email) // <- Error Occurs Here
request.object.set("username", responseData.email.split("#")[0].concat(".", pad(Math.floor(Math.random()*10000),4))) // <- Error Occurs Here
request.object.set("firstName", responseData.first_name)
request.object.set("lastName", responseData.last_name)
response.success()
},
error: function(){
response.error("Something went wrong.")
}
})
})
[SWIFT CODE]
#IBAction func loginWithFacebookButtonAction(sender: AnyObject){
loginWithFacebookButtonOutlet.setTitle("Logging In...", forState: UIControlState.Normal)
let permissions = ["public_profile", "user_friends", "email"]
PFFacebookUtils.logInWithPermissions(permissions, {
(user: PFUser!, error: NSError!) -> Void in
if user == nil {
println("Uh oh. The user cancelled the Facebook login.")
} else if user.isNew {
println("User signed up and logged in through Facebook!")
self.performSegueWithIdentifier("facebookToApp", sender: self)
} else {
println("User logged in through Facebook!")
self.performSegueWithIdentifier("facebookToApp", sender: self)
}
})
}
Other questions here that I've encountered are caused when the user tries to use .save() instead of response.success() which doesn't seem to be the problem in this situation.
Many thanks.
it seems that username & email can not be altered in beforeSave trigger as they should be unique
Right now, the email field cannot be modified on a beforeSave because this field should be unique. If you were to use a value that another user is already using, this would result in an error that your beforeSave method would not be able to handle.
reference: https://www.parse.com/questions/why-cant-i-modify-the-email-field-in-a-before-save-trigger