diff --git a/android/app/build.gradle b/android/app/build.gradle
index 76f5acfaea..91ab54cf22 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -53,6 +53,9 @@ android {
         debug {
             buildConfigField "String", "BACKTRACE_URL", "\"" + (System.getenv("CMAKE_BACKTRACE_URL") ? System.getenv("CMAKE_BACKTRACE_URL") : '') + "\""
             buildConfigField "String", "BACKTRACE_TOKEN", "\"" + (System.getenv("CMAKE_BACKTRACE_TOKEN") ? System.getenv("CMAKE_BACKTRACE_TOKEN") : '') + "\""
+            buildConfigField "String", "OAUTH_CLIENT_ID", "\"" + (System.getenv("OAUTH_CLIENT_ID") ? System.getenv("OAUTH_CLIENT_ID") : '') + "\""
+            buildConfigField "String", "OAUTH_CLIENT_SECRET", "\"" + (System.getenv("OAUTH_CLIENT_SECRET") ? System.getenv("OAUTH_CLIENT_SECRET") : '') + "\""
+            buildConfigField "String", "OAUTH_REDIRECT_URI", "\"" + (System.getenv("OAUTH_REDIRECT_URI") ? System.getenv("OAUTH_REDIRECT_URI") : '') + "\""
         }
         release {
             minifyEnabled false
@@ -63,6 +66,9 @@ android {
                     project.hasProperty("HIFI_ANDROID_KEY_PASSWORD")? signingConfigs.release : null
             buildConfigField "String", "BACKTRACE_URL", "\"" + (System.getenv("CMAKE_BACKTRACE_URL") ? System.getenv("CMAKE_BACKTRACE_URL") : '') + "\""
             buildConfigField "String", "BACKTRACE_TOKEN", "\"" + (System.getenv("CMAKE_BACKTRACE_TOKEN") ? System.getenv("CMAKE_BACKTRACE_TOKEN") : '') + "\""
+            buildConfigField "String", "OAUTH_CLIENT_ID", "\"" + (System.getenv("OAUTH_CLIENT_ID") ? System.getenv("OAUTH_CLIENT_ID") : '') + "\""
+            buildConfigField "String", "OAUTH_CLIENT_SECRET", "\"" + (System.getenv("OAUTH_CLIENT_SECRET") ? System.getenv("OAUTH_CLIENT_SECRET") : '') + "\""
+            buildConfigField "String", "OAUTH_REDIRECT_URI", "\"" + (System.getenv("OAUTH_REDIRECT_URI") ? System.getenv("OAUTH_REDIRECT_URI") : '') + "\""
         }
     }
 
diff --git a/android/app/src/main/cpp/native.cpp b/android/app/src/main/cpp/native.cpp
index 63e116abcc..4d60912831 100644
--- a/android/app/src/main/cpp/native.cpp
+++ b/android/app/src/main/cpp/native.cpp
@@ -363,6 +363,52 @@ Java_io_highfidelity_hifiinterface_fragment_LoginFragment_login(JNIEnv *env, job
                               Q_ARG(const QString&, username), Q_ARG(const QString&, password));
 }
 
+JNIEXPORT void JNICALL
+Java_io_highfidelity_hifiinterface_fragment_LoginFragment_retrieveAccessToken(JNIEnv *env,
+                                                                                  jobject instance,
+                                                                                  jstring authCode_,
+                                                                                  jstring clientId_,
+                                                                                  jstring clientSecret_,
+                                                                                  jstring redirectUri_) {
+    const char *c_authCode = env->GetStringUTFChars(authCode_, 0);
+    const char *c_clientId = env->GetStringUTFChars(clientId_, 0);
+    const char *c_clientSecret = env->GetStringUTFChars(clientSecret_, 0);
+    const char *c_redirectUri = env->GetStringUTFChars(redirectUri_, 0);
+
+    QString authCode = QString(c_authCode);
+    QString clientId = QString(c_clientId);
+    QString clientSecret = QString(c_clientSecret);
+    QString redirectUri = QString(c_redirectUri);
+
+    env->ReleaseStringUTFChars(authCode_, c_authCode);
+    env->ReleaseStringUTFChars(clientId_, c_clientId);
+    env->ReleaseStringUTFChars(clientSecret_, c_clientSecret);
+    env->ReleaseStringUTFChars(redirectUri_, c_redirectUri);
+
+    auto accountManager = DependencyManager::get<AccountManager>();
+
+    __loginCompletedListener = QAndroidJniObject(instance); // TODO: use a different listener?
+
+    QObject::connect(accountManager.data(), &AccountManager::loginComplete, [](const QUrl& authURL) {
+        jboolean jSuccess = (jboolean) true;
+        if (__loginCompletedListener.isValid()) {
+            __loginCompletedListener.callMethod<void>("handleLoginCompleted", "(Z)V", jSuccess);
+        }
+    });
+
+    QObject::connect(accountManager.data(), &AccountManager::loginFailed, []() {
+        jboolean jSuccess = (jboolean) false;
+        if (__loginCompletedListener.isValid()) {
+            __loginCompletedListener.callMethod<void>("handleLoginCompleted", "(Z)V", jSuccess);
+        }
+    });
+
+    QMetaObject::invokeMethod(accountManager.data(), "requestAccessTokenWithAuthCode",
+                              Q_ARG(const QString&, authCode), Q_ARG(const QString&, clientId),
+                              Q_ARG(const QString&, clientSecret), Q_ARG(const QString&, redirectUri));
+
+}
+
 JNIEXPORT void JNICALL
 Java_io_highfidelity_hifiinterface_fragment_SignupFragment_login(JNIEnv *env,
                                                                        jobject instance,
diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java
index 5250d2582b..7db158a100 100644
--- a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java
+++ b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java
@@ -142,6 +142,12 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
         LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         webSlidingDrawer = (SlidingDrawer) inflater.inflate(R.layout.web_drawer, mainLayout, false);
         QtLayout qtLayout = (QtLayout) mainLayout.getChildAt(0);
+        if (qtLayout == null) {
+            Log.d("[QTLAYOUT-NULL]" , "Mainlayout children: "  + mainLayout.getChildCount());
+            for (int i=0; i< mainLayout.getChildCount(); i++) {
+                Log.d("[QTLAYOUT-NULL]" , "Child " + i + ": "  + mainLayout.getChildAt(i));
+            }
+        }
         QtLayout.LayoutParams layoutParams = new QtLayout.LayoutParams(webSlidingDrawer.getLayoutParams());
         webSlidingDrawer.setOnDrawerCloseListener(() -> {
             WebViewFragment webViewFragment = (WebViewFragment) getFragmentManager().findFragmentByTag("webViewFragment");
@@ -380,4 +386,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
     public void onExpand() {
         keepInterfaceRunning = true;
     }
+
+    @Override
+    public void onOAuthAuthorizeCallback(Uri uri) { }
 }
diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/LoginMenuActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/LoginMenuActivity.java
index 67ff772573..63f74ac3e2 100644
--- a/android/app/src/main/java/io/highfidelity/hifiinterface/LoginMenuActivity.java
+++ b/android/app/src/main/java/io/highfidelity/hifiinterface/LoginMenuActivity.java
@@ -92,7 +92,7 @@ public class LoginMenuActivity extends AppCompatActivity
 
     @Override
     public void onLoginButtonClicked() {
-        loadLoginFragment();
+        loadLoginFragment(false);
     }
 
     @Override
@@ -100,6 +100,10 @@ public class LoginMenuActivity extends AppCompatActivity
         loadMainActivity();
     }
 
+    @Override
+    public void onSteamLoginButtonClicked() {
+        loadLoginFragment(true);
+    }
 
     private void loadSignupFragment() {
         FragmentManager fragmentManager = getFragmentManager();
@@ -113,10 +117,10 @@ public class LoginMenuActivity extends AppCompatActivity
         hideStatusBar();
     }
 
-    private void loadLoginFragment() {
+    private void loadLoginFragment(boolean useOauth) {
         FragmentManager fragmentManager = getFragmentManager();
         FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
-        Fragment fragment = LoginFragment.newInstance();
+        Fragment fragment = LoginFragment.newInstance(useOauth);
         String tag = getString(R.string.tagFragmentLogin);
         fragmentTransaction.replace(R.id.content_frame, fragment, tag);
         fragmentTransaction.addToBackStack(tag);
diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/WebViewActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/WebViewActivity.java
index 5d65bcad51..e906c4b734 100644
--- a/android/app/src/main/java/io/highfidelity/hifiinterface/WebViewActivity.java
+++ b/android/app/src/main/java/io/highfidelity/hifiinterface/WebViewActivity.java
@@ -28,11 +28,18 @@ import java.net.MalformedURLException;
 import java.net.URL;
 
 import io.highfidelity.hifiinterface.fragment.WebViewFragment;
+import io.highfidelity.hifiinterface.fragment.WebViewFragment.OnWebViewInteractionListener;
 
-public class WebViewActivity extends Activity implements WebViewFragment.OnWebViewInteractionListener {
+public class WebViewActivity extends Activity implements OnWebViewInteractionListener {
 
     public static final String WEB_VIEW_ACTIVITY_EXTRA_URL = "url";
+    public static final String WEB_VIEW_ACTIVITY_EXTRA_CLEAR_COOKIES = "clear_cookies";
+    public static final String RESULT_OAUTH_CODE = "code";
+    public static final String RESULT_OAUTH_STATE = "state";
+
     private static final String FRAGMENT_TAG = "WebViewActivity_WebFragment";
+    private static final String OAUTH_CODE = "code";
+    private static final String OAUTH_STATE = "state";
 
     private native void nativeProcessURL(String url);
 
@@ -47,14 +54,15 @@ public class WebViewActivity extends Activity implements WebViewFragment.OnWebVi
         mActionBar = getActionBar();
         mActionBar.setDisplayHomeAsUpEnabled(true);
 
-        loadWebViewFragment(getIntent().getStringExtra(WEB_VIEW_ACTIVITY_EXTRA_URL));
+        loadWebViewFragment(getIntent().getStringExtra(WEB_VIEW_ACTIVITY_EXTRA_URL), getIntent().getBooleanExtra(WEB_VIEW_ACTIVITY_EXTRA_CLEAR_COOKIES, false));
     }
 
-    private void loadWebViewFragment(String url) {
+    private void loadWebViewFragment(String url, boolean clearCookies) {
         WebViewFragment fragment = WebViewFragment.newInstance();
         Bundle bundle = new Bundle();
         bundle.putString(WebViewFragment.URL, url);
         bundle.putBoolean(WebViewFragment.TOOLBAR_VISIBLE, false);
+        bundle.putBoolean(WebViewFragment.CLEAR_COOKIES, clearCookies);
         fragment.setArguments(bundle);
         FragmentManager fragmentManager = getFragmentManager();
         FragmentTransaction ft = fragmentManager.beginTransaction();
@@ -131,4 +139,13 @@ public class WebViewActivity extends Activity implements WebViewFragment.OnWebVi
     @Override
     public void onExpand() { }
 
+    @Override
+    public void onOAuthAuthorizeCallback(Uri uri) {
+        Intent result = new Intent();
+        result.putExtra(RESULT_OAUTH_CODE, uri.getQueryParameter(OAUTH_CODE));
+        result.putExtra(RESULT_OAUTH_STATE, uri.getQueryParameter(OAUTH_STATE));
+        setResult(Activity.RESULT_OK, result);
+        finish();
+    }
+
 }
diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java
index eea65d425f..79fbb50737 100644
--- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java
+++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java
@@ -1,10 +1,13 @@
 package io.highfidelity.hifiinterface.fragment;
 
+import android.app.Activity;
 import android.app.Fragment;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.util.Log;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -18,8 +21,14 @@ import android.widget.TextView;
 
 import org.qtproject.qt5.android.QtNative;
 
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Random;
+
+import io.highfidelity.hifiinterface.BuildConfig;
 import io.highfidelity.hifiinterface.HifiUtils;
 import io.highfidelity.hifiinterface.R;
+import io.highfidelity.hifiinterface.WebViewActivity;
 
 import static org.qtproject.qt5.android.QtActivityDelegate.ApplicationActive;
 import static org.qtproject.qt5.android.QtActivityDelegate.ApplicationInactive;
@@ -27,6 +36,14 @@ import static org.qtproject.qt5.android.QtActivityDelegate.ApplicationInactive;
 public class LoginFragment extends Fragment
                             implements  OnBackPressedListener {
 
+    private static final String ARG_USE_OAUTH = "use_oauth";
+    private static final String TAG = "Interface";
+
+    private final String OAUTH_CLIENT_ID = BuildConfig.OAUTH_CLIENT_ID;
+    private final String OAUTH_REDIRECT_URI = BuildConfig.OAUTH_REDIRECT_URI;
+    private final String OAUTH_AUTHORIZE_BASE_URL = "https://highfidelity.com/oauth/authorize";
+    private static final int OAUTH_AUTORIZE_REQUEST = 1;
+
     private EditText mUsername;
     private EditText mPassword;
     private TextView mError;
@@ -37,8 +54,12 @@ public class LoginFragment extends Fragment
     private ViewGroup mLoggedInFrame;
     private boolean mLoginInProgress;
     private boolean mLoginSuccess;
+    private boolean mUseOauth;
+    private String mOauthState;
 
     public native void login(String username, String password, boolean keepLoggedIn);
+    private native void retrieveAccessToken(String authCode, String clientId, String clientSecret, String redirectUri);
+
     public native void cancelLogin();
 
     private LoginFragment.OnLoginInteractionListener mListener;
@@ -47,11 +68,22 @@ public class LoginFragment extends Fragment
         // Required empty public constructor
     }
 
-    public static LoginFragment newInstance() {
+    public static LoginFragment newInstance(boolean useOauth) {
         LoginFragment fragment = new LoginFragment();
+        Bundle args = new Bundle();
+        args.putBoolean(ARG_USE_OAUTH, useOauth);
+        fragment.setArguments(args);
         return fragment;
     }
 
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (getArguments() != null) {
+            mUseOauth = getArguments().getBoolean(ARG_USE_OAUTH, false);
+        }
+    }
+
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
                              Bundle savedInstanceState) {
@@ -79,6 +111,11 @@ public class LoginFragment extends Fragment
 
         mKeepMeLoggedInCheckbox.setChecked(HifiUtils.getInstance().isKeepingLoggedIn());
 
+        if (mUseOauth) {
+            openWebForAuthorization();
+        } else {
+            showLoginForm();
+        }
         return rootView;
     }
 
@@ -109,12 +146,33 @@ public class LoginFragment extends Fragment
     @Override
     public void onStop() {
         super.onStop();
-        showLoginForm();
+        if (!mUseOauth) {
+            showLoginForm();
+        }
         // Leave the Qt app paused
         QtNative.setApplicationState(ApplicationInactive);
         hideKeyboard();
     }
 
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == OAUTH_AUTORIZE_REQUEST) {
+            if (resultCode == Activity.RESULT_OK) {
+                String authCode = data.getStringExtra(WebViewActivity.RESULT_OAUTH_CODE);
+                String state = data.getStringExtra(WebViewActivity.RESULT_OAUTH_STATE);
+                if (state != null && state.equals(mOauthState) && mListener != null) {
+                    mOauthState = null;
+                    showActivityIndicator();
+                    mLoginInProgress = true;
+                    retrieveAccessToken(authCode, BuildConfig.OAUTH_CLIENT_ID, BuildConfig.OAUTH_CLIENT_SECRET, BuildConfig.OAUTH_REDIRECT_URI);
+                }
+            } else {
+                onCancelLogin();
+            }
+        }
+
+    }
+
     private void onCancelLogin() {
         if (mListener != null) {
             mListener.onCancelLogin();
@@ -207,8 +265,12 @@ public class LoginFragment extends Fragment
                 mLoginSuccess = true;
                 showLoggedInMessage();
             } else {
-                showLoginForm();
-                showError(getString(R.string.login_username_or_password_incorrect));
+                if (!mUseOauth) {
+                    showLoginForm();
+                    showError(getString(R.string.login_username_or_password_incorrect));
+                } else {
+                    openWebForAuthorization();
+                }
             }
         });
     }
@@ -229,6 +291,33 @@ public class LoginFragment extends Fragment
         }
     }
 
+    private void updateOauthState() {
+        mOauthState = Long.toString(new Random().nextLong());
+    }
+
+    private String buildAuthorizeUrl() {
+        StringBuilder sb = new StringBuilder(OAUTH_AUTHORIZE_BASE_URL);
+        sb.append("?client_id=").append(OAUTH_CLIENT_ID);
+        try {
+            String redirectUri = URLEncoder.encode(OAUTH_REDIRECT_URI, "utf-8");
+            sb.append("&redirect_uri=").append(redirectUri);
+        } catch (UnsupportedEncodingException e) {
+            Log.e(TAG, "Cannot build oauth autorization url", e);
+        }
+        sb.append("&response_type=code&scope=owner");
+        sb.append("&state=").append(mOauthState);
+        return sb.toString();
+    }
+
+    private void openWebForAuthorization() {
+        Intent openUrlIntent = new Intent(getActivity(), WebViewActivity.class);
+        updateOauthState();
+        openUrlIntent.putExtra(WebViewActivity.WEB_VIEW_ACTIVITY_EXTRA_URL, buildAuthorizeUrl());
+        openUrlIntent.putExtra(WebViewActivity.WEB_VIEW_ACTIVITY_EXTRA_CLEAR_COOKIES, true);
+        startActivityForResult(openUrlIntent, OAUTH_AUTORIZE_REQUEST);
+    }
+
+
     public interface OnLoginInteractionListener {
         void onLoginCompleted();
         void onCancelLogin();
diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/StartMenuFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/StartMenuFragment.java
index 22614fa300..fe77991962 100644
--- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/StartMenuFragment.java
+++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/StartMenuFragment.java
@@ -1,8 +1,8 @@
 package io.highfidelity.hifiinterface.fragment;
 
+import android.app.Fragment;
 import android.content.Context;
 import android.os.Bundle;
-import android.app.Fragment;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -11,6 +11,7 @@ import io.highfidelity.hifiinterface.R;
 
 public class StartMenuFragment extends Fragment {
 
+    private String TAG = "HighFidelity";
     private StartMenuInteractionListener mListener;
 
     public StartMenuFragment() {
@@ -39,6 +40,12 @@ public class StartMenuFragment extends Fragment {
             }
         });
 
+        rootView.findViewById(R.id.steamLoginButton).setOnClickListener(view -> {
+            if (mListener != null) {
+                mListener.onSteamLoginButtonClicked();
+            }
+        });
+
         rootView.findViewById(R.id.takeMeInWorld).setOnClickListener(view -> {
             if (mListener != null) {
                 mListener.onSkipLoginClicked();
@@ -81,5 +88,6 @@ public class StartMenuFragment extends Fragment {
         void onSignupButtonClicked();
         void onLoginButtonClicked();
         void onSkipLoginClicked();
+        void onSteamLoginButtonClicked();
     }
 }
diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/WebViewFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/WebViewFragment.java
index 2d887d5a19..94aa6f29a3 100644
--- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/WebViewFragment.java
+++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/WebViewFragment.java
@@ -4,9 +4,11 @@ import android.app.Fragment;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.net.Uri;
 import android.net.http.SslError;
 import android.os.Bundle;
 import android.os.Handler;
+import android.util.Log;
 import android.view.GestureDetector;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -14,6 +16,7 @@ import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.AlphaAnimation;
+import android.webkit.CookieManager;
 import android.webkit.SslErrorHandler;
 import android.webkit.WebChromeClient;
 import android.webkit.WebResourceError;
@@ -25,6 +28,10 @@ import android.webkit.WebViewClient;
 import android.widget.ProgressBar;
 import android.widget.Toast;
 
+import java.util.List;
+import java.util.Set;
+
+import io.highfidelity.hifiinterface.BuildConfig;
 import io.highfidelity.hifiinterface.R;
 import io.highfidelity.hifiinterface.WebViewActivity;
 
@@ -32,6 +39,7 @@ public class WebViewFragment extends Fragment implements GestureDetector.OnGestu
 
     public static final String URL = "url";
     public static final String TOOLBAR_VISIBLE = "toolbar_visible";
+    public static final String CLEAR_COOKIES = "clear_cookies";
     private static final long DELAY_HIDE_TOOLBAR_MILLIS = 3000;
     private static final long FADE_OUT_DURATION = 2000;
 
@@ -41,6 +49,7 @@ public class WebViewFragment extends Fragment implements GestureDetector.OnGestu
     private ProgressBar mProgressBar;
     private String mUrl;
     private boolean mToolbarVisible;
+    private boolean mClearCookies;
 
     private OnWebViewInteractionListener mListener;
     private Runnable mCloseAction;
@@ -170,6 +179,7 @@ public class WebViewFragment extends Fragment implements GestureDetector.OnGestu
         if (getArguments() != null) {
             mUrl = getArguments().getString(URL);
             mToolbarVisible = getArguments().getBoolean(TOOLBAR_VISIBLE);
+            mClearCookies = getArguments().getBoolean(CLEAR_COOKIES);
         }
     }
 
@@ -179,6 +189,10 @@ public class WebViewFragment extends Fragment implements GestureDetector.OnGestu
         View rootView = inflater.inflate(R.layout.fragment_web_view, container, false);
         mProgressBar = rootView.findViewById(R.id.toolbarProgressBar);
         myWebView = rootView.findViewById(R.id.web_view);
+        if (mClearCookies) {
+            CookieManager.getInstance().removeAllCookies(null);
+        }
+
         mHandler = new Handler();
         gestureDetector = new GestureDetector(this);
         gestureDetector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() {
@@ -251,6 +265,7 @@ public class WebViewFragment extends Fragment implements GestureDetector.OnGestu
         void onWebLoaded(String url, SafenessLevel safenessLevel);
         void onTitleReceived(String title);
         void onExpand();
+        void onOAuthAuthorizeCallback(Uri uri);
     }
 
 
@@ -320,6 +335,17 @@ public class WebViewFragment extends Fragment implements GestureDetector.OnGestu
                 super.onLoadResource(view, url);
             }
         }
+
+        @Override
+        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
+            if (request.getUrl().toString().startsWith(BuildConfig.OAUTH_REDIRECT_URI)) {
+                if (mListener != null) {
+                    mListener.onOAuthAuthorizeCallback(request.getUrl());
+                }
+                return true;
+            }
+            return super.shouldOverrideUrlLoading(view, request);
+        }
     }
 
     class HiFiWebChromeClient extends WebChromeClient {
diff --git a/android/app/src/main/res/drawable/ic_steam.xml b/android/app/src/main/res/drawable/ic_steam.xml
new file mode 100644
index 0000000000..9b739c1f73
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_steam.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="21dp"
+    android:height="21dp"
+    android:viewportWidth="21"
+    android:viewportHeight="21">
+
+    <path
+        android:fillColor="#ffffff"
+        android:pathData="M10.4866,0 C4.92045,0,0.367395,4.32104,0,9.78734 L5.45262,11.9841 C5.93184,11.651,6.51333,11.4545,7.14145,11.4545 C7.27641,11.4545,7.4083,11.4666,7.53829,11.4841 L10.1047,7.72495 C10.1083,5.56672,11.861,3.81818,14.0229,3.81818 C16.1872,3.81818,17.9416,5.57035,17.9416,7.73182 C17.9416,9.89329,16.1872,11.6455,14.0229,11.6455 C14.021,11.6455,14.0189,11.6453,14.017,11.6453 L10.0936,14.2008 C10.0986,14.2712,10.1043,14.3419,10.1043,14.4136 C10.1043,16.048,8.77791,17.3727,7.14145,17.3727 C5.69539,17.3727,4.49304,16.3376,4.2325,14.969 L0.378099,13.3841 C1.6334,17.7801,5.6822,21,10.4866,21 C16.2931,21,21,16.2991,21,10.5 C21,4.70114,16.2931,0,10.4866,0 Z M7.14145,16.0364 C6.96655,16.0364,6.79833,16.0081,6.64044,15.9569 L6.63968,15.9589 L6.59151,15.939 C6.54506,15.9224,6.49975,15.9037,6.45541,15.8831 L5.15462,15.3483 C5.50614,16.0927,6.26253,16.6091,7.14145,16.6091 C8.35546,16.6091,9.33971,15.6263,9.33971,14.4136 C9.33971,13.201,8.35546,12.2182,7.14145,12.2182 C6.87269,12.2182,6.61636,12.2688,6.37818,12.357 L7.75448,12.9114 C7.76404,12.9154,7.77359,12.9188,7.78296,12.923 L7.89001,12.9662 L7.88714,12.9732 C8.40898,13.243,8.76625,13.7861,8.76625,14.4136 C8.76625,15.3098,8.03872,16.0364,7.14145,16.0364 Z M16.7946,7.73182 C16.7946,6.20302,15.5537,4.96364,14.0229,4.96364 C12.4922,4.96364,11.2512,6.20302,11.2512,7.73182 C11.2512,9.26062,12.4922,10.5,14.0229,10.5 C15.5537,10.5,16.7946,9.26062,16.7946,7.73182 Z M12.0158,7.73182 C12.0158,6.62474,12.9144,5.72727,14.0229,5.72727 C15.1314,5.72727,16.03,6.62474,16.03,7.73182 C16.03,8.8389,15.1314,9.73636,14.0229,9.73636 C12.9144,9.73636,12.0158,8.8389,12.0158,7.73182 Z" />
+</vector>
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/rounded_button_color4.xml b/android/app/src/main/res/drawable/rounded_button_color4.xml
new file mode 100644
index 0000000000..679bf24513
--- /dev/null
+++ b/android/app/src/main/res/drawable/rounded_button_color4.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item android:state_pressed="true" >
+        <shape android:shape="rectangle"  >
+            <corners android:radius="4dip" />
+            <stroke android:width="1dip" android:color="@color/colorButton4" />
+            <solid android:color="@color/colorButton4"/>
+        </shape>
+    </item>
+    <item android:state_focused="true">
+        <shape android:shape="rectangle"  >
+            <corners android:radius="4dip" />
+            <stroke android:width="1dip" android:color="@color/colorButton4" />
+            <solid android:color="@color/colorButton4"/>
+        </shape>
+    </item>
+    <item>
+        <shape android:shape="rectangle"  >
+            <corners android:radius="4dip" />
+            <stroke android:width="1dip" android:color="@color/colorButton4" />
+            <solid android:color="@color/colorButton4"/>
+        </shape>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/fragment_login.xml b/android/app/src/main/res/layout/fragment_login.xml
index 2f48424ae8..343822e243 100644
--- a/android/app/src/main/res/layout/fragment_login.xml
+++ b/android/app/src/main/res/layout/fragment_login.xml
@@ -111,7 +111,7 @@
         app:layout_constraintRight_toRightOf="parent"
         app:layout_constraintTop_toBottomOf="@id/header"
         app:layout_constraintBottom_toBottomOf="parent"
-        android:visibility="visible">
+        android:visibility="gone">
         <TextView
             android:id="@+id/error"
             android:layout_width="wrap_content"
diff --git a/android/app/src/main/res/layout/fragment_login_menu.xml b/android/app/src/main/res/layout/fragment_login_menu.xml
index cda055c883..7484713b6b 100644
--- a/android/app/src/main/res/layout/fragment_login_menu.xml
+++ b/android/app/src/main/res/layout/fragment_login_menu.xml
@@ -74,6 +74,24 @@ tools:context=".fragment.StartMenuFragment">
             app:layout_constraintLeft_toLeftOf="parent"
             app:layout_constraintRight_toRightOf="parent"/>
 
+        <Button
+            android:id="@+id/steamLoginButton"
+            android:layout_width="238dp"
+            android:layout_height="42dp"
+            android:background="@drawable/rounded_button_color4"
+            android:fontFamily="@font/raleway_bold"
+            android:layout_marginTop="10dp"
+            android:text="@string/steam_log_in"
+            android:textAlignment="center"
+            android:textColor="@color/white_opaque"
+            android:textAllCaps="false"
+            android:textSize="18sp"
+            android:drawableLeft="@drawable/ic_steam"
+            android:paddingLeft="38dp"
+            android:paddingRight="38dp"
+            app:layout_constraintTop_toBottomOf="@id/loginButton"
+            app:layout_constraintLeft_toLeftOf="parent"
+            app:layout_constraintRight_toRightOf="parent"/>
 
         <TextView
             android:id="@+id/takeMeInWorld"
diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml
index 73b2d5d61d..fc5ce7ba16 100644
--- a/android/app/src/main/res/values/colors.xml
+++ b/android/app/src/main/res/values/colors.xml
@@ -11,6 +11,7 @@
     <color name="colorButton1">#00B4EF</color>
     <color name="colorButton2">#828282</color>
     <color name="colorButton3">#8F8F8F</color>
+    <color name="colorButton4">#434343</color>
     <color name="backgroundDark">#333333</color>
     <color name="backgroundLight">#4F4F4F</color>
     <color name="backgroundSearch">#33999999</color>
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index 26ed105e91..671f171c3e 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -39,6 +39,7 @@
     <!-- tags -->
     <string name="tagFragmentHome">tagFragmentHome</string>
     <string name="tagFragmentLogin">tagFragmentLogin</string>
+    <string name="tagFragmentLoggingIn">tagFragmentLogginIn</string>
     <string name="tagFragmentSignup">tagFragmentSignup</string>
     <string name="tagFragmentPolicy">tagFragmentPolicy</string>
     <string name="tagFragmentPeople">tagFragmentPeople</string>
@@ -52,4 +53,5 @@
     <string name="keep_me_logged_in">Keep Me Logged In</string>
     <string name="take_me_in_world">No thanks, take me in-world!</string>
     <string name="be_anywere">BE ANYWHERE, WITH ANYONE \nRIGHT NOW</string>
+    <string name="steam_log_in">STEAM LOG IN</string>
 </resources>
diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp
index d99c0020da..b2af35f16c 100644
--- a/libraries/networking/src/AccountManager.cpp
+++ b/libraries/networking/src/AccountManager.cpp
@@ -540,6 +540,30 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas
     connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished);
 }
 
+void AccountManager::requestAccessTokenWithAuthCode(const QString& authCode, const QString& clientId, const QString& clientSecret, const QString& redirectUri) {
+    QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+
+    QNetworkRequest request;
+    request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
+    request.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter());
+
+    QUrl grantURL = _authURL;
+    grantURL.setPath("/oauth/token");
+
+    QByteArray postData;
+    postData.append("grant_type=authorization_code&");
+    postData.append("client_id=" + clientId + "&");
+    postData.append("client_secret=" + clientSecret + "&");
+    postData.append("code=" + authCode + "&");
+    postData.append("redirect_uri=" + QUrl::toPercentEncoding(redirectUri));
+
+    request.setUrl(grantURL);
+    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
+
+    QNetworkReply* requestReply = networkAccessManager.post(request, postData);
+    connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished);
+}
+
 void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) {
     QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
 
diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h
index f3b81cf1c9..3185a8677b 100644
--- a/libraries/networking/src/AccountManager.h
+++ b/libraries/networking/src/AccountManager.h
@@ -101,6 +101,7 @@ public:
 public slots:
     void requestAccessToken(const QString& login, const QString& password);
     void requestAccessTokenWithSteam(QByteArray authSessionTicket);
+    void requestAccessTokenWithAuthCode(const QString& authCode, const QString& clientId, const QString& clientSecret, const QString& redirectUri);
     void refreshAccessToken();
 
     void requestAccessTokenFinished();