зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1127638 - Provide a way to get an OAuth token for a set of desired scopes for the currently logged in user r=markh
This commit is contained in:
Родитель
120a757f5f
Коммит
3154b578e0
|
@ -1756,6 +1756,12 @@ pref("identity.fxaccounts.remote.signin.uri", "https://accounts.firefox.com/sign
|
|||
// "identity.fxaccounts.remote.signup.uri" pref.
|
||||
pref("identity.fxaccounts.settings.uri", "https://accounts.firefox.com/settings");
|
||||
|
||||
// The remote URL of the FxA Profile Server
|
||||
pref("identity.fxaccounts.remote.profile.uri", "https://profile.accounts.firefox.com/v1");
|
||||
|
||||
// The remote URL of the FxA OAuth Server
|
||||
pref("identity.fxaccounts.remote.oauth.uri", "https://oauth.accounts.firefox.com/v1");
|
||||
|
||||
// Migrate any existing Firefox Account data from the default profile to the
|
||||
// Developer Edition profile.
|
||||
#ifdef MOZ_DEV_EDITION
|
||||
|
|
|
@ -23,6 +23,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsClient",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "jwcrypto",
|
||||
"resource://gre/modules/identity/jwcrypto.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsOAuthGrantClient",
|
||||
"resource://gre/modules/FxAccountsOAuthGrantClient.jsm");
|
||||
|
||||
// All properties exposed by the public FxAccounts API.
|
||||
let publicProperties = [
|
||||
"accountStatus",
|
||||
|
@ -32,6 +35,7 @@ let publicProperties = [
|
|||
"getAssertion",
|
||||
"getKeys",
|
||||
"getSignedInUser",
|
||||
"getOAuthToken",
|
||||
"loadAndPoll",
|
||||
"localtimeOffsetMsec",
|
||||
"now",
|
||||
|
@ -904,6 +908,28 @@ FxAccountsInternal.prototype = {
|
|||
newQueryPortion += "email=" + encodeURIComponent(accountData.email);
|
||||
return url + newQueryPortion;
|
||||
}).then(result => currentState.resolve(result));
|
||||
},
|
||||
|
||||
/*
|
||||
* Get an OAuth token for the user
|
||||
*/
|
||||
getOAuthToken: function (options = {}) {
|
||||
log.debug("getOAuthToken enter");
|
||||
|
||||
if (!options.scope) {
|
||||
throw new Error("Missing 'scope' option");
|
||||
}
|
||||
|
||||
let oAuthURL = Services.urlFormatter.formatURLPref("identity.fxaccounts.remote.oauth.uri");
|
||||
|
||||
let client = options.client || new FxAccountsOAuthGrantClient({
|
||||
serverURL: oAuthURL,
|
||||
client_id: FX_OAUTH_CLIENT_ID
|
||||
});
|
||||
|
||||
return this.getAssertion(oAuthURL)
|
||||
.then(assertion => client.getTokenFromAssertion(assertion, options.scope))
|
||||
.then(result => result.access_token);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -94,6 +94,9 @@ exports.ON_FXA_UPDATE_NOTIFICATION = "fxaccounts:update";
|
|||
exports.UI_REQUEST_SIGN_IN_FLOW = "signInFlow";
|
||||
exports.UI_REQUEST_REFRESH_AUTH = "refreshAuthentication";
|
||||
|
||||
// The OAuth client ID for Firefox Desktop
|
||||
exports.FX_OAUTH_CLIENT_ID = "5882386c6d801776";
|
||||
|
||||
// Server errno.
|
||||
// From https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#response-format
|
||||
exports.ERRNO_ACCOUNT_ALREADY_EXISTS = 101;
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Firefox Accounts OAuth Grant Client allows clients to obtain
|
||||
* an OAuth token from a BrowserID assertion. Only certain client
|
||||
* IDs support this privilage.
|
||||
*/
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["FxAccountsOAuthGrantClient", "FxAccountsOAuthGrantClientError"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://gre/modules/FxAccountsCommon.js");
|
||||
Cu.import("resource://services-common/rest.js");
|
||||
|
||||
Cu.importGlobalProperties(["URL"]);
|
||||
|
||||
const AUTH_ENDPOINT = "/authorization";
|
||||
|
||||
/**
|
||||
* Create a new FxAccountsOAuthClient for browser some service.
|
||||
*
|
||||
* @param {Object} options Options
|
||||
* @param {Object} options.parameters
|
||||
* @param {String} options.parameters.client_id
|
||||
* OAuth id returned from client registration
|
||||
* @param {String} options.parameters.serverURL
|
||||
* The FxA OAuth server URL
|
||||
* @param [authorizationEndpoint] {String}
|
||||
* Optional authorization endpoint for the OAuth server
|
||||
* @constructor
|
||||
*/
|
||||
this.FxAccountsOAuthGrantClient = function(options) {
|
||||
|
||||
this._validateOptions(options);
|
||||
this.parameters = options;
|
||||
|
||||
try {
|
||||
this.serverURL = new URL(this.parameters.serverURL);
|
||||
} catch (e) {
|
||||
throw new Error("Invalid 'serverURL'");
|
||||
}
|
||||
|
||||
log.debug("FxAccountsOAuthGrantClient Initialized");
|
||||
};
|
||||
|
||||
this.FxAccountsOAuthGrantClient.prototype = {
|
||||
|
||||
/**
|
||||
* Retrieves an OAuth access token for the signed in user
|
||||
*
|
||||
* @param {Object} assertion BrowserID assertion
|
||||
* @param {String} scope OAuth scope
|
||||
* @return Promise
|
||||
* Resolves: {Object} Object with access_token property
|
||||
*/
|
||||
getTokenFromAssertion: function (assertion, scope) {
|
||||
if (!assertion) {
|
||||
throw new Error("Missing 'assertion' parameter");
|
||||
}
|
||||
if (!scope) {
|
||||
throw new Error("Missing 'scope' parameter");
|
||||
}
|
||||
let params = {
|
||||
scope: scope,
|
||||
client_id: this.parameters.client_id,
|
||||
assertion: assertion,
|
||||
response_type: "token"
|
||||
};
|
||||
|
||||
return this._createRequest(AUTH_ENDPOINT, "POST", params);
|
||||
},
|
||||
|
||||
/**
|
||||
* Validates the required FxA OAuth parameters
|
||||
*
|
||||
* @param options {Object}
|
||||
* OAuth client options
|
||||
* @private
|
||||
*/
|
||||
_validateOptions: function (options) {
|
||||
if (!options) {
|
||||
throw new Error("Missing configuration options");
|
||||
}
|
||||
|
||||
["serverURL", "client_id"].forEach(option => {
|
||||
if (!options[option]) {
|
||||
throw new Error("Missing '" + option + "' parameter");
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Interface for making remote requests.
|
||||
*/
|
||||
_Request: RESTRequest,
|
||||
|
||||
/**
|
||||
* Remote request helper
|
||||
*
|
||||
* @param {String} path
|
||||
* Profile server path, i.e "/profile".
|
||||
* @param {String} [method]
|
||||
* Type of request, i.e "GET".
|
||||
* @return Promise
|
||||
* Resolves: {Object} Successful response from the Profile server.
|
||||
* Rejects: {FxAccountsOAuthGrantClientError} Profile client error.
|
||||
* @private
|
||||
*/
|
||||
_createRequest: function(path, method = "POST", params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let profileDataUrl = this.serverURL + path;
|
||||
let request = new this._Request(profileDataUrl);
|
||||
method = method.toUpperCase();
|
||||
|
||||
request.setHeader("Accept", "application/json");
|
||||
request.setHeader("Content-Type", "application/json");
|
||||
|
||||
request.onComplete = function (error) {
|
||||
if (error) {
|
||||
return reject(new FxAccountsOAuthGrantClientError({
|
||||
error: ERROR_NETWORK,
|
||||
errno: ERRNO_NETWORK,
|
||||
message: error.toString(),
|
||||
}));
|
||||
}
|
||||
|
||||
let body = null;
|
||||
try {
|
||||
body = JSON.parse(request.response.body);
|
||||
} catch (e) {
|
||||
return reject(new FxAccountsOAuthGrantClientError({
|
||||
error: ERROR_PARSE,
|
||||
errno: ERRNO_PARSE,
|
||||
code: request.response.status,
|
||||
message: request.response.body,
|
||||
}));
|
||||
}
|
||||
|
||||
// "response.success" means status code is 200
|
||||
if (request.response.success) {
|
||||
return resolve(body);
|
||||
}
|
||||
|
||||
return reject(new FxAccountsOAuthGrantClientError(body));
|
||||
};
|
||||
|
||||
if (method === "POST") {
|
||||
request.post(params);
|
||||
} else {
|
||||
// method not supported
|
||||
return reject(new FxAccountsOAuthGrantClientError({
|
||||
error: ERROR_NETWORK,
|
||||
errno: ERRNO_NETWORK,
|
||||
code: ERROR_CODE_METHOD_NOT_ALLOWED,
|
||||
message: ERROR_MSG_METHOD_NOT_ALLOWED,
|
||||
}));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalized profile client errors
|
||||
* @param {Object} [details]
|
||||
* Error details object
|
||||
* @param {number} [details.code]
|
||||
* Error code
|
||||
* @param {number} [details.errno]
|
||||
* Error number
|
||||
* @param {String} [details.error]
|
||||
* Error description
|
||||
* @param {String|null} [details.message]
|
||||
* Error message
|
||||
* @constructor
|
||||
*/
|
||||
this.FxAccountsOAuthGrantClientError = function(details) {
|
||||
details = details || {};
|
||||
|
||||
this.name = "FxAccountsOAuthGrantClientError";
|
||||
this.code = details.code || null;
|
||||
this.errno = details.errno || ERRNO_UNKNOWN_ERROR;
|
||||
this.error = details.error || ERROR_UNKNOWN;
|
||||
this.message = details.message || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns error object properties
|
||||
*
|
||||
* @returns {{name: *, code: *, errno: *, error: *, message: *}}
|
||||
* @private
|
||||
*/
|
||||
FxAccountsOAuthGrantClientError.prototype._toStringFields = function() {
|
||||
return {
|
||||
name: this.name,
|
||||
code: this.code,
|
||||
errno: this.errno,
|
||||
error: this.error,
|
||||
message: this.message,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* String representation of a oauth grant client error
|
||||
*
|
||||
* @returns {String}
|
||||
*/
|
||||
FxAccountsOAuthGrantClientError.prototype.toString = function() {
|
||||
return this.name + "(" + JSON.stringify(this._toStringFields()) + ")";
|
||||
};
|
|
@ -13,6 +13,7 @@ EXTRA_JS_MODULES += [
|
|||
'FxAccountsClient.jsm',
|
||||
'FxAccountsCommon.js',
|
||||
'FxAccountsOAuthClient.jsm',
|
||||
'FxAccountsOAuthGrantClient.jsm',
|
||||
'FxAccountsProfileClient.jsm',
|
||||
]
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||
Cu.import("resource://gre/modules/FxAccounts.jsm");
|
||||
Cu.import("resource://gre/modules/FxAccountsClient.jsm");
|
||||
Cu.import("resource://gre/modules/FxAccountsCommon.js");
|
||||
Cu.import("resource://gre/modules/FxAccountsOAuthGrantClient.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
|
||||
|
@ -26,6 +27,11 @@ log.level = Log.Level.Debug;
|
|||
// See verbose logging from FxAccounts.jsm
|
||||
Services.prefs.setCharPref("identity.fxaccounts.loglevel", "DEBUG");
|
||||
|
||||
// The oauth server is mocked, but set these prefs to pass param checks
|
||||
Services.prefs.setCharPref("identity.fxaccounts.remote.oauth.uri", "https://example.com/v1");
|
||||
Services.prefs.setCharPref("identity.fxaccounts.oauth.client_id", "abc123");
|
||||
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
@ -681,6 +687,46 @@ add_test(function test_sign_out_with_remote_error() {
|
|||
fxa.signOut();
|
||||
});
|
||||
|
||||
add_test(function test_getOAuthToken() {
|
||||
let fxa = new MockFxAccounts();
|
||||
let alice = getTestUser("alice");
|
||||
let getTokenFromAssertionCalled = false;
|
||||
|
||||
fxa.internal._d_signCertificate.resolve("cert1");
|
||||
|
||||
// create a mock oauth client
|
||||
let client = new FxAccountsOAuthGrantClient({
|
||||
serverURL: "http://example.com/v1",
|
||||
client_id: "abc123"
|
||||
});
|
||||
client.getTokenFromAssertion = function () {
|
||||
getTokenFromAssertionCalled = true;
|
||||
return Promise.resolve({ access_token: "token" });
|
||||
};
|
||||
|
||||
fxa.setSignedInUser(alice).then(
|
||||
() => {
|
||||
fxa.getOAuthToken({ scope: "profile", client: client }).then(
|
||||
(result) => {
|
||||
do_check_true(getTokenFromAssertionCalled);
|
||||
do_check_eq(result, "token");
|
||||
run_next_test();
|
||||
}
|
||||
)
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
add_test(function test_getOAuthToken_missing_scope() {
|
||||
let fxa = new MockFxAccounts();
|
||||
|
||||
do_check_throws_message(() => {
|
||||
fxa.getOAuthToken();
|
||||
}, "Missing 'scope' option");
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/*
|
||||
* End of tests.
|
||||
* Utility functions follow.
|
||||
|
@ -743,7 +789,7 @@ function do_check_throws(func, result, stack)
|
|||
if (ex.name == result) {
|
||||
return;
|
||||
}
|
||||
do_throw("Expected result " + result + ", caught " + ex, stack);
|
||||
do_throw("Expected result " + result + ", caught " + ex.name, stack);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
|
|
|
@ -0,0 +1,256 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/FxAccountsCommon.js");
|
||||
Cu.import("resource://gre/modules/FxAccountsOAuthGrantClient.jsm");
|
||||
|
||||
const CLIENT_OPTIONS = {
|
||||
serverURL: "http://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}
|
||||
*/
|
||||
let mockResponse = function (response) {
|
||||
return function () {
|
||||
return {
|
||||
setHeader: function () {},
|
||||
post: function () {
|
||||
this.response = response;
|
||||
this.onComplete();
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Mock request error responder
|
||||
* @param {Error} error
|
||||
* Error object
|
||||
* @returns {Function}
|
||||
*/
|
||||
let mockResponseError = function (error) {
|
||||
return function () {
|
||||
return {
|
||||
setHeader: function () {},
|
||||
post: function () {
|
||||
this.onComplete(error);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
add_test(function missingParams () {
|
||||
let client = new FxAccountsOAuthGrantClient(CLIENT_OPTIONS);
|
||||
try {
|
||||
client.getTokenFromAssertion()
|
||||
} catch (e) {
|
||||
do_check_eq(e.message, "Missing 'assertion' parameter");
|
||||
}
|
||||
|
||||
try {
|
||||
client.getTokenFromAssertion("assertion")
|
||||
} catch (e) {
|
||||
do_check_eq(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: "{\"access_token\":\"http://example.com/image.jpeg\",\"id\":\"0d5c1a89b8c54580b8e3e8adadae864a\"}",
|
||||
};
|
||||
|
||||
client._Request = new mockResponse(response);
|
||||
client.getTokenFromAssertion("assertion", "scope")
|
||||
.then(
|
||||
function (result) {
|
||||
do_check_eq(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")
|
||||
.then(
|
||||
null,
|
||||
function (e) {
|
||||
do_check_eq(e.name, "FxAccountsOAuthGrantClientError");
|
||||
do_check_eq(e.code, STATUS_SUCCESS);
|
||||
do_check_eq(e.errno, ERRNO_PARSE);
|
||||
do_check_eq(e.error, ERROR_PARSE);
|
||||
do_check_eq(e.message, "unexpected");
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_test(function serverErrorResponse () {
|
||||
let client = new FxAccountsOAuthGrantClient(CLIENT_OPTIONS);
|
||||
let response = {
|
||||
status: 400,
|
||||
body: "{ \"code\": 400, \"errno\": 104, \"error\": \"Bad Request\", \"message\": \"Unauthorized\", \"reason\": \"Invalid fxa assertion\" }",
|
||||
};
|
||||
|
||||
client._Request = new mockResponse(response);
|
||||
client.getTokenFromAssertion("blah", "scope")
|
||||
.then(
|
||||
null,
|
||||
function (e) {
|
||||
do_check_eq(e.name, "FxAccountsOAuthGrantClientError");
|
||||
do_check_eq(e.code, 400);
|
||||
do_check_eq(e.errno, 104);
|
||||
do_check_eq(e.error, "Bad Request");
|
||||
do_check_eq(e.message, "Unauthorized");
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_test(function networkErrorResponse () {
|
||||
let client = new FxAccountsOAuthGrantClient({
|
||||
serverURL: "http://",
|
||||
client_id: "abc123"
|
||||
});
|
||||
client.getTokenFromAssertion("assertion", "scope")
|
||||
.then(
|
||||
null,
|
||||
function (e) {
|
||||
do_check_eq(e.name, "FxAccountsOAuthGrantClientError");
|
||||
do_check_eq(e.code, null);
|
||||
do_check_eq(e.errno, ERRNO_NETWORK);
|
||||
do_check_eq(e.error, ERROR_NETWORK);
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_test(function unsupportedMethod () {
|
||||
let client = new FxAccountsOAuthGrantClient(CLIENT_OPTIONS);
|
||||
|
||||
return client._createRequest("/", "PUT")
|
||||
.then(
|
||||
null,
|
||||
function (e) {
|
||||
do_check_eq(e.name, "FxAccountsOAuthGrantClientError");
|
||||
do_check_eq(e.code, ERROR_CODE_METHOD_NOT_ALLOWED);
|
||||
do_check_eq(e.errno, ERRNO_NETWORK);
|
||||
do_check_eq(e.error, ERROR_NETWORK);
|
||||
do_check_eq(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")
|
||||
.then(
|
||||
null,
|
||||
function (e) {
|
||||
do_check_eq(e.name, "FxAccountsOAuthGrantClientError");
|
||||
do_check_eq(e.code, null);
|
||||
do_check_eq(e.errno, ERRNO_NETWORK);
|
||||
do_check_eq(e.error, ERROR_NETWORK);
|
||||
do_check_eq(e.message, "Error: onComplete error");
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_test(function constructorTests() {
|
||||
validationHelper(undefined,
|
||||
"Error: Missing configuration options");
|
||||
|
||||
validationHelper({},
|
||||
"Error: Missing 'serverURL' parameter");
|
||||
|
||||
validationHelper({ serverURL: "http://example.com" },
|
||||
"Error: Missing 'client_id' parameter");
|
||||
|
||||
validationHelper({ client_id: "123ABC" },
|
||||
"Error: Missing 'serverURL' parameter");
|
||||
|
||||
validationHelper({ client_id: "123ABC", serverURL: "badUrl" },
|
||||
"Error: Invalid 'serverURL'");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function errorTests() {
|
||||
let error1 = new FxAccountsOAuthGrantClientError();
|
||||
do_check_eq(error1.name, "FxAccountsOAuthGrantClientError");
|
||||
do_check_eq(error1.code, null);
|
||||
do_check_eq(error1.errno, ERRNO_UNKNOWN_ERROR);
|
||||
do_check_eq(error1.error, ERROR_UNKNOWN);
|
||||
do_check_eq(error1.message, null);
|
||||
|
||||
let error2 = new FxAccountsOAuthGrantClientError({
|
||||
code: STATUS_SUCCESS,
|
||||
errno: 1,
|
||||
error: "Error",
|
||||
message: "Something",
|
||||
});
|
||||
let fields2 = error2._toStringFields();
|
||||
let statusCode = 1;
|
||||
|
||||
do_check_eq(error2.name, "FxAccountsOAuthGrantClientError");
|
||||
do_check_eq(error2.code, STATUS_SUCCESS);
|
||||
do_check_eq(error2.errno, statusCode);
|
||||
do_check_eq(error2.error, "Error");
|
||||
do_check_eq(error2.message, "Something");
|
||||
|
||||
do_check_eq(fields2.name, "FxAccountsOAuthGrantClientError");
|
||||
do_check_eq(fields2.code, STATUS_SUCCESS);
|
||||
do_check_eq(fields2.errno, statusCode);
|
||||
do_check_eq(fields2.error, "Error");
|
||||
do_check_eq(fields2.message, "Something");
|
||||
|
||||
do_check_true(error2.toString().indexOf("Something") >= 0);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick way to test the "FxAccountsOAuthGrantClient" constructor.
|
||||
*
|
||||
* @param {Object} options
|
||||
* FxAccountsOAuthGrantClient constructor options
|
||||
* @param {String} expected
|
||||
* Expected error message
|
||||
* @returns {*}
|
||||
*/
|
||||
function validationHelper(options, expected) {
|
||||
try {
|
||||
new FxAccountsOAuthGrantClient(options);
|
||||
} catch (e) {
|
||||
return do_check_eq(e.toString(), expected);
|
||||
}
|
||||
throw new Error("Validation helper error");
|
||||
}
|
|
@ -13,4 +13,5 @@ skip-if = appname == 'b2g' # login manager storage only used on desktop.
|
|||
skip-if = appname != 'b2g'
|
||||
reason = FxAccountsManager is only available for B2G for now
|
||||
[test_oauth_client.js]
|
||||
[test_oauth_grant_client.js]
|
||||
[test_profile_client.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче