зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1189148 - Attempt to refresh the FXA certificate when a 401 is received from the token server. r=markh
MozReview-Commit-ID: B7p6gJ8EJCQ --HG-- extra : transplant_source : %00%5C%9C%E9%93%CE%95%E3%94%09ba%25g%28%E4%E9%B4r%8C
This commit is contained in:
Родитель
163ca97aa0
Коммит
190873347a
|
@ -46,6 +46,7 @@ var publicProperties = [
|
|||
"getSignedInUser",
|
||||
"getOAuthToken",
|
||||
"getSignedInUserProfile",
|
||||
"invalidateCertificate",
|
||||
"loadAndPoll",
|
||||
"localtimeOffsetMsec",
|
||||
"now",
|
||||
|
@ -598,6 +599,14 @@ FxAccountsInternal.prototype = {
|
|||
).then(result => currentState.resolve(result));
|
||||
},
|
||||
|
||||
/**
|
||||
* Invalidate the FxA certificate, so that it will be refreshed from the server
|
||||
* the next time it is needed.
|
||||
*/
|
||||
invalidateCertificate() {
|
||||
return this.currentAccountState.updateUserAccountData({ cert: null });
|
||||
},
|
||||
|
||||
getDeviceId() {
|
||||
return this.currentAccountState.getUserAccountData()
|
||||
.then(data => {
|
||||
|
|
|
@ -569,7 +569,7 @@ this.BrowserIDManager.prototype = {
|
|||
);
|
||||
}
|
||||
|
||||
let getToken = (tokenServerURI, assertion) => {
|
||||
let getToken = assertion => {
|
||||
log.debug("Getting a token");
|
||||
let deferred = Promise.defer();
|
||||
let cb = function (err, token) {
|
||||
|
@ -597,7 +597,18 @@ this.BrowserIDManager.prototype = {
|
|||
return fxa.whenVerified(this._signedInUser)
|
||||
.then(() => maybeFetchKeys())
|
||||
.then(() => getAssertion())
|
||||
.then(assertion => getToken(tokenServerURI, assertion))
|
||||
.then(assertion => getToken(assertion))
|
||||
.catch(err => {
|
||||
// If we get a 401 fetching the token it may be that our certificate
|
||||
// needs to be regenerated.
|
||||
if (!err.response || err.response.status !== 401) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
log.warn("Token server returned 401, refreshing certificate and retrying token fetch");
|
||||
return fxa.invalidateCertificate()
|
||||
.then(() => getAssertion())
|
||||
.then(assertion => getToken(assertion))
|
||||
})
|
||||
.then(token => {
|
||||
// TODO: Make it be only 80% of the duration, so refresh the token
|
||||
// before it actually expires. This is to avoid sync storage errors
|
||||
|
|
|
@ -16,6 +16,7 @@ Cu.import("resource://gre/modules/FxAccountsCommon.js");
|
|||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/status.js");
|
||||
Cu.import("resource://services-sync/constants.js");
|
||||
Cu.import("resource://services-common/tokenserverclient.js");
|
||||
|
||||
const SECOND_MS = 1000;
|
||||
const MINUTE_MS = SECOND_MS * 60;
|
||||
|
@ -436,6 +437,74 @@ add_task(function* test_getTokenErrors() {
|
|||
Assert.equal(Status.login, LOGIN_FAILED_NETWORK_ERROR, "login state is LOGIN_FAILED_NETWORK_ERROR");
|
||||
});
|
||||
|
||||
add_task(function* test_refreshCertificateOn401() {
|
||||
_("BrowserIDManager refreshes the FXA certificate after a 401.");
|
||||
var identityConfig = makeIdentityConfig();
|
||||
var browseridManager = new BrowserIDManager();
|
||||
// Use the real `_getAssertion` method that calls
|
||||
// `mockFxAClient.signCertificate`.
|
||||
let fxaInternal = makeFxAccountsInternalMock(identityConfig);
|
||||
delete fxaInternal._getAssertion;
|
||||
configureFxAccountIdentity(browseridManager, identityConfig, fxaInternal);
|
||||
browseridManager._fxaService.internal.initialize();
|
||||
|
||||
let getCertCount = 0;
|
||||
|
||||
let MockFxAccountsClient = function() {
|
||||
FxAccountsClient.apply(this);
|
||||
};
|
||||
MockFxAccountsClient.prototype = {
|
||||
__proto__: FxAccountsClient.prototype,
|
||||
signCertificate() {
|
||||
++getCertCount;
|
||||
}
|
||||
};
|
||||
|
||||
let mockFxAClient = new MockFxAccountsClient();
|
||||
browseridManager._fxaService.internal._fxAccountsClient = mockFxAClient;
|
||||
|
||||
let didReturn401 = false;
|
||||
let didReturn200 = false;
|
||||
let mockTSC = mockTokenServer(() => {
|
||||
if (getCertCount <= 1) {
|
||||
didReturn401 = true;
|
||||
return {
|
||||
status: 401,
|
||||
headers: {"content-type": "application/json"},
|
||||
body: JSON.stringify({}),
|
||||
};
|
||||
} else {
|
||||
didReturn200 = true;
|
||||
return {
|
||||
status: 200,
|
||||
headers: {"content-type": "application/json"},
|
||||
body: JSON.stringify({
|
||||
id: "id",
|
||||
key: "key",
|
||||
api_endpoint: "http://example.com/",
|
||||
uid: "uid",
|
||||
duration: 300,
|
||||
})
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
browseridManager._tokenServerClient = mockTSC;
|
||||
|
||||
yield browseridManager.initializeWithCurrentIdentity();
|
||||
yield browseridManager.whenReadyToAuthenticate.promise;
|
||||
|
||||
do_check_eq(getCertCount, 2);
|
||||
do_check_true(didReturn401);
|
||||
do_check_true(didReturn200);
|
||||
do_check_true(browseridManager.account);
|
||||
do_check_true(browseridManager._token);
|
||||
do_check_true(browseridManager.hasValidToken());
|
||||
do_check_true(browseridManager.account);
|
||||
});
|
||||
|
||||
|
||||
|
||||
add_task(function* test_getTokenErrorWithRetry() {
|
||||
_("tokenserver sends an observer notification on various backoff headers.");
|
||||
|
||||
|
@ -793,3 +862,29 @@ function getTimestampDelta(hawkAuthHeader, now=Date.now()) {
|
|||
return Math.abs(getTimestamp(hawkAuthHeader) - now);
|
||||
}
|
||||
|
||||
function mockTokenServer(func) {
|
||||
let requestLog = Log.repository.getLogger("testing.mock-rest");
|
||||
if (!requestLog.appenders.length) { // might as well see what it says :)
|
||||
requestLog.addAppender(new Log.DumpAppender());
|
||||
requestLog.level = Log.Level.Trace;
|
||||
}
|
||||
function MockRESTRequest(url) {};
|
||||
MockRESTRequest.prototype = {
|
||||
_log: requestLog,
|
||||
setHeader: function() {},
|
||||
get: function(callback) {
|
||||
this.response = func();
|
||||
callback.call(this);
|
||||
}
|
||||
}
|
||||
// The mocked TokenServer client which will get the response.
|
||||
function MockTSC() { }
|
||||
MockTSC.prototype = new TokenServerClient();
|
||||
MockTSC.prototype.constructor = MockTSC;
|
||||
MockTSC.prototype.newRESTRequest = function(url) {
|
||||
return new MockRESTRequest(url);
|
||||
}
|
||||
// Arrange for the same observerPrefix as browserid_identity uses.
|
||||
MockTSC.prototype.observerPrefix = "weave:service";
|
||||
return new MockTSC();
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче