summaryrefslogtreecommitdiffstats
path: root/services/fxaccounts/FxAccounts.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'services/fxaccounts/FxAccounts.sys.mjs')
-rw-r--r--services/fxaccounts/FxAccounts.sys.mjs52
1 files changed, 43 insertions, 9 deletions
diff --git a/services/fxaccounts/FxAccounts.sys.mjs b/services/fxaccounts/FxAccounts.sys.mjs
index 18169c6b2d..790a6195f8 100644
--- a/services/fxaccounts/FxAccounts.sys.mjs
+++ b/services/fxaccounts/FxAccounts.sys.mjs
@@ -65,6 +65,8 @@ XPCOMUtils.defineLazyPreferenceGetter(
true
);
+export const ERROR_INVALID_ACCOUNT_STATE = "ERROR_INVALID_ACCOUNT_STATE";
+
// An AccountState object holds all state related to one specific account.
// It is considered "private" to the FxAccounts modules.
// Only one AccountState is ever "current" in the FxAccountsInternal object -
@@ -170,7 +172,7 @@ AccountState.prototype = {
delete updatedFields.uid;
}
if (!this.isCurrent) {
- return Promise.reject(new Error("Another user has signed in"));
+ return Promise.reject(new Error(ERROR_INVALID_ACCOUNT_STATE));
}
return this.storageManager.updateAccountData(updatedFields);
},
@@ -179,11 +181,11 @@ AccountState.prototype = {
if (!this.isCurrent) {
log.info(
"An accountState promise was resolved, but was actually rejected" +
- " due to a different user being signed in. Originally resolved" +
- " with",
+ " due to the account state changing. This can happen if a new account signed in, or" +
+ " the account was signed out. Originally resolved with, ",
result
);
- return Promise.reject(new Error("A different user signed in"));
+ return Promise.reject(new Error(ERROR_INVALID_ACCOUNT_STATE));
}
return Promise.resolve(result);
},
@@ -195,12 +197,13 @@ AccountState.prototype = {
// problems.
if (!this.isCurrent) {
log.info(
- "An accountState promise was rejected, but we are ignoring that " +
- "reason and rejecting it due to a different user being signed in. " +
- "Originally rejected with",
+ "An accountState promise was rejected, but we are ignoring that" +
+ " reason and rejecting it due to the account state changing. This can happen if" +
+ " a different account signed in or the account was signed out" +
+ " originally resolved with, ",
error
);
- return Promise.reject(new Error("A different user signed in"));
+ return Promise.reject(new Error(ERROR_INVALID_ACCOUNT_STATE));
}
return Promise.reject(error);
},
@@ -215,7 +218,7 @@ AccountState.prototype = {
// A preamble for the cache helpers...
_cachePreamble() {
if (!this.isCurrent) {
- throw new Error("Another user has signed in");
+ throw new Error(ERROR_INVALID_ACCOUNT_STATE);
}
},
@@ -466,6 +469,37 @@ export class FxAccounts {
}
}
+ /** Gets both the OAuth token and the users scoped keys for that token
+ * and verifies that both operations were done for the same user,
+ * preventing race conditions where a caller
+ * can get the key for one user, and the id of another if the user
+ * is rapidly switching between accounts
+ *
+ * @param options
+ * {
+ * scope: string the oauth scope being requested. This must
+ * be a scope with an associated key, otherwise an error
+ * will be thrown that the key is not available.
+ * ttl: (number) OAuth token TTL in seconds
+ * }
+ *
+ * @return Promise.<Object | Error>
+ * The promise resolve to both the access token being requested, and the scoped key
+ * {
+ * token: (string) access token
+ * key: (object) the scoped key object
+ * }
+ * The promise can reject, with one of the errors `getOAuthToken`, `FxAccountKeys.getKeyForScope`, or
+ * error if the user changed in-between operations
+ */
+ getOAuthTokenAndKey(options = {}) {
+ return this._withCurrentAccountState(async () => {
+ const key = await this.keys.getKeyForScope(options.scope);
+ const token = await this.getOAuthToken(options);
+ return { token, key };
+ });
+ }
+
/**
* Remove an OAuth token from the token cache. Callers should call this
* after they determine a token is invalid, so a new token will be fetched