Skip to content

Commit 98a2ee7

Browse files
authored
Fix SSO login on ChromeOS by using Device Code Flow (#134)
* Fix SSO login on ChromeOS by using Device Code Flow ChromeOS runs Android apps in a container with separate network namespace, preventing the browser from reaching the localhost callback server used by PKCE flow. This causes "service not available" errors after authentication. Use Device Code Flow on ChromeOS (like Android TV) which uses polling instead of localhost callback. On ChromeOS, also auto-open the browser for a similar UX to PKCE while showing QR dialog as fallback. * update submodule
1 parent cfad8fd commit 98a2ee7

File tree

3 files changed

+31
-6
lines changed

3 files changed

+31
-6
lines changed

app/src/main/java/io/netbird/client/MainActivity.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ private enum ConnectionState {
7373

7474
private boolean isSSOFinishedWell = false;
7575
private boolean isRunningOnTV = false;
76+
private boolean useDeviceCodeFlow = false;
7677

7778
// Last known state for UI updates
7879
private ConnectionState lastKnownState = ConnectionState.UNKNOWN;
@@ -105,9 +106,13 @@ protected void onCreate(Bundle savedInstanceState) {
105106
setSupportActionBar(binding.appBarMain.toolbar);
106107

107108
isRunningOnTV = PlatformUtils.isAndroidTV(this);
109+
useDeviceCodeFlow = PlatformUtils.requiresDeviceCodeFlow(this);
108110
if (isRunningOnTV) {
109111
Log.i(LOGTAG, "Running on Android TV - optimizing for D-pad navigation");
110112
}
113+
if (useDeviceCodeFlow && !isRunningOnTV) {
114+
Log.i(LOGTAG, "Running on ChromeOS - using device code flow for authentication");
115+
}
111116

112117
setVersionText();
113118

@@ -177,7 +182,7 @@ public void onDrawerClosed(View drawerView) {
177182
}
178183
});
179184

180-
if (!isRunningOnTV) {
185+
if (!useDeviceCodeFlow) {
181186
urlOpener = new CustomTabURLOpener(this, () -> {
182187
if (isSSOFinishedWell) {
183188
return;
@@ -202,11 +207,20 @@ public void open(String url, String userCode) {
202207
mBinder.stopEngine();
203208
});
204209
qrCodeDialog.show(getSupportFragmentManager(), "QrCodeDialog");
210+
211+
if (!isRunningOnTV) {
212+
try {
213+
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
214+
startActivity(browserIntent);
215+
} catch (Exception e) {
216+
Log.e(LOGTAG, "Failed to open browser for device code flow: " + e.getMessage());
217+
}
218+
}
205219
}
206220

207221
@Override
208222
public void onLoginSuccess() {
209-
Log.d(LOGTAG, "onLoginSuccess fired for TV.");
223+
Log.d(LOGTAG, "onLoginSuccess fired for device code flow.");
210224
if (qrCodeDialog != null && qrCodeDialog.isVisible()) {
211225
qrCodeDialog.dismiss();
212226
qrCodeDialog = null;
@@ -233,12 +247,12 @@ public void onLoginSuccess() {
233247
if (VPNService.isUsingAlwaysOnVPN(this)) {
234248
showAlwaysOnDialog(() -> {
235249
if (mBinder != null) {
236-
mBinder.runEngine(urlOpener, isRunningOnTV);
250+
mBinder.runEngine(urlOpener, useDeviceCodeFlow);
237251
}
238252
});
239253
} else {
240254
if (mBinder != null) {
241-
mBinder.runEngine(urlOpener, isRunningOnTV);
255+
mBinder.runEngine(urlOpener, useDeviceCodeFlow);
242256
}
243257
}
244258
});
@@ -347,7 +361,7 @@ public void switchConnection(boolean status) {
347361
if (prepareIntent != null) {
348362
vpnActivityResultLauncher.launch(prepareIntent);
349363
} else {
350-
mBinder.runEngine(urlOpener, isRunningOnTV);
364+
mBinder.runEngine(urlOpener, useDeviceCodeFlow);
351365
}
352366
}
353367

app/src/main/java/io/netbird/client/PlatformUtils.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import android.app.UiModeManager;
44
import android.content.Context;
5+
import android.content.pm.PackageManager;
56
import android.content.res.Configuration;
67

78
public final class PlatformUtils {
@@ -16,5 +17,15 @@ public static boolean isAndroidTV(Context context) {
1617
}
1718
return false;
1819
}
20+
21+
public static boolean isChromeOS(Context context) {
22+
PackageManager pm = context.getPackageManager();
23+
return pm.hasSystemFeature("org.chromium.arc")
24+
|| pm.hasSystemFeature("org.chromium.arc.device_management");
25+
}
26+
27+
public static boolean requiresDeviceCodeFlow(Context context) {
28+
return isAndroidTV(context) || isChromeOS(context);
29+
}
1930
}
2031

netbird

Submodule netbird updated 116 files

0 commit comments

Comments
 (0)