Bug 1047617 - Register logged in user with the Loop server. r=ckarlof,vladikoff,markh

This commit is contained in:
Matthew Noorenberghe 2014-08-25 22:11:35 -04:00
parent 1c4d55a40b
commit 5f23ae520a
4 changed files with 182 additions and 8 deletions

View File

@ -60,6 +60,7 @@ let gLocalizedStrings = null;
let gInitializeTimer = null;
let gFxAOAuthClientPromise = null;
let gFxAOAuthClient = null;
let gFxAOAuthTokenData = null;
/**
* Internal helper methods and state
@ -195,7 +196,10 @@ let MozLoopServiceInternal = {
2 * 32, true);
}
return gHawkClient.request(path, method, credentials, payloadObj);
return gHawkClient.request(path, method, credentials, payloadObj).catch(error => {
console.error("Loop hawkRequest error:", error);
throw error;
});
},
/**
@ -492,7 +496,7 @@ let MozLoopServiceInternal = {
},
error => {
gFxAOAuthClientPromise = null;
return error;
throw error;
}
);
@ -500,7 +504,9 @@ let MozLoopServiceInternal = {
}),
/**
* Params => web flow => code
* Get the OAuth client and do the authorization web flow to get an OAuth code.
*
* @return {Promise}
*/
promiseFxAOAuthAuthorization: function() {
let deferred = Promise.defer();
@ -510,13 +516,38 @@ let MozLoopServiceInternal = {
client.launchWebFlow();
},
error => {
Cu.reportError(error);
console.error(error);
deferred.reject(error);
}
);
return deferred.promise;
},
/**
* Get the OAuth token using the OAuth code and state.
*
* The caller should approperiately handle 4xx errors (which should lead to a logout)
* and 5xx or connectivity issues with messaging to try again later.
*
* @param {String} code
* @param {String} state
*
* @return {Promise} resolving with OAuth token data.
*/
promiseFxAOAuthToken: function(code, state) {
if (!code || !state) {
throw new Error("promiseFxAOAuthToken: code and state are required.");
}
let payload = {
code: code,
state: state,
};
return this.hawkRequest("/fxa-oauth/token", "POST", payload).then(response => {
return JSON.parse(response.body);
});
},
/**
* Called once gFxAOAuthClient fires onComplete.
*
@ -558,8 +589,14 @@ this.MozLoopService = {
return MozLoopServiceInternal;
},
get gFxAOAuthTokenData() {
return gFxAOAuthTokenData;
},
resetFxA: function() {
gFxAOAuthClientPromise = null;
gFxAOAuthClient = null;
gFxAOAuthTokenData = null;
},
#endif
@ -740,6 +777,13 @@ this.MozLoopService = {
logInToFxA: function() {
return MozLoopServiceInternal.promiseFxAOAuthAuthorization().then(response => {
return MozLoopServiceInternal.promiseFxAOAuthToken(response.code, response.state);
}).then(tokenData => {
gFxAOAuthTokenData = tokenData;
return tokenData;
},
error => {
gFxAOAuthTokenData = null;
throw error;
});
},

View File

@ -90,3 +90,122 @@ add_task(function* invalidState() {
ok(error, "The login promise should be rejected due to invalid state");
});
});
add_task(function* basicRegistration() {
MozLoopService.resetFxA();
let params = {
client_id: "client_id",
content_uri: BASE_URL + "/content",
oauth_uri: BASE_URL + "/oauth",
profile_uri: BASE_URL + "/profile",
state: "state",
};
yield promiseOAuthParamsSetup(BASE_URL, params);
let tokenData = yield MozLoopService.internal.promiseFxAOAuthToken("code1", "state");
is(tokenData.access_token, "code1_access_token", "Check access_token");
is(tokenData.scope, "profile", "Check scope");
is(tokenData.token_type, "bearer", "Check token_type");
});
add_task(function* registrationWithInvalidState() {
MozLoopService.resetFxA();
let params = {
client_id: "client_id",
content_uri: BASE_URL + "/content",
oauth_uri: BASE_URL + "/oauth",
profile_uri: BASE_URL + "/profile",
state: "invalid_state",
};
yield promiseOAuthParamsSetup(BASE_URL, params);
let tokenPromise = MozLoopService.internal.promiseFxAOAuthToken("code1", "state");
yield tokenPromise.then(body => {
ok(false, "Promise should have rejected");
},
error => {
is(error.code, 400, "Check error code");
});
});
add_task(function* registrationWith401() {
MozLoopService.resetFxA();
let params = {
client_id: "client_id",
content_uri: BASE_URL + "/content",
oauth_uri: BASE_URL + "/oauth",
profile_uri: BASE_URL + "/profile",
state: "state",
test_error: "token_401",
};
yield promiseOAuthParamsSetup(BASE_URL, params);
let tokenPromise = MozLoopService.internal.promiseFxAOAuthToken("code1", "state");
yield tokenPromise.then(body => {
ok(false, "Promise should have rejected");
},
error => {
is(error.code, 401, "Check error code");
});
});
add_task(function* basicAuthorizationAndRegistration() {
MozLoopService.resetFxA();
let params = {
client_id: "client_id",
content_uri: BASE_URL + "/content",
oauth_uri: BASE_URL + "/oauth",
profile_uri: BASE_URL + "/profile",
state: "state",
};
yield promiseOAuthParamsSetup(BASE_URL, params);
let tokenData = yield MozLoopService.logInToFxA();
ise(tokenData.access_token, "code1_access_token", "Check access_token");
ise(tokenData.scope, "profile", "Check scope");
ise(tokenData.token_type, "bearer", "Check token_type");
});
add_task(function* loginWithParams401() {
MozLoopService.resetFxA();
let params = {
client_id: "client_id",
content_uri: BASE_URL + "/content",
oauth_uri: BASE_URL + "/oauth",
profile_uri: BASE_URL + "/profile",
state: "state",
test_error: "params_401",
};
yield promiseOAuthParamsSetup(BASE_URL, params);
let loginPromise = MozLoopService.logInToFxA();
yield loginPromise.then(tokenData => {
ok(false, "Promise should have rejected");
},
error => {
ise(error.code, 401, "Check error code");
ise(MozLoopService.gFxAOAuthTokenData, null, "Check there is no saved token data");
});
});
add_task(function* loginWithRegistration401() {
MozLoopService.resetFxA();
let params = {
client_id: "client_id",
content_uri: BASE_URL + "/content",
oauth_uri: BASE_URL + "/oauth",
profile_uri: BASE_URL + "/profile",
state: "state",
test_error: "token_401",
};
yield promiseOAuthParamsSetup(BASE_URL, params);
let loginPromise = MozLoopService.logInToFxA();
yield loginPromise.then(tokenData => {
ok(false, "Promise should have rejected");
},
error => {
ise(error.code, 401, "Check error code");
ise(MozLoopService.gFxAOAuthTokenData, null, "Check there is no saved token data");
});
});

View File

@ -69,7 +69,7 @@ add_task(function* token_request() {
let request = yield promiseToken("my_code", params.state);
ise(request.status, 200, "Check token response status");
ise(request.response.access_token, "my_code_access_token", "Check access_token");
ise(request.response.scopes, "", "Check scopes");
ise(request.response.scope, "profile", "Check scope");
ise(request.response.token_type, "bearer", "Check token_type");
});

View File

@ -76,10 +76,14 @@ function params(request, response) {
return;
}
let origin = request.scheme + "://" + request.host + ":" + request.port;
let params = JSON.parse(getSharedState("/fxa-oauth/params") || "{}");
if (params.test_error && params.test_error == "params_401") {
response.setStatusLine(request.httpVersion, 401, "Unauthorized");
response.write("401 Unauthorized");
return;
}
// Warn if required parameters are missing.
for (let paramName of REQUIRED_PARAMS) {
if (!(paramName in params)) {
@ -113,6 +117,13 @@ function oauth_authorization(request, response) {
*/
function token(request, response) {
let params = JSON.parse(getSharedState("/fxa-oauth/params") || "{}");
if (params.test_error && params.test_error == "token_401") {
response.setStatusLine(request.httpVersion, 401, "Unauthorized");
response.write("401 Unauthorized");
return;
}
let body = NetUtil.readInputStreamToString(request.bodyInputStream,
request.bodyInputStream.available());
let payload = JSON.parse(body);
@ -124,7 +135,7 @@ function token(request, response) {
let tokenData = {
access_token: payload.code + "_access_token",
scopes: "",
scope: "profile",
token_type: "bearer",
};
response.setHeader("Content-Type", "application/json; charset=utf-8", false);