Bug 1147473 - Expose Firefox Account debug information from Settings activity. r=rnewman, a=readinglist
authorNick Alexander <nalexander@mozilla.com>
Tue, 24 Mar 2015 22:10:05 -0700
changeset 258208 ac9b83aca21f
parent 258207 c27964aaa4c5
child 258209 0aedf96a7cdc
push id4620
push userrnewman@mozilla.com
push date2015-04-02 16:21 +0000
treeherdermozilla-beta@27f61020a9e4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrnewman, readinglist
bugs1147473, 1142596
milestone38.0
Bug 1147473 - Expose Firefox Account debug information from Settings activity. r=rnewman, a=readinglist ======== https://github.com/mozilla-services/android-sync/commit/afcbbba32cdf290afc26e89a7db8c739f0216d5e Author: Nick Alexander <nalexander@mozilla.com> Bug 1147473 - Part 2: Make it possible to toggle Firefox Accounts debug / PII logging at runtime. Tap the email address in the Firefox Account settings activity 5 times. Only available when: !defined(MOZILLA_OFFICIAL) || defined(NIGHTLY_BUILD) || defined(MOZ_DEBUG) ======== https://github.com/mozilla-services/android-sync/commit/261f912d1267d52ffb0268d2a83c7870caad8457 Author: Nick Alexander <nalexander@mozilla.com> Bug 1147473 - Part 1: Add additional (dangerous!) debug commands. These make it easier to test oauth token authorization, especially using stage servers. ======== https://github.com/mozilla-services/android-sync/commit/c1509baa7736e95c4bda9ea972dd407ca5966536 Author: Nick Alexander <nalexander@mozilla.com> Date: Tue Mar 24 22:06:43 2015 -0700 Bug 1142596 - Pre: Move constants around. ======== https://github.com/mozilla-services/android-sync/commit/2c676500c32ae11e06fa65fd0aa9c05e13d3fd79 Author: Nick Alexander <nalexander@mozilla.com> Date: Thu Mar 26 11:28:22 2015 -0700 Bug 1142596 - Pre: Make it easier to construct intermediate states.
mobile/android/base/fxa/FxAccountConstants.java
mobile/android/base/fxa/activities/FxAccountStatusFragment.java
mobile/android/base/fxa/authenticator/AndroidFxAccount.java
mobile/android/base/fxa/login/Cohabiting.java
mobile/android/base/fxa/login/Married.java
mobile/android/base/reading/ReadingListConstants.java
mobile/android/base/reading/ReadingListSyncAdapter.java
mobile/android/base/resources/xml/fxaccount_status_prefscreen.xml
--- a/mobile/android/base/fxa/FxAccountConstants.java
+++ b/mobile/android/base/fxa/FxAccountConstants.java
@@ -5,21 +5,26 @@
 package org.mozilla.gecko.fxa;
 
 import org.mozilla.gecko.AppConstants;
 
 public class FxAccountConstants {
   public static final String GLOBAL_LOG_TAG = "FxAccounts";
   public static final String ACCOUNT_TYPE = AppConstants.MOZ_ANDROID_SHARED_FXACCOUNT_TYPE;
 
+  // Must be a client ID allocated with "canGrant" privileges!
+  public static final String OAUTH_CLIENT_ID_FENNEC = "3332a18d142636cb";
+
   public static final String DEFAULT_AUTH_SERVER_ENDPOINT = "https://api.accounts.firefox.com/v1";
   public static final String DEFAULT_TOKEN_SERVER_ENDPOINT = "https://token.services.mozilla.com/1.0/sync/1.5";
+  public static final String DEFAULT_OAUTH_SERVER_ENDPOINT = "https://oauth.accounts.firefox.com/v1";
 
-  public static final String STAGE_AUTH_SERVER_ENDPOINT = "https://api-accounts.stage.mozaws.net/v1";
-  public static final String STAGE_TOKEN_SERVER_ENDPOINT = "https://token.stage.mozaws.net/1.0/sync/1.5";
+  public static final String STAGE_AUTH_SERVER_ENDPOINT = "https://stable.dev.lcip.org/auth/v1";
+  public static final String STAGE_TOKEN_SERVER_ENDPOINT = "https://stable.dev.lcip.org/syncserver/token/1.0/sync/1.5";
+  public static final String STAGE_OAUTH_SERVER_ENDPOINT = "https://oauth-stable.dev.lcip.org/v1";
 
   // You must be at least 13 years old, on the day of creation, to create a Firefox Account.
   public static final int MINIMUM_AGE_TO_CREATE_AN_ACCOUNT = 13;
 
   // You must wait 15 minutes after failing an age check before trying to create a different account.
   public static final long MINIMUM_TIME_TO_WAIT_AFTER_AGE_CHECK_FAILED_IN_MILLISECONDS = 15 * 60 * 1000;
 
   public static final String USER_AGENT = "Firefox-Android-FxAccounts/" + AppConstants.MOZ_APP_VERSION + " (" + AppConstants.MOZ_APP_DISPLAYNAME + ")";
--- a/mobile/android/base/fxa/activities/FxAccountStatusFragment.java
+++ b/mobile/android/base/fxa/activities/FxAccountStatusFragment.java
@@ -1,77 +1,81 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.fxa.activities;
 
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.background.fxa.FxAccountUtils;
 import org.mozilla.gecko.background.preferences.PreferenceFragment;
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.fxa.FirefoxAccounts;
 import org.mozilla.gecko.fxa.FxAccountConstants;
 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
 import org.mozilla.gecko.fxa.login.Married;
 import org.mozilla.gecko.fxa.login.State;
 import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
 import org.mozilla.gecko.fxa.tasks.FxAccountCodeResender;
+import org.mozilla.gecko.reading.ReadingListConstants;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
 import org.mozilla.gecko.sync.SyncConfiguration;
 import org.mozilla.gecko.util.HardwareUtils;
 
 import android.accounts.Account;
+import android.accounts.AccountManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.os.Bundle;
 import android.os.Handler;
 import android.preference.CheckBoxPreference;
 import android.preference.EditTextPreference;
 import android.preference.Preference;
 import android.preference.Preference.OnPreferenceChangeListener;
 import android.preference.Preference.OnPreferenceClickListener;
 import android.preference.PreferenceCategory;
 import android.preference.PreferenceScreen;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
-
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
+import android.widget.Toast;
 
 
 /**
  * A fragment that displays the status of an AndroidFxAccount.
  * <p>
  * The owning activity is responsible for providing an AndroidFxAccount at
  * appropriate times.
  */
 public class FxAccountStatusFragment
     extends PreferenceFragment
     implements OnPreferenceClickListener, OnPreferenceChangeListener {
   private static final String LOG_TAG = FxAccountStatusFragment.class.getSimpleName();
 
-    /**
-     * If a device claims to have synced before this date, we will assume it has never synced.
-     */
-    private static final Date EARLIEST_VALID_SYNCED_DATE;
-    static {
-        final Calendar c = GregorianCalendar.getInstance();
-        c.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
-        EARLIEST_VALID_SYNCED_DATE = c.getTime();
-    }
+  /**
+   * If a device claims to have synced before this date, we will assume it has never synced.
+   */
+  private static final Date EARLIEST_VALID_SYNCED_DATE;
+  static {
+    final Calendar c = GregorianCalendar.getInstance();
+    c.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
+    EARLIEST_VALID_SYNCED_DATE = c.getTime();
+  }
+
   // When a checkbox is toggled, wait 5 seconds (for other checkbox actions)
   // before trying to sync. Should we kill off the fragment before the sync
   // request happens, that's okay: the runnable will run if the UI thread is
   // still around to service it, and since we're not updating any UI, we'll just
   // schedule the sync as usual. See also comment below about garbage
   // collection.
   private static final long DELAY_IN_MILLISECONDS_BEFORE_REQUESTING_SYNC = 5 * 1000;
   private static final long LAST_SYNCED_TIME_UPDATE_INTERVAL_IN_MILLISECONDS = 60 * 1000;
@@ -79,16 +83,25 @@ public class FxAccountStatusFragment
   // By default, the auth/account server preference is only shown when the
   // account is configured to use a custom server. In debug mode, this is set.
   private static boolean ALWAYS_SHOW_AUTH_SERVER = false;
 
   // By default, the Sync server preference is only shown when the account is
   // configured to use a custom Sync server. In debug mode, this is set.
   private static boolean ALWAYS_SHOW_SYNC_SERVER = false;
 
+  // If the user clicks the email field this many times, the debug / personal
+  // information logging setting will toggle. The setting is not permanent: it
+  // lasts until this process is killed. We don't want to dump PII to the log
+  // for a long time!
+  private final int NUMBER_OF_CLICKS_TO_TOGGLE_DEBUG =
+      // !defined(MOZILLA_OFFICIAL) || defined(NIGHTLY_BUILD) || defined(MOZ_DEBUG)
+      (!AppConstants.MOZILLA_OFFICIAL || AppConstants.NIGHTLY_BUILD || AppConstants.DEBUG_BUILD) ? 5 : -1 /* infinite */;
+  private int debugClickCount = 0;
+
   protected PreferenceCategory accountCategory;
   protected Preference emailPreference;
   protected Preference authServerPreference;
 
   protected Preference needsPasswordPreference;
   protected Preference needsUpgradePreference;
   protected Preference needsVerificationPreference;
   protected Preference needsMasterSyncAutomaticallyEnabledPreference;
@@ -172,16 +185,18 @@ public class FxAccountStatusFragment
     if (!FxAccountUtils.LOG_PERSONAL_INFORMATION) {
       removeDebugButtons();
     } else {
       connectDebugButtons();
       ALWAYS_SHOW_AUTH_SERVER = true;
       ALWAYS_SHOW_SYNC_SERVER = true;
     }
 
+    emailPreference.setOnPreferenceClickListener(this);
+
     needsPasswordPreference.setOnPreferenceClickListener(this);
     needsVerificationPreference.setOnPreferenceClickListener(this);
     needsFinishMigratingPreference.setOnPreferenceClickListener(this);
 
     bookmarksPreference.setOnPreferenceClickListener(this);
     historyPreference.setOnPreferenceClickListener(this);
     tabsPreference.setOnPreferenceClickListener(this);
     passwordsPreference.setOnPreferenceClickListener(this);
@@ -209,16 +224,27 @@ public class FxAccountStatusFragment
    */
   @Override
   public void onResume() {
     super.onResume();
   }
 
   @Override
   public boolean onPreferenceClick(Preference preference) {
+    if (preference == emailPreference) {
+      debugClickCount += 1;
+      if (NUMBER_OF_CLICKS_TO_TOGGLE_DEBUG > 0 && debugClickCount >= NUMBER_OF_CLICKS_TO_TOGGLE_DEBUG) {
+        debugClickCount = 0;
+        FxAccountUtils.LOG_PERSONAL_INFORMATION = !FxAccountUtils.LOG_PERSONAL_INFORMATION;
+        Toast.makeText(getActivity(), "Toggled logging Firefox Account personal information!", Toast.LENGTH_LONG).show();
+        hardRefresh(); // Display or hide debug options.
+      }
+      return true;
+    }
+
     if (preference == needsPasswordPreference) {
       Intent intent = new Intent(getActivity(), FxAccountUpdateCredentialsActivity.class);
       final Bundle extras = getExtrasForAccount();
       if (extras != null) {
         intent.putExtras(extras);
       }
       // Per http://stackoverflow.com/a/8992365, this triggers a known bug with
       // the soft keyboard not being shown for the started activity. Why, Android, why?
@@ -759,42 +785,71 @@ public class FxAccountStatusFragment
         Logger.info(LOG_TAG, "Refreshing.");
         refresh();
       } else if ("debug_dump".equals(key)) {
         fxAccount.dump();
       } else if ("debug_force_sync".equals(key)) {
         Logger.info(LOG_TAG, "Force syncing.");
         fxAccount.requestSync(FirefoxAccounts.FORCE);
         // No sense refreshing, since the sync will complete in the future.
+      } else if ("debug_forget_reading_list_oauth_token".equals(key)) {
+        final Account account = fxAccount.getAndroidAccount();
+        final AccountManager accountManager = AccountManager.get(getActivity());
+        final String authToken = accountManager.peekAuthToken(account, ReadingListConstants.AUTH_TOKEN_TYPE);
+        if (authToken != null) {
+          Logger.info(LOG_TAG, "Forgetting reading list oauth token: " + authToken);
+          accountManager.invalidateAuthToken(account.type, authToken);
+        } else {
+          Logger.warn(LOG_TAG, "No reading list oauth token to forget!");
+        }
       } else if ("debug_forget_certificate".equals(key)) {
         State state = fxAccount.getState();
         try {
           Married married = (Married) state;
           Logger.info(LOG_TAG, "Moving to Cohabiting state: Forgetting certificate.");
           fxAccount.setState(married.makeCohabitingState());
           refresh();
         } catch (ClassCastException e) {
           Logger.info(LOG_TAG, "Not in Married state; can't forget certificate.");
           // Ignore.
         }
+      } else if ("debug_invalidate_certificate".equals(key)) {
+        State state = fxAccount.getState();
+        try {
+          Married married = (Married) state;
+          Logger.info(LOG_TAG, "Invalidating certificate.");
+          fxAccount.setState(married.makeCohabitingState().withCertificate("INVALID CERTIFICATE"));
+          refresh();
+        } catch (ClassCastException e) {
+          Logger.info(LOG_TAG, "Not in Married state; can't invalidate certificate.");
+          // Ignore.
+        }
       } else if ("debug_require_password".equals(key)) {
         Logger.info(LOG_TAG, "Moving to Separated state: Forgetting password.");
         State state = fxAccount.getState();
         fxAccount.setState(state.makeSeparatedState());
         refresh();
       } else if ("debug_require_upgrade".equals(key)) {
         Logger.info(LOG_TAG, "Moving to Doghouse state: Requiring upgrade.");
         State state = fxAccount.getState();
         fxAccount.setState(state.makeDoghouseState());
         refresh();
       } else if ("debug_migrated_from_sync11".equals(key)) {
         Logger.info(LOG_TAG, "Moving to MigratedFromSync11 state: Requiring password.");
         State state = fxAccount.getState();
         fxAccount.setState(state.makeMigratedFromSync11State(null));
         refresh();
+      } else if ("debug_make_account_stage".equals(key)) {
+        Logger.info(LOG_TAG, "Moving Account endpoints, in place, to stage.  Deleting Sync and RL prefs and requiring password.");
+        fxAccount.unsafeTransitionToStageEndpoints();
+        refresh();
+      } else if ("debug_make_account_default".equals(key)) {
+        Logger.info(LOG_TAG, "Moving Account endpoints, in place, to default (production).  Deleting Sync and RL prefs and requiring password.");
+        fxAccount.unsafeTransitionToDefaultEndpoints();
+        refresh();
       } else {
         return false;
       }
       return true;
     }
   }
 
   /**
@@ -802,30 +857,22 @@ public class FxAccountStatusFragment
    * listener to each of them.
    */
   protected void connectDebugButtons() {
     // Separate listener to really separate debug logic from main code paths.
     final OnPreferenceClickListener listener = new DebugPreferenceClickListener();
 
     // We don't want to use Android resource strings for debug UI, so we just
     // use the keys throughout.
-    final Preference debugCategory = ensureFindPreference("debug_category");
+    final PreferenceCategory debugCategory = (PreferenceCategory) ensureFindPreference("debug_category");
     debugCategory.setTitle(debugCategory.getKey());
 
-    String[] debugKeys = new String[] {
-        "debug_refresh",
-        "debug_dump",
-        "debug_force_sync",
-        "debug_forget_certificate",
-        "debug_require_password",
-        "debug_require_upgrade",
-        "debug_migrated_from_sync11" };
-    for (String debugKey : debugKeys) {
-      final Preference button = ensureFindPreference(debugKey);
-      button.setTitle(debugKey); // Not very friendly, but this is for debugging only!
+    for (int i = 0; i < debugCategory.getPreferenceCount(); i++) {
+      final Preference button = debugCategory.getPreference(i);
+      button.setTitle(button.getKey()); // Not very friendly, but this is for debugging only!
       button.setOnPreferenceClickListener(listener);
     }
   }
 
   @Override
   public boolean onPreferenceChange(Preference preference, Object newValue) {
     if (preference == deviceNamePreference) {
       String newClientName = (String) newValue;
--- a/mobile/android/base/fxa/authenticator/AndroidFxAccount.java
+++ b/mobile/android/base/fxa/authenticator/AndroidFxAccount.java
@@ -615,9 +615,40 @@ public class AndroidFxAccount {
     final long neverSynced = -1L;
     try {
       return getSyncPrefs().getLong(PREF_KEY_LAST_SYNCED_TIMESTAMP, neverSynced);
     } catch (Exception e) {
       Logger.warn(LOG_TAG, "Got exception getting last synced time; ignoring.", e);
       return neverSynced;
     }
   }
+
+  // Debug only!  This is dangerous!
+  public void unsafeTransitionToDefaultEndpoints() {
+    unsafeTransitionToStageEndpoints(
+        FxAccountConstants.DEFAULT_AUTH_SERVER_ENDPOINT,
+        FxAccountConstants.DEFAULT_TOKEN_SERVER_ENDPOINT);
+    }
+
+  // Debug only!  This is dangerous!
+  public void unsafeTransitionToStageEndpoints() {
+    unsafeTransitionToStageEndpoints(
+        FxAccountConstants.STAGE_AUTH_SERVER_ENDPOINT,
+        FxAccountConstants.STAGE_TOKEN_SERVER_ENDPOINT);
+  }
+
+  protected void unsafeTransitionToStageEndpoints(String authServerEndpoint, String tokenServerEndpoint) {
+    try {
+      getReadingListPrefs().edit().clear().commit();
+    } catch (UnsupportedEncodingException | GeneralSecurityException e) {
+      // Ignore.
+    }
+    try {
+      getSyncPrefs().edit().clear().commit();
+    } catch (UnsupportedEncodingException | GeneralSecurityException e) {
+      // Ignore.
+    }
+    State state = getState();
+    setState(state.makeSeparatedState());
+    accountManager.setUserData(account, ACCOUNT_KEY_IDP_SERVER, authServerEndpoint);
+    accountManager.setUserData(account, ACCOUNT_KEY_TOKEN_SERVER, tokenServerEndpoint);
+  }
 }
--- a/mobile/android/base/fxa/login/Cohabiting.java
+++ b/mobile/android/base/fxa/login/Cohabiting.java
@@ -13,16 +13,20 @@ import org.mozilla.gecko.sync.ExtendedJS
 
 public class Cohabiting extends TokensAndKeysState {
   private static final String LOG_TAG = Cohabiting.class.getSimpleName();
 
   public Cohabiting(String email, String uid, byte[] sessionToken, byte[] kA, byte[] kB, BrowserIDKeyPair keyPair) {
     super(StateLabel.Cohabiting, email, uid, sessionToken, kA, kB, keyPair);
   }
 
+  public Married withCertificate(String certificate) {
+    return new Married(email, uid, sessionToken, kA, kB, keyPair, certificate);
+  }
+
   @Override
   public void execute(final ExecuteDelegate delegate) {
     delegate.getClient().sign(sessionToken, keyPair.getPublic().toJSONObject(), delegate.getCertificateDurationInMilliseconds(),
         new BaseRequestDelegate<String>(this, delegate) {
       @Override
       public void handleSuccess(String certificate) {
         if (FxAccountUtils.LOG_PERSONAL_INFORMATION) {
           try {
@@ -34,13 +38,13 @@ public class Cohabiting extends TokensAn
               FxAccountUtils.pii(LOG_TAG, "Signature: " + c.getString("signature"));
             } else {
               FxAccountUtils.pii(LOG_TAG, "Could not parse certificate!");
             }
           } catch (Exception e) {
             FxAccountUtils.pii(LOG_TAG, "Could not parse certificate!");
           }
         }
-        delegate.handleTransition(new LogMessage("sign succeeded"), new Married(email, uid, sessionToken, kA, kB, keyPair, certificate));
+        delegate.handleTransition(new LogMessage("sign succeeded"), withCertificate(certificate));
       }
     });
   }
 }
--- a/mobile/android/base/fxa/login/Married.java
+++ b/mobile/android/base/fxa/login/Married.java
@@ -107,12 +107,12 @@ public class Married extends TokensAndKe
 
   public String getClientState() {
     if (FxAccountUtils.LOG_PERSONAL_INFORMATION) {
       FxAccountUtils.pii(LOG_TAG, "Client state: " + this.clientState);
     }
     return this.clientState;
   }
 
-  public State makeCohabitingState() {
+  public Cohabiting makeCohabitingState() {
     return new Cohabiting(email, uid, sessionToken, kA, kB, keyPair);
   }
 }
--- a/mobile/android/base/reading/ReadingListConstants.java
+++ b/mobile/android/base/reading/ReadingListConstants.java
@@ -7,12 +7,13 @@ package org.mozilla.gecko.reading;
 import org.mozilla.gecko.AppConstants;
 
 public class ReadingListConstants {
   public static final String GLOBAL_LOG_TAG = "FxReadingList";
   public static final String USER_AGENT = "Firefox-Android-FxReader/" + AppConstants.MOZ_APP_VERSION + " (" + AppConstants.MOZ_APP_DISPLAYNAME + ")";
   public static final String DEFAULT_DEV_ENDPOINT = "https://readinglist.dev.mozaws.net/v1/";
   public static final String DEFAULT_PROD_ENDPOINT = "https://readinglist.services.mozilla.com/v1/";
 
-  public static final String OAUTH_ENDPOINT_PROD = "https://oauth.accounts.firefox.com/v1";
+  public static final String OAUTH_SCOPE_READINGLIST = "readinglist";
+  public static final String AUTH_TOKEN_TYPE = "oauth::" + OAUTH_SCOPE_READINGLIST;
 
   public static boolean DEBUG = false;
 }
--- a/mobile/android/base/reading/ReadingListSyncAdapter.java
+++ b/mobile/android/base/reading/ReadingListSyncAdapter.java
@@ -20,16 +20,17 @@ import org.mozilla.gecko.background.fxa.
 import org.mozilla.gecko.background.fxa.FxAccountUtils;
 import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClient.RequestDelegate;
 import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClientException.FxAccountAbstractClientRemoteException;
 import org.mozilla.gecko.background.fxa.oauth.FxAccountOAuthClient10;
 import org.mozilla.gecko.background.fxa.oauth.FxAccountOAuthClient10.AuthorizationResponse;
 import org.mozilla.gecko.browserid.BrowserIDKeyPair;
 import org.mozilla.gecko.browserid.JSONWebTokenUtils;
 import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
+import org.mozilla.gecko.fxa.FxAccountConstants;
 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
 import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine;
 import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine.LoginStateMachineDelegate;
 import org.mozilla.gecko.fxa.login.FxAccountLoginTransition.Transition;
 import org.mozilla.gecko.fxa.login.Married;
 import org.mozilla.gecko.fxa.login.State;
 import org.mozilla.gecko.fxa.login.State.StateLabel;
 import org.mozilla.gecko.fxa.login.StateFactory;
@@ -43,18 +44,16 @@ import android.content.ContentProviderCl
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.SyncResult;
 import android.os.Bundle;
 
 public class ReadingListSyncAdapter extends AbstractThreadedSyncAdapter {
   public static final String PREF_LOCAL_NAME = "device.localname";
-  public static final String OAUTH_CLIENT_ID_FENNEC = "3332a18d142636cb";
-  public static final String OAUTH_SCOPE_READINGLIST = "readinglist";
 
   private static final String LOG_TAG = ReadingListSyncAdapter.class.getSimpleName();
   private static final long TIMEOUT_SECONDS = 60;
   protected final ExecutorService executor;
 
   public ReadingListSyncAdapter(Context context, boolean autoInitialize) {
     super(context, autoInitialize);
     this.executor = Executors.newSingleThreadExecutor();
@@ -140,17 +139,17 @@ public class ReadingListSyncAdapter exte
       final State state;
       try {
         state = fxAccount.getState();
       } catch (Exception e) {
         Logger.error(LOG_TAG, "Unable to sync.", e);
         return;
       }
 
-      final String oauthServerUri = ReadingListConstants.OAUTH_ENDPOINT_PROD;
+      final String oauthServerUri = FxAccountConstants.STAGE_OAUTH_SERVER_ENDPOINT;
       final String authServerEndpoint = fxAccount.getAccountServerURI();
       final String audience = FxAccountUtils.getAudienceForURL(oauthServerUri); // The assertion gets traded in for an oauth bearer token.
 
       final SharedPreferences sharedPrefs = fxAccount.getReadingListPrefs();
       final FxAccountClient client = new FxAccountClient20(authServerEndpoint, executor);
       final FxAccountLoginStateMachine stateMachine = new FxAccountLoginStateMachine();
 
       stateMachine.advance(state, StateLabel.Married, new LoginStateMachineDelegate() {
@@ -190,18 +189,18 @@ public class ReadingListSyncAdapter exte
               syncDelegate.handleCannotSync(state);
               return;
             }
 
             final Married married = (Married) state;
             final String assertion = married.generateAssertion(audience, JSONWebTokenUtils.DEFAULT_ASSERTION_ISSUER);
             JSONWebTokenUtils.dumpAssertion(assertion);
 
-            final String clientID = OAUTH_CLIENT_ID_FENNEC;
-            final String scope = OAUTH_SCOPE_READINGLIST;
+            final String clientID = FxAccountConstants.OAUTH_CLIENT_ID_FENNEC;
+            final String scope = ReadingListConstants.OAUTH_SCOPE_READINGLIST;
             syncWithAssertion(clientID, scope, assertion, sharedPrefs, extras);
           } catch (Exception e) {
             syncDelegate.handleError(e);
             return;
           }
         }
 
         private void syncWithAssertion(final String client_id, final String scope, final String assertion,
--- a/mobile/android/base/resources/xml/fxaccount_status_prefscreen.xml
+++ b/mobile/android/base/resources/xml/fxaccount_status_prefscreen.xml
@@ -121,15 +121,19 @@
                 android:targetPackage="@string/browser_intent_package" />
         </Preference>
     </PreferenceCategory>
     <PreferenceCategory
         android:key="debug_category" >
         <Preference android:key="debug_refresh" />
         <Preference android:key="debug_dump" />
         <Preference android:key="debug_force_sync" />
+        <Preference android:key="debug_invalidate_certificate" />
+        <Preference android:key="debug_forget_reading_list_oauth_token" />
         <Preference android:key="debug_forget_certificate" />
         <Preference android:key="debug_require_password" />
         <Preference android:key="debug_require_upgrade" />
         <Preference android:key="debug_migrated_from_sync11" />
-    </PreferenceCategory>
+        <Preference android:key="debug_make_account_stage" />
+        <Preference android:key="debug_make_account_default" />
+	</PreferenceCategory>
 
 </PreferenceScreen>