Created
February 18, 2012 08:28
-
-
Save anantn/1858230 to your computer and use it in GitHub Desktop.
How to verify a Mozilla Apps receipt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function doVerifyReceipt(cb, options) { | |
navigator.mozApps.getSelf(function(app) { | |
var record = app.receipt; | |
if (!record) { | |
cb({"error": "Application not installed"}); | |
return; | |
} | |
if (typeof cb !== "function") { | |
throw "Callback not provided in doVerifyReceipt"; | |
} | |
function base64urldecode(arg) { | |
var s = arg; | |
s = s.replace(/-/g, '+'); // 62nd char of encoding | |
s = s.replace(/_/g, '/'); // 63rd char of encoding | |
switch (s.length % 4) // Pad with trailing '='s | |
{ | |
case 0: break; // No pad chars in this case | |
case 2: s += "=="; break; // Two pad chars | |
case 3: s += "="; break; // One pad char | |
default: throw new InputException("Illegal base64url string!"); | |
} | |
return window.atob(s); // Standard base64 decoder | |
} | |
function parseReceipt(rcptData) { | |
// rcptData is a JWT. We should use a JWT library. | |
var data = rcptData.split("."); | |
if (data.length != 3) | |
return null; | |
// convert base64url to base64 | |
var payload = base64urldecode(data[1]); | |
var parsed = JSON.parse(payload); | |
return parsed; | |
} | |
try { | |
if (!record.install_data) { | |
throw "Receipt not found"; | |
} | |
var receipt = parseReceipt(record.install_data.receipt); | |
if (!receipt) { | |
throw "Invalid Receipt"; | |
} | |
} catch (e) { | |
cb({"error": e}); | |
return; | |
} | |
// Two status "flags", one for verify XHR other for BrowserID XHR | |
// These two XHRs run in parallel, and the first one to error out invokes cb() | |
// If both XHRs succeed, the last one to succeed will invoke cb() | |
var assertion; | |
var errorSent = false; | |
var verifyStatus = false; | |
var assertStatus = false; | |
var verifyURL = receipt.verify; | |
var verifyReq = new XMLHttpRequest(); | |
verifyReq.open('POST', verifyURL, true); | |
verifyReq.onreadystatechange = function (aEvt) { | |
if (verifyReq.readyState == 4) { | |
try { | |
if (verifyReq.status == 200) { | |
var resp = JSON.parse(verifyReq.responseText); | |
if (resp.status != "ok") { | |
throw resp.status; | |
} | |
dump("verifyReq success! " + verifyReq.responseText + "\n"); | |
verifyStatus = true; | |
if (options && options.receiptVerified && typeof options.receiptVerified == "function") { | |
options.receiptVerified(receipt); | |
} | |
if (verifyStatus && assertStatus) { | |
cb({"success": {"receipt": receipt, "assertion": assertion}}); | |
} | |
} else { | |
throw verifyReq.status; | |
} | |
} catch(e) { | |
dump("error in verifyReq! " + verifyReq.responseText); | |
if (!errorSent) { | |
cb({"error": "Invalid Receipt: " + verifyReq.responseText}); | |
errorSent = true; | |
} | |
} | |
} | |
}; | |
try { | |
verifyReq.send(record.install_data.receipt); | |
} catch (e) { | |
// Offline | |
if (!errorSent) { | |
cb({"error": "Offline: Could not verify receipt"}); | |
errorSent = true; | |
} | |
} | |
// Start BrowserID verification | |
var idOptions = {"silent": true, "requiredEmail": receipt.user.value}; | |
navigator.id.get(function(ast) { | |
assertion = ast; | |
if (!assertion) { | |
cb({"error": "Invalid Identity"}); | |
return; | |
} | |
var assertReq = new XMLHttpRequest(); | |
assertReq.open('POST', 'https://browserid.org/verify', true); | |
assertReq.onreadystatechange = function(aEvt) { | |
if (assertReq.readyState == 4) { | |
try { | |
if (assertReq.status == 200) { | |
var resp = JSON.parse(assertReq.responseText); | |
if (resp["status"] != "okay") { | |
throw resp.status; | |
} | |
dump("assertReq success! " + assertReq.responseText + "\n"); | |
assertStatus = true; | |
if (verifyStatus && assertStatus) { | |
cb({"success": {"receipt": receipt, "assertion": assertion}}); | |
} | |
} else { | |
throw assertReq.status; | |
} | |
} catch(e) { | |
dump("error in assertReq! " + assertReq.responseText); | |
if (!errorSent) { | |
cb({"error": "Invalid Identity: " + assertReq.responseText}); | |
errorSent = true; | |
} | |
} | |
} | |
}; | |
var body = "assertion=" + encodeURIComponent(assertion) + "&audience=" + | |
encodeURIComponent(window.location.protocol + "//" + window.location.host); | |
assertReq.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); | |
try { | |
assertReq.send(body); | |
} catch (e) { | |
// Offline | |
if (!errorSent) { | |
cb({"error": "Offline: Could not verify receipt"}); | |
errorSent = true; | |
} | |
} | |
}, idOptions, function(err) { | |
// Ideally we'd implement a fallback here where we open a BrowserID | |
// popup dialog. But this is not trivial to do, punting for now. | |
if (!errorSent) { | |
cb({"error": "Could not obtain Identity: " + err}); | |
errorSent = true; | |
} | |
}); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment