Bug 1048530 - Create a Loop test server for exchanging an OAuth code for a token. r=vladikoff,ckarlof,mikedeboer
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Fri, 15 Aug 2014 16:52:05 -0700
changeset 221514 e9ae608b82e896cf4ce444e694a1ff66e009a2a6
parent 221513 4068cffc03fcc48aa87a75b06d4d681de3b88b30
child 221515 70b77e14d448386ec331a4dc3aff15e29b7c4f2d
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvladikoff, ckarlof, mikedeboer
bugs1048530
milestone34.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
Bug 1048530 - Create a Loop test server for exchanging an OAuth code for a token. r=vladikoff,ckarlof,mikedeboer
browser/components/loop/test/mochitest/browser_loop_fxa_server.js
browser/components/loop/test/mochitest/loop_fxa.sjs
--- a/browser/components/loop/test/mochitest/browser_loop_fxa_server.js
+++ b/browser/components/loop/test/mochitest/browser_loop_fxa_server.js
@@ -50,23 +50,78 @@ add_task(function* optional_setup_params
 });
 
 add_task(function* delete_setup_params() {
   yield promiseDeletedOAuthParams(BASE_URL);
   let request = yield promiseParams();
   is(Object.keys(request.response).length, 0, "Params should have been deleted");
 });
 
+// Begin /fxa-oauth/token tests
+
+add_task(function* token_request() {
+  let params = {
+    client_id: "my_client_id",
+    content_uri: "https://example.com/content/",
+    oauth_uri: "https://example.com/oauth/",
+    profile_uri: "https://example.com/profile/",
+    state: "my_state",
+  };
+  yield promiseOAuthParamsSetup(BASE_URL, params);
+  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.token_type, "bearer", "Check token_type");
+});
+
+add_task(function* token_request_invalid_state() {
+  let params = {
+    client_id: "my_client_id",
+    content_uri: "https://example.com/content/",
+    oauth_uri: "https://example.com/oauth/",
+    profile_uri: "https://example.com/profile/",
+    state: "my_invalid_state",
+  };
+  yield promiseOAuthParamsSetup(BASE_URL, params);
+  let request = yield promiseToken("my_code", "my_state");
+  ise(request.status, 400, "Check token response status");
+  ise(request.response, null, "Check token response body");
+});
+
+
+// Helper methods
+
 function promiseParams() {
   let deferred = Promise.defer();
   let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
               createInstance(Ci.nsIXMLHttpRequest);
   xhr.open("POST", BASE_URL + "/fxa-oauth/params", true);
   xhr.responseType = "json";
   xhr.addEventListener("load", () => {
     info("/fxa-oauth/params response:\n" + JSON.stringify(xhr.response, null, 4));
     deferred.resolve(xhr);
   });
-  xhr.addEventListener("error", error => deferred.reject(error));
+  xhr.addEventListener("error", deferred.reject);
   xhr.send();
 
   return deferred.promise;
 }
+
+function promiseToken(code, state) {
+  let deferred = Promise.defer();
+  let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
+              createInstance(Ci.nsIXMLHttpRequest);
+  xhr.open("POST", BASE_URL + "/fxa-oauth/token", true);
+  xhr.responseType = "json";
+  xhr.addEventListener("load", () => {
+    info("/fxa-oauth/token response:\n" + JSON.stringify(xhr.response, null, 4));
+    deferred.resolve(xhr);
+  });
+  xhr.addEventListener("error", deferred.reject);
+  let payload = {
+    code: code,
+    state: state,
+  };
+  xhr.send(JSON.stringify(payload, null, 4));
+
+  return deferred.promise;
+}
--- a/browser/components/loop/test/mochitest/loop_fxa.sjs
+++ b/browser/components/loop/test/mochitest/loop_fxa.sjs
@@ -4,27 +4,32 @@
 /**
  * This is a mock server that implements the FxA endpoints on the Loop server.
  */
 
 "use strict";
 
 const REQUIRED_PARAMS = ["client_id", "content_uri", "oauth_uri", "profile_uri", "state"];
 
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
 /**
  * Entry point for HTTP requests.
  */
 function handleRequest(request, response) {
   switch (request.queryString) {
     case "/setup_params":
       setup_params(request, response);
       return;
     case "/fxa-oauth/params":
       params(request, response);
       return;
+    case "/fxa-oauth/token":
+      token(request, response);
+      return;
   }
   response.setStatusLine(request.httpVersion, 404, "Not Found");
 }
 
 /**
  * POST /setup_params
  * DELETE /setup_params
  *
@@ -78,8 +83,36 @@ function params(request, response) {
     }
   }
 
   // Save the result so we have the effective `state` value.
   setSharedState("/fxa-oauth/params", JSON.stringify(params));
   response.setHeader("Content-Type", "application/json; charset=utf-8", false);
   response.write(JSON.stringify(params, null, 2));
 }
+
+/**
+ * POST /fxa-oauth/token
+ *
+ * Validate the state parameter with the server session state and if it matches, exchange the code
+ * for an OAuth Token.
+ * Parameters: code & state as JSON in the POST body.
+ * Response: JSON containing an object of OAuth token information.
+ */
+function token(request, response) {
+  let params = JSON.parse(getSharedState("/fxa-oauth/params") || "{}");
+  let body = NetUtil.readInputStreamToString(request.bodyInputStream,
+                                             request.bodyInputStream.available());
+  let payload = JSON.parse(body);
+  if (!params.state || params.state !== payload.state) {
+    response.setStatusLine(request.httpVersion, 400, "State mismatch");
+    response.write("State mismatch");
+    return;
+  }
+
+  let tokenData = {
+    access_token: payload.code + "_access_token",
+    scopes: "",
+    token_type: "bearer",
+  };
+  response.setHeader("Content-Type", "application/json; charset=utf-8", false);
+  response.write(JSON.stringify(tokenData, null, 2));
+}