summaryrefslogtreecommitdiffstats
path: root/services/fxaccounts/tests/xpcshell
diff options
context:
space:
mode:
Diffstat (limited to 'services/fxaccounts/tests/xpcshell')
-rw-r--r--services/fxaccounts/tests/xpcshell/test_accounts.js4
-rw-r--r--services/fxaccounts/tests/xpcshell/test_keys.js122
-rw-r--r--services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js2
-rw-r--r--services/fxaccounts/tests/xpcshell/test_oauth_flow.js82
4 files changed, 203 insertions, 7 deletions
diff --git a/services/fxaccounts/tests/xpcshell/test_accounts.js b/services/fxaccounts/tests/xpcshell/test_accounts.js
index 239adb206f..1dcbb2d2f2 100644
--- a/services/fxaccounts/tests/xpcshell/test_accounts.js
+++ b/services/fxaccounts/tests/xpcshell/test_accounts.js
@@ -44,7 +44,7 @@ initTestLogging("Trace");
var log = Log.repository.getLogger("Services.FxAccounts.test");
log.level = Log.Level.Debug;
-// See verbose logging from FxAccounts.jsm and jwcrypto.jsm.
+// See verbose logging from FxAccounts.sys.mjs and jwcrypto.sys.mjs.
Services.prefs.setStringPref("identity.fxaccounts.loglevel", "Trace");
Log.repository.getLogger("FirefoxAccounts").level = Log.Level.Trace;
Services.prefs.setStringPref("services.crypto.jwcrypto.log.level", "Debug");
@@ -217,7 +217,7 @@ function MockFxAccounts() {
},
});
// and for convenience so we don't have to touch as many lines in this test
- // when we refactored FxAccounts.jsm :)
+ // when we refactored FxAccounts.sys.mjs :)
result.setSignedInUser = function (creds) {
return result._internal.setSignedInUser(creds);
};
diff --git a/services/fxaccounts/tests/xpcshell/test_keys.js b/services/fxaccounts/tests/xpcshell/test_keys.js
index 6e650a1609..9a25ca90f3 100644
--- a/services/fxaccounts/tests/xpcshell/test_keys.js
+++ b/services/fxaccounts/tests/xpcshell/test_keys.js
@@ -99,6 +99,128 @@ add_task(async function test_derive_multiple_keys_at_once() {
});
});
+add_task(function test_check_valid_scoped_keys() {
+ const keys = new FxAccountsKeys(null);
+ add_task(function test_missing_key_data() {
+ const scopedKeys = {
+ "https://identity.mozilla.com/apps/oldsync": {
+ kty: "oct",
+ kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw",
+ scope: "https://identity.mozilla.com/apps/oldsync",
+ },
+ };
+ Assert.equal(keys.validScopedKeys(scopedKeys), false);
+ });
+ add_task(function test_unexpected_scope() {
+ const scopedKeys = {
+ "https://identity.mozilla.com/apps/oldsync": {
+ kty: "oct",
+ kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw",
+ k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang",
+ scope: "UnexpectedScope",
+ },
+ };
+ Assert.equal(keys.validScopedKeys(scopedKeys), false);
+ });
+ add_task(function test_not_oct_key() {
+ const scopedKeys = {
+ "https://identity.mozilla.com/apps/oldsync": {
+ // Should be "oct"!
+ kty: "EC",
+ kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw",
+ k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang",
+ scope: "https://identity.mozilla.com/apps/oldsync",
+ },
+ };
+ Assert.equal(keys.validScopedKeys(scopedKeys), false);
+ });
+ add_task(function test_invalid_kid_not_timestamp() {
+ const scopedKeys = {
+ "https://identity.mozilla.com/apps/oldsync": {
+ kty: "oct",
+ // Does not have the timestamp!
+ kid: "IqQv4onc7VcVE1kTQkyyOw",
+ k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang",
+ scope: "https://identity.mozilla.com/apps/oldsync",
+ },
+ };
+ Assert.equal(keys.validScopedKeys(scopedKeys), false);
+ });
+ add_task(function test_invalid_kid_not_valid_timestamp() {
+ const scopedKeys = {
+ "https://identity.mozilla.com/apps/oldsync": {
+ kty: "oct",
+ // foo is not a valid timestamp!
+ kid: "foo-IqQv4onc7VcVE1kTQkyyOw",
+ k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang",
+ scope: "https://identity.mozilla.com/apps/oldsync",
+ },
+ };
+ Assert.equal(keys.validScopedKeys(scopedKeys), false);
+ });
+ add_task(function test_invalid_kid_not_b64_fingerprint() {
+ const scopedKeys = {
+ "https://identity.mozilla.com/apps/oldsync": {
+ kty: "oct",
+ // fingerprint not a valid base64 encoded string.
+ kid: "1510726318123-notvalidb64][",
+ k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang",
+ scope: "https://identity.mozilla.com/apps/oldsync",
+ },
+ };
+ Assert.equal(keys.validScopedKeys(scopedKeys), false);
+ });
+ add_task(function test_invalid_k_not_base64() {
+ const scopedKeys = {
+ "https://identity.mozilla.com/apps/oldsync": {
+ kty: "oct",
+ kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw",
+ k: "notavalidb64[]",
+ scope: "https://identity.mozilla.com/apps/oldsync",
+ },
+ };
+ Assert.equal(keys.validScopedKeys(scopedKeys), false);
+ });
+
+ add_task(function test_multiple_scoped_keys_one_invalid() {
+ const scopedKeys = {
+ // Valid
+ "https://identity.mozilla.com/apps/otherscope": {
+ kty: "oct",
+ kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw",
+ k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang",
+ scope: "https://identity.mozilla.com/apps/otherscope",
+ },
+ // Invalid
+ "https://identity.mozilla.com/apps/oldsync": {
+ kty: "oct",
+ kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw",
+ k: "notavalidb64[]",
+ scope: "https://identity.mozilla.com/apps/oldsync",
+ },
+ };
+ Assert.equal(keys.validScopedKeys(scopedKeys), false);
+ });
+
+ add_task(function test_valid_scopedkeys() {
+ const scopedKeys = {
+ "https://identity.mozilla.com/apps/oldsync": {
+ kty: "oct",
+ kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw",
+ k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang",
+ scope: "https://identity.mozilla.com/apps/oldsync",
+ },
+ "https://identity.mozilla.com/apps/otherscope": {
+ kty: "oct",
+ kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw",
+ k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang",
+ scope: "https://identity.mozilla.com/apps/otherscope",
+ },
+ };
+ Assert.equal(keys.validScopedKeys(scopedKeys), true);
+ });
+});
+
add_task(async function test_rejects_bad_scoped_key_data() {
const keys = new FxAccountsKeys(null);
const uid = "aeaa1725c7a24ff983c6295725d5fc9b";
diff --git a/services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js b/services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js
index 5b80035418..1fdfecca61 100644
--- a/services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js
+++ b/services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js
@@ -5,7 +5,7 @@
// Tests for FxAccounts, storage and the master password.
-// See verbose logging from FxAccounts.jsm
+// See verbose logging from FxAccounts.sys.mjs
Services.prefs.setStringPref("identity.fxaccounts.loglevel", "Trace");
const { FxAccounts } = ChromeUtils.importESModule(
diff --git a/services/fxaccounts/tests/xpcshell/test_oauth_flow.js b/services/fxaccounts/tests/xpcshell/test_oauth_flow.js
index ef5102ae17..a5f53b6100 100644
--- a/services/fxaccounts/tests/xpcshell/test_oauth_flow.js
+++ b/services/fxaccounts/tests/xpcshell/test_oauth_flow.js
@@ -8,6 +8,7 @@
const {
FxAccountsOAuth,
ERROR_INVALID_SCOPES,
+ ERROR_INVALID_SCOPED_KEYS,
ERROR_INVALID_STATE,
ERROR_SYNC_SCOPE_NOT_GRANTED,
ERROR_NO_KEYS_JWE,
@@ -22,6 +23,7 @@ const { SCOPE_PROFILE, FX_OAUTH_CLIENT_ID } = ChromeUtils.importESModule(
ChromeUtils.defineESModuleGetters(this, {
jwcrypto: "resource://services-crypto/jwcrypto.sys.mjs",
+ FxAccountsKeys: "resource://gre/modules/FxAccountsKeys.sys.mjs",
});
initTestLogging("Trace");
@@ -159,17 +161,21 @@ add_task(function test_complete_oauth_flow() {
const oauthCode = "fake oauth code";
const sessionToken = "01abcef12";
const plainTextScopedKeys = {
- kid: "fake key id",
- k: "fake key",
- kty: "oct",
+ "https://identity.mozilla.com/apps/oldsync": {
+ kty: "oct",
+ kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw",
+ k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang",
+ scope: "https://identity.mozilla.com/apps/oldsync",
+ },
};
const fakeAccessToken = "fake access token";
const fakeRefreshToken = "fake refresh token";
// Then, we initialize a fake http client, we'll add our fake oauthToken call
// once we have started the oauth flow (so we have the public keys!)
const fxaClient = {};
+ const fxaKeys = new FxAccountsKeys(null);
// Then, we initialize our oauth object with the given client and begin a new flow
- const oauth = new FxAccountsOAuth(fxaClient);
+ const oauth = new FxAccountsOAuth(fxaClient, fxaKeys);
const queryParams = await oauth.beginOAuthFlow(scopes);
// Now that we have the public keys in `keys_jwk`, we use it to generate a JWE
// representing our scoped keys
@@ -271,4 +277,72 @@ add_task(function test_complete_oauth_flow() {
// Finally, we verify that all stored flows were cleared
Assert.equal(oauth.numOfFlows(), 0);
});
+ add_task(async function test_complete_oauth_invalid_scoped_keys() {
+ // First, we initialize some fake values we would typically get
+ // from outside our system
+ const scopes = [SCOPE_PROFILE, SCOPE_OLD_SYNC];
+ const oauthCode = "fake oauth code";
+ const sessionToken = "01abcef12";
+ const invalidScopedKeys = {
+ "https://identity.mozilla.com/apps/oldsync": {
+ // ====== This is an invalid key type! Should be "oct", so we will raise an error once we realize
+ kty: "EC",
+ kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw",
+ k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang",
+ scope: "https://identity.mozilla.com/apps/oldsync",
+ },
+ };
+ const fakeAccessToken = "fake access token";
+ const fakeRefreshToken = "fake refresh token";
+ // Then, we initialize a fake http client, we'll add our fake oauthToken call
+ // once we have started the oauth flow (so we have the public keys!)
+ const fxaClient = {};
+ const fxaKeys = new FxAccountsKeys(null);
+ // Then, we initialize our oauth object with the given client and begin a new flow
+ const oauth = new FxAccountsOAuth(fxaClient, fxaKeys);
+ const queryParams = await oauth.beginOAuthFlow(scopes);
+ // Now that we have the public keys in `keys_jwk`, we use it to generate a JWE
+ // representing our scoped keys
+ const keysJwk = queryParams.keys_jwk;
+ const decodedKeysJwk = JSON.parse(
+ new TextDecoder().decode(
+ ChromeUtils.base64URLDecode(keysJwk, { padding: "reject" })
+ )
+ );
+ delete decodedKeysJwk.key_ops;
+ const jwe = await jwcrypto.generateJWE(
+ decodedKeysJwk,
+ new TextEncoder().encode(JSON.stringify(invalidScopedKeys))
+ );
+ // We also grab the stored PKCE verifier that the oauth object stored internally
+ // to verify that we correctly send it as a part of our HTTP request
+ const storedVerifier = oauth.getFlow(queryParams.state).verifier;
+
+ // Now we initialize our mock of the HTTP request, it verifies we passed in all the correct
+ // parameters and returns what we'd expect a healthy HTTP Response would look like
+ fxaClient.oauthToken = (sessionTokenHex, code, verifier, clientId) => {
+ Assert.equal(sessionTokenHex, sessionToken);
+ Assert.equal(code, oauthCode);
+ Assert.equal(verifier, storedVerifier);
+ Assert.equal(clientId, queryParams.client_id);
+ const response = {
+ access_token: fakeAccessToken,
+ refresh_token: fakeRefreshToken,
+ scope: scopes.join(" "),
+ keys_jwe: jwe,
+ };
+ return Promise.resolve(response);
+ };
+
+ // Then, we call the completeOAuthFlow function, and get back our access token,
+ // refresh token and scopedKeys
+ try {
+ await oauth.completeOAuthFlow(sessionToken, oauthCode, queryParams.state);
+ Assert.fail(
+ "Should have thrown an error because the scoped keys are not valid"
+ );
+ } catch (err) {
+ Assert.equal(err.message, ERROR_INVALID_SCOPED_KEYS);
+ }
+ });
});