These are some advanced topics about the Registration Plugin.
If you want to add validation to any of your fields during registration, you must use the XFBML version of the plugin, since the iframe can't do cross domain communication.
Pass the name of a single global function as the onvalidate param to the <fb:registration>. This function will be called whenever a user blurs a field or submits the form. For privacy reasons, the input to your function will be only the custom fields you requested, on blur or submit. (After registration, you will receive the complete set of data.) Your function should return an object with keys matching your field names, and values being the error string. Returning an empty object {} means there were no errors.
During the blur call, only non-empty field's errors will be shown. Submitting the form will be canceled if your function returns any errors.
<fb:registration redirect-uri="http://developers.facebook.com/tools/echo"
fields='[
{"name":"name"},
{"name":"foo","description":"Type foo","type":"text"},
{"name":"bar","description":"Type bar","type":"text"},
{"name":"facebooker","description":"Pick Paul","type":"select","options":
{"coder":"Paul","pm":"Austin","partners":"Cat"}},
{"name":"check","description":"Check this","type":"checkbox"},
{"name":"date","description":"Dec 16 2010","type":"date"},
{"name":"city","description":"Calgary","type":"typeahead","categories":
["city"]}]'
onvalidate="validate"></fb:registration>
<script>
function validate(form) {
errors = {};
if (form.foo !== "foo") {
errors.foo = "You didn't type foo";
}
if (form.bar !== "bar") {
errors.bar = "You didn't type bar";
}
if (form.facebooker !== "coder") {
errors.facebooker = "Pick the geeky one";
}
if (!form.check) {
errors.check = "Check the little box";
}
if (form.date !== '12/16/2010') {
errors.date = "That isn't the launch date";
}
if (form.city.id !== '111983945494775') {
errors.city = "That isn't Calgary, Alberta";
}
return errors;
}
</script>
If you have to check something on your server (e.g. if a username is taken) then you don't have to reply from the validation function right away. You can return null (which is the default return in javascript) and then use the second parameter to reply with any errors. You have 20 seconds before the form submits anyways.
<fb:registration redirect-uri="http://developers.facebook.com/tools/echo"
fields='[{"name":"name"},
{"name":"username","description":"Username","type":"text"}]'
onvalidate="validate_async"></fb:registration>
<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script>
function validate_async(form, cb) {
$.getJSON('http://graph.facebook.com/' + form.username + '?callback=?',
function(response) {
if (response.error) {
// Username isn't taken, let the form submit
cb();
}
cb({username: 'That username is taken'});
});
}
</script>
When a user clicks the 'Register' button on your site there are two data transfers. First, the data in the plugin is sent to Facebook. Facebook then signs the data with your application secret and passes it back to your redirect_uri. By default these data transfers are done in HTTPS and if you are using the iframe method, you should set the iframe source of the plugin to:
https://www.facebook.com/plugins/registration
instead of
http ://www.facebook.com/plugins/registration
If you are using the xfbml version of the plugin, simply load the javascript library over https instead of http. For example:
<script src="https://connect.facebook.net..."></script>
If you enable https, your redirect-uri must be an https URL, otherwise browsers will show an error warning. Enabling https protects your users against man-in-the-middle attacks during the two data transfers.
localeThe plugin will render in the locale that the user uses facebook in. If you want to always force a locale, just pass the locale parameter set to be a POSIX locale, which is a 2 letter lowercase language code then an _ followed by a 2 letter uppercase country code. The plugin hasn't been translated into every language, so if you want to help in your local language, use the translation app.
no_submitIf there are fields that you really don't want to travel over the wire to Facebook servers, you can specify the no_submit:true attribute and they will removed from the DOM before submitting. To get the data on your end, you must use the client side validation function to add those to the user's session (via a cookie or server-side call). Then when the form submit comes through you can read the signed_request and the data you stored in the user's session and build your user row in your database.
One 'gotcha' is that your client side validation function and the form submit result page are two different web pages. You have to store a browser cookie so that you can correlate the data.
Try submitting this form, and see that the password field disappears as the form submits.
<fb:registration redirect-uri="http://developers.facebook.com/tools/echo"
fields='[{"name":"name"},{"name":"password","no_submit":true}]'
onvalidate="validateAndSave"></fb:registration>
<script>
function validateAndSave(form) {
if (!form.password) {
return({"password":"Type a password"});
}
var dt = new Date(), expiryTime = dt.setTime( dt.getTime() + 1000*5 );
document.cookie = 'password='+form.password+';expires='+dt.toGMTString();
return {};
}
</script>
fieldsWhen you request facebook data, we verify the form fields before packaging them up in the signed_request. This lets you assume that all the data is genuine and saves you from having to verify things. The one problem that could arise, is a smart attacker could change the form fields and submit them to you, thereby giving you unverified data.
For example, these two fields will look the same in the registration part of the signed_request.
fields='[
{"name":"name"},
{"name":"email"}]'
fields='[
{"name":"name"},
{"name":"email","description":"Fake Email","type":"text"}]'
To protect yourself against this attack, you should look inside the registration_metadata key and make sure the fields exactly match the fields you used to build your forms.
Compare the registration and registration_metadata keys when you submit these two forms.
If you are unable to validate the signed_request because you can't embed your application secret (e.g. in javascript or a desktop application) then you MUST only use one piece of information from the payload, the oauth_token.
You should hit the graph api to fetch any user data. For example, use /me to fetch the basic information. You will be granted the permission to read any data from the graph api that the user originally put into your form.