/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const { ERRNO_INVALID_FXA_ASSERTION, ERRNO_NETWORK, ERRNO_PARSE, ERRNO_UNKNOWN_ERROR, ERROR_CODE_METHOD_NOT_ALLOWED, ERROR_MSG_METHOD_NOT_ALLOWED, ERROR_NETWORK, ERROR_PARSE, ERROR_UNKNOWN, } = ChromeUtils.import("resource://gre/modules/FxAccountsCommon.js"); const { FxAccountsOAuthGrantClient, FxAccountsOAuthGrantClientError, } = ChromeUtils.import("resource://gre/modules/FxAccountsOAuthGrantClient.jsm"); const CLIENT_OPTIONS = { serverURL: "https://127.0.0.1:9010/v1", client_id: "abc123", }; const STATUS_SUCCESS = 200; /** * Mock request responder * @param {String} response * Mocked raw response from the server * @returns {Function} */ var mockResponse = function(response) { return function() { return { setHeader() {}, async post() { this.response = response; return response; }, }; }; }; /** * Mock request error responder * @param {Error} error * Error object * @returns {Function} */ var mockResponseError = function(error) { return function() { return { setHeader() {}, async post() { throw error; }, }; }; }; add_test(function missingParams() { let client = new FxAccountsOAuthGrantClient(CLIENT_OPTIONS); try { client.getTokenFromAssertion(); } catch (e) { Assert.equal(e.message, "Missing 'assertion' parameter"); } try { client.getTokenFromAssertion("assertion"); } catch (e) { Assert.equal(e.message, "Missing 'scope' parameter"); } run_next_test(); }); add_test(function successfulResponse() { let client = new FxAccountsOAuthGrantClient(CLIENT_OPTIONS); let response = { success: true, status: STATUS_SUCCESS, body: JSON.stringify({ access_token: "http://example.com/image.jpeg", id: "0d5c1a89b8c54580b8e3e8adadae864a", }), }; client._Request = new mockResponse(response); client.getTokenFromAssertion("assertion", "scope").then(function(result) { Assert.equal(result.access_token, "http://example.com/image.jpeg"); run_next_test(); }); }); add_test(function parseErrorResponse() { let client = new FxAccountsOAuthGrantClient(CLIENT_OPTIONS); let response = { success: true, status: STATUS_SUCCESS, body: "unexpected", }; client._Request = new mockResponse(response); client.getTokenFromAssertion("assertion", "scope").catch(function(e) { Assert.equal(e.name, "FxAccountsOAuthGrantClientError"); Assert.equal(e.code, STATUS_SUCCESS); Assert.equal(e.errno, ERRNO_PARSE); Assert.equal(e.error, ERROR_PARSE); Assert.equal(e.message, "unexpected"); run_next_test(); }); }); add_task(async function serverErrorResponse() { let client = new FxAccountsOAuthGrantClient(CLIENT_OPTIONS); let response = { status: 400, body: JSON.stringify({ code: 400, errno: 104, error: "Bad Request", message: "Unauthorized", reason: "Invalid fxa assertion", }), }; client._Request = new mockResponse(response); client.getTokenFromAssertion("blah", "scope").catch(function(e) { Assert.equal(e.name, "FxAccountsOAuthGrantClientError"); Assert.equal(e.code, 400); Assert.equal(e.errno, ERRNO_INVALID_FXA_ASSERTION); Assert.equal(e.error, "Bad Request"); Assert.equal(e.message, "Unauthorized"); run_next_test(); }); }); add_task(async function networkErrorResponse() { let client = new FxAccountsOAuthGrantClient({ serverURL: "https://domain.dummy", client_id: "abc123", }); client.getTokenFromAssertion("assertion", "scope").catch(function(e) { Assert.equal(e.name, "FxAccountsOAuthGrantClientError"); Assert.equal(e.code, null); Assert.equal(e.errno, ERRNO_NETWORK); Assert.equal(e.error, ERROR_NETWORK); run_next_test(); }); }); add_test(function unsupportedMethod() { let client = new FxAccountsOAuthGrantClient(CLIENT_OPTIONS); return client._createRequest("/", "PUT").catch(function(e) { Assert.equal(e.name, "FxAccountsOAuthGrantClientError"); Assert.equal(e.code, ERROR_CODE_METHOD_NOT_ALLOWED); Assert.equal(e.errno, ERRNO_NETWORK); Assert.equal(e.error, ERROR_NETWORK); Assert.equal(e.message, ERROR_MSG_METHOD_NOT_ALLOWED); run_next_test(); }); }); add_test(function onCompleteRequestError() { let client = new FxAccountsOAuthGrantClient(CLIENT_OPTIONS); client._Request = new mockResponseError(new Error("onComplete error")); client.getTokenFromAssertion("assertion", "scope").catch(function(e) { Assert.equal(e.name, "FxAccountsOAuthGrantClientError"); Assert.equal(e.code, null); Assert.equal(e.errno, ERRNO_NETWORK); Assert.equal(e.error, ERROR_NETWORK); Assert.equal(e.message, "Error: onComplete error"); run_next_test(); }); }); add_test(function incorrectErrno() { let client = new FxAccountsOAuthGrantClient(CLIENT_OPTIONS); let response = { status: 400, body: JSON.stringify({ code: 400, errno: "bad errno", error: "Bad Request", message: "Unauthorized", reason: "Invalid fxa assertion", }), }; client._Request = new mockResponse(response); client.getTokenFromAssertion("blah", "scope").catch(function(e) { Assert.equal(e.name, "FxAccountsOAuthGrantClientError"); Assert.equal(e.code, 400); Assert.equal(e.errno, ERRNO_UNKNOWN_ERROR); Assert.equal(e.error, "Bad Request"); Assert.equal(e.message, "Unauthorized"); run_next_test(); }); }); add_test(function constructorTests() { try { Services.prefs.setCharPref( "identity.fxaccounts.remote.oauth.uri", "https://example.com/v1" ); validationHelper({}, "Error: Missing 'client_id' parameter"); } finally { Services.prefs.clearUserPref("identity.fxaccounts.remote.oauth.uri"); } validationHelper( { serverURL: "https://example.com" }, "Error: Missing 'client_id' parameter" ); validationHelper( { serverURL: "https://example.com" }, "Error: Missing 'client_id' parameter" ); validationHelper( { client_id: "123ABC", serverURL: "http://example.com" }, "Error: 'serverURL' must be HTTPS" ); try { Services.prefs.setBoolPref("identity.fxaccounts.allowHttp", true); validationHelper( { client_id: "123ABC", serverURL: "http://example.com" }, null ); } finally { Services.prefs.clearUserPref("identity.fxaccounts.allowHttp"); } run_next_test(); }); add_test(function errorTests() { let error1 = new FxAccountsOAuthGrantClientError(); Assert.equal(error1.name, "FxAccountsOAuthGrantClientError"); Assert.equal(error1.code, null); Assert.equal(error1.errno, ERRNO_UNKNOWN_ERROR); Assert.equal(error1.error, ERROR_UNKNOWN); Assert.equal(error1.message, null); let error2 = new FxAccountsOAuthGrantClientError({ code: STATUS_SUCCESS, errno: 1, error: "Error", message: "Something", }); let fields2 = error2._toStringFields(); let statusCode = 1; Assert.equal(error2.name, "FxAccountsOAuthGrantClientError"); Assert.equal(error2.code, STATUS_SUCCESS); Assert.equal(error2.errno, statusCode); Assert.equal(error2.error, "Error"); Assert.equal(error2.message, "Something"); Assert.equal(fields2.name, "FxAccountsOAuthGrantClientError"); Assert.equal(fields2.code, STATUS_SUCCESS); Assert.equal(fields2.errno, statusCode); Assert.equal(fields2.error, "Error"); Assert.equal(fields2.message, "Something"); Assert.ok(error2.toString().includes("Something")); run_next_test(); }); add_test(function networkErrorResponse() { let client = new FxAccountsOAuthGrantClient({ serverURL: "https://domain.dummy", client_id: "abc123", }); client.getTokenFromAssertion("assertion", "scope").catch(function(e) { Assert.equal(e.name, "FxAccountsOAuthGrantClientError"); Assert.equal(e.code, null); Assert.equal(e.errno, ERRNO_NETWORK); Assert.equal(e.error, ERROR_NETWORK); run_next_test(); }); }); /** * Quick way to test the "FxAccountsOAuthGrantClient" constructor. * * @param {Object} options * FxAccountsOAuthGrantClient constructor options * @param {String} expected * Expected error message, or null if it's expected to pass. * @returns {*} */ function validationHelper(options, expected) { try { new FxAccountsOAuthGrantClient(options); } catch (e) { return Assert.equal(e.toString(), expected); } return Assert.equal(expected, null); }