author | Fernando Jimenez <ferjmoreno@gmail.com> |
Thu, 22 Oct 2015 16:18:17 +0800 | |
changeset 268964 | 6b4a97d6111ac549d6a99f3ffa66934daa0aaca5 |
parent 268963 | 24653ad483de41bc8e84fad1b4e9f83d2bf4b38f |
child 268965 | 0ac4e2e6cb78db629e6d33d89fba5cc01dda945f |
push id | 29567 |
push user | kwierso@gmail.com |
push date | Thu, 22 Oct 2015 23:37:33 +0000 |
treeherder | mozilla-central@3888eea6aaf2 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | markh |
bugs | 1215509 |
milestone | 44.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/services/fxaccounts/FxAccountsClient.jsm +++ b/services/fxaccounts/FxAccountsClient.jsm @@ -24,16 +24,19 @@ const STATUS_CODE_TO_OTHER_ERRORS_LABEL 403: 2, 410: 3, 411: 4, 413: 5, 429: 6, 500: 7, }; +const SIGNIN = "/account/login"; +const SIGNUP = "/account/create"; + this.FxAccountsClient = function(host = HOST) { this.host = host; // The FxA auth server expects requests to certain endpoints to be authorized // using Hawk. this.hawk = new HawkClient(host); this.hawk.observerPrefix = "FxA:hawk"; @@ -63,43 +66,20 @@ this.FxAccountsClient.prototype = { * Not used by this module, but made available to the FxAccounts.jsm * that uses this client. */ now: function() { return this.hawk.now(); }, /** - * Create a new Firefox Account and authenticate + * Common code from signIn and signUp. * - * @param email - * The email address for the account (utf8) - * @param password - * The user's password - * @return Promise - * Returns a promise that resolves to an object: - * { - * uid: the user's unique ID (hex) - * sessionToken: a session token (hex) - * keyFetchToken: a key fetch token (hex) - * } - */ - signUp: function(email, password) { - return Credentials.setup(email, password).then((creds) => { - let data = { - email: creds.emailUTF8, - authPW: CommonUtils.bytesAsHex(creds.authPW), - }; - return this._request("/account/create", "POST", null, data); - }); - }, - - /** - * Authenticate and create a new session with the Firefox Account API server - * + * @param path + * Request URL path. Can be /account/create or /account/login * @param email * The email address for the account (utf8) * @param password * The user's password * @param [getKeys=false] * If set to true the keyFetchToken will be retrieved * @param [retryOK=true] * If capitalization of the email is wrong and retryOK is set to true, @@ -109,63 +89,117 @@ this.FxAccountsClient.prototype = { * { * authAt: authentication time for the session (seconds since epoch) * email: the primary email for this account * keyFetchToken: a key fetch token (hex) * sessionToken: a session token (hex) * uid: the user's unique ID (hex) * unwrapBKey: used to unwrap kB, derived locally from the * password (not revealed to the FxA server) - * verified: flag indicating verification status of the email + * verified (optional): flag indicating verification status of the + * email * } */ - signIn: function signIn(email, password, getKeys=false, retryOK=true) { + _createSession: function(path, email, password, getKeys=false, + retryOK=true) { return Credentials.setup(email, password).then((creds) => { let data = { authPW: CommonUtils.bytesAsHex(creds.authPW), email: creds.emailUTF8, }; let keys = getKeys ? "?keys=true" : ""; - return this._request("/account/login" + keys, "POST", null, data).then( + return this._request(path + keys, "POST", null, data).then( // Include the canonical capitalization of the email in the response so // the caller can set its signed-in user state accordingly. result => { result.email = data.email; result.unwrapBKey = CommonUtils.bytesAsHex(creds.unwrapBKey); return result; }, error => { - log.debug("signIn error: " + JSON.stringify(error)); + log.debug("Session creation failed", error); // If the user entered an email with different capitalization from // what's stored in the database (e.g., Greta.Garbo@gmail.COM as // opposed to greta.garbo@gmail.com), the server will respond with a // errno 120 (code 400) and the expected capitalization of the email. // We retry with this email exactly once. If successful, we use the // server's version of the email as the signed-in-user's email. This // is necessary because the email also serves as salt; so we must be // in agreement with the server on capitalization. // // API reference: // https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md if (ERRNO_INCORRECT_EMAIL_CASE === error.errno && retryOK) { if (!error.email) { log.error("Server returned errno 120 but did not provide email"); throw error; } - return this.signIn(error.email, password, getKeys, false); + return this._createSession(path, error.email, password, getKeys, + false); } throw error; } ); }); }, /** + * Create a new Firefox Account and authenticate + * + * @param email + * The email address for the account (utf8) + * @param password + * The user's password + * @param [getKeys=false] + * If set to true the keyFetchToken will be retrieved + * @return Promise + * Returns a promise that resolves to an object: + * { + * uid: the user's unique ID (hex) + * sessionToken: a session token (hex) + * keyFetchToken: a key fetch token (hex), + * unwrapBKey: used to unwrap kB, derived locally from the + * password (not revealed to the FxA server) + * } + */ + signUp: function(email, password, getKeys=false) { + return this._createSession(SIGNUP, email, password, getKeys, + false /* no retry */); + }, + + /** + * Authenticate and create a new session with the Firefox Account API server + * + * @param email + * The email address for the account (utf8) + * @param password + * The user's password + * @param [getKeys=false] + * If set to true the keyFetchToken will be retrieved + * @return Promise + * Returns a promise that resolves to an object: + * { + * authAt: authentication time for the session (seconds since epoch) + * email: the primary email for this account + * keyFetchToken: a key fetch token (hex) + * sessionToken: a session token (hex) + * uid: the user's unique ID (hex) + * unwrapBKey: used to unwrap kB, derived locally from the + * password (not revealed to the FxA server) + * verified: flag indicating verification status of the email + * } + */ + signIn: function signIn(email, password, getKeys=false) { + return this._createSession(SIGNIN, email, password, getKeys, + true /* retry */); + }, + + /** * Destroy the current session with the Firefox Account API server * * @param sessionTokenHex * The session token encoded in hex * @return Promise */ signOut: function (sessionTokenHex, options = {}) { let path = "/session/destroy";
--- a/services/fxaccounts/tests/xpcshell/test_client.js +++ b/services/fxaccounts/tests/xpcshell/test_client.js @@ -156,55 +156,83 @@ add_task(function test_backoffError() { let result = yield client._request("/duringDelayIShouldNotBeCalled", method); do_check_eq(client.backoffError, null); do_check_eq(result.working, "yes"); yield deferredStop(server); }); add_task(function test_signUp() { - let creationMessage = JSON.stringify({ + let creationMessage_noKey = JSON.stringify({ + uid: "uid", + sessionToken: "sessionToken" + }); + let creationMessage_withKey = JSON.stringify({ uid: "uid", sessionToken: "sessionToken", keyFetchToken: "keyFetchToken" }); let errorMessage = JSON.stringify({code: 400, errno: 101, error: "account exists"}); let created = false; let server = httpd_setup({ "/account/create": function(request, response) { let body = CommonUtils.readBytesFromInputStream(request.bodyInputStream); let jsonBody = JSON.parse(body); // https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol#wiki-test-vectors - do_check_eq(jsonBody.email, "andré@example.org"); + + if (created) { + // Error trying to create same account a second time + response.setStatusLine(request.httpVersion, 400, "Bad request"); + response.bodyOutputStream.write(errorMessage, errorMessage.length); + return; + } - if (!created) { + if (jsonBody.email == "andré@example.org") { + do_check_eq("", request._queryString); do_check_eq(jsonBody.authPW, "247b675ffb4c46310bc87e26d712153abe5e1c90ef00a4784594f97ef54f2375"); + + response.setStatusLine(request.httpVersion, 200, "OK"); + response.bodyOutputStream.write(creationMessage_noKey, + creationMessage_noKey.length); + return; + } + + if (jsonBody.email == "you@example.org") { + do_check_eq("keys=true", request._queryString); + do_check_eq(jsonBody.authPW, "e5c1cdfdaa5fcee06142db865b212cc8ba8abee2a27d639d42c139f006cdb930"); created = true; response.setStatusLine(request.httpVersion, 200, "OK"); - response.bodyOutputStream.write(creationMessage, creationMessage.length); + response.bodyOutputStream.write(creationMessage_withKey, + creationMessage_withKey.length); return; } - - // Error trying to create same account a second time - response.setStatusLine(request.httpVersion, 400, "Bad request"); - response.bodyOutputStream.write(errorMessage, errorMessage.length); - return; }, }); + // Try to create an account without retrieving optional keys. let client = new FxAccountsClient(server.baseURI); let result = yield client.signUp('andré@example.org', 'pässwörd'); do_check_eq("uid", result.uid); do_check_eq("sessionToken", result.sessionToken); - do_check_eq("keyFetchToken", result.keyFetchToken); + do_check_eq(undefined, result.keyFetchToken); + do_check_eq(result.unwrapBKey, + "de6a2648b78284fcb9ffa81ba95803309cfba7af583c01a8a1a63e567234dd28"); - // Try to create account again. Triggers error path. + // Try to create an account retrieving optional keys. + result = yield client.signUp('you@example.org', 'pässwörd', true); + do_check_eq("uid", result.uid); + do_check_eq("sessionToken", result.sessionToken); + do_check_eq("keyFetchToken", result.keyFetchToken); + do_check_eq(result.unwrapBKey, + "f589225b609e56075d76eb74f771ff9ab18a4dc0e901e131ba8f984c7fb0ca8c"); + + // Try to create an existing account. Triggers error path. try { result = yield client.signUp('andré@example.org', 'pässwörd'); do_throw("Expected to catch an exception"); } catch(expectedError) { do_check_eq(101, expectedError.errno); } yield deferredStop(server); @@ -285,25 +313,16 @@ add_task(function test_signIn() { // Retry due to wrong email capitalization result = yield client.signIn('You@example.com', 'bigsecret', true); do_check_eq(FAKE_SESSION_TOKEN, result.sessionToken); do_check_eq(result.unwrapBKey, "65970516211062112e955d6420bebe020269d6b6a91ebd288319fc8d0cb49624"); do_check_eq("keyFetchToken", result.keyFetchToken); - // Don't retry due to wrong email capitalization - try { - let result = yield client.signIn('You@example.com', 'bigsecret', true, false); - do_throw("Expected to catch an exception"); - } catch (expectedError) { - do_check_eq(120, expectedError.errno); - do_check_eq("you@example.com", expectedError.email); - } - // Trigger error path try { result = yield client.signIn("yøü@bad.example.org", "nofear"); do_throw("Expected to catch an exception"); } catch (expectedError) { do_check_eq(102, expectedError.errno); }
--- a/services/sync/tps/extensions/tps/resource/auth/fxaccounts.jsm +++ b/services/sync/tps/extensions/tps/resource/auth/fxaccounts.jsm @@ -65,17 +65,17 @@ var Authentication = { let cb = Async.makeSpinningCallback(); Logger.AssertTrue(account["username"], "Username has been found"); Logger.AssertTrue(account["password"], "Password has been found"); Logger.logInfo("Login user: " + account["username"] + '\n'); let client = new FxAccountsClient(); - client.signIn(account["username"], account["password"], true).then(credentials => { + client.signIn(account["username"], account["password"]).then(credentials => { return fxAccounts.setSignedInUser(credentials); }).then(() => { cb(null, true); }, error => { cb(error, false); }); try {