What is the proper way to do two factor registration when dealing with api?
1) On frontend there is a form - email and phone - sending data to api method
2) I have an API that register user, by email and phone, generate code and send it over sms on phone
Question - what is the proper way to check sms code on front? (send code as response for register looking not so secure)
I think the flow should be (more or less):
client:
- user fills out form and hits submit
- sends form data to backend
- show field for 2FA code entry
server:
- handles form data, generates a 2FA code, and sends that code to the client via email and/or SMS
- store that code in a DB (relational or not, doesn't really matter); you could even just store it whatever session manager you're using
- map that code to the current session, or supply that code to the client to correlate the 2FA code to the client
the user receives the code
client:
- user enters the code and hits submit
- sends entered code to the server
server:
- using the session or the DB, validates the code
- then, and only then, you auth the user
You're right - never validate auth on the frontend.
Your basic code should look like this on the frontend:
class TwoFactorAuth extends React.Component {
state = { stage: 1 };
onSubmitStageOne = (data) => {
// goes to /
submit(data).then(() => {
this.setState({ stage: 2 });
});
}
onSubmitStageTwo = (data) => {
// goes to /auth
authenticate(data).then(() => {
// success
}).catch(() => {
// wrong code, try again
});
}
renderStageOne() {
return (
<form onSubmit={this.onSubmitStageOne}>
<input name="email" />
<input name="phone" />
</form>
)
}
renderStageTwo() {
return (
<form onSubmit={this.onSubmitStageTwo}>
<input name="code" />
</form>
)
}
render() {
return this.state.stage === 1 ?
this.renderStageOne() :
this.renderStageTwo();
}
}
something to note is that I'm using ES6 classes and ES7 class property initializers - you'll need babel if you want to copy/paste the code above.
your backend routes would be similar to:
router.post("/", (req, res) => {
const code = utils.generate2FACode();
request.session["code"] = code;
utils.emailOrSMSUser(code);
res.sendStatus(200);
});
router.post("/auth", (req, res) => {
const code = req.session["code"];
if (code === req.body.code) {
req.session["signedIn"] = true; // simplified for demonstration
res.sendStatus(200);
} else {
res.sendStatus(401);
}
});
here I'm using express
and express-session
. I'll let you write generate2FACode
and emailOrSMSUser
.
edit: whoops, you said PHP API in the title - similar logic, just with PHP instead of Node/Express.