Skip to content

Commit

Permalink
Third Party Authentication for Android Part VII - Security Review Fee…
Browse files Browse the repository at this point in the history
…dback

This CL changes the redirect URI to use the intent scheme specified in  https://developer.chrome.com/multidevice/android/intents.
The intent scheme can restrict a specific package to handle the intent and prevent a malicious app on the phone to intercept the access token.

Review URL: https://codereview.chromium.org/361053003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@280964 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
kelvinp@chromium.org committed Jul 2, 2014
1 parent f21ec37 commit 31d2d54
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 36 deletions.
2 changes: 1 addition & 1 deletion remoting/android/java/AndroidManifest.xml.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="{{ APK_PACKAGE_NAME }}"/>
<data android:host="oauthredirect"/>
<data android:path="/oauthredirect/"/>
</intent-filter>
</activity>
<activity android:name="org.chromium.chromoting.Desktop"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,21 @@

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;

/**
* This class is responsible for fetching a third party token from the user using the OAuth2
* implicit flow. It pops up a third party login page located at |tokenurl|. It relies on the
* |ThirdPartyTokenFetcher$OAuthRedirectActivity| to intercept the access token from the redirect at
* |REDIRECT_URI_SCHEME|://|REDIRECT_URI_HOST| upon successful login.
* implicit flow. It directs the user to a third party login page located at |tokenUrl|. It relies
* on the |ThirdPartyTokenFetcher$OAuthRedirectActivity| to intercept the access token from the
* redirect at intent://|REDIRECT_URI_PATH|#Intent;...end; upon successful login.
*/
public class ThirdPartyTokenFetcher {
/** Callback for receiving the token. */
public interface Callback {
void onTokenFetched(String code, String accessToken);
}

/** Redirect URI. See http://tools.ietf.org/html/rfc6749#section-3.1.2. */
private static final String REDIRECT_URI_HOST = "oauthredirect";
/** The path of the Redirect URI. */
private static final String REDIRECT_URI_PATH = "/oauthredirect/";

/**
* Request both the authorization code and access token from the server. See
Expand Down Expand Up @@ -71,7 +70,14 @@ public ThirdPartyTokenFetcher(Activity context,
this.mTokenUrlPatterns = tokenUrlPatterns;

this.mRedirectUriScheme = context.getApplicationContext().getPackageName();
this.mRedirectUri = mRedirectUriScheme + "://" + REDIRECT_URI_HOST;

// We don't follow the OAuth spec (http://tools.ietf.org/html/rfc6749#section-3.1.2) of the
// redirect URI as it is possible for the other applications to intercept the redirect URI.
// Instead, we use the intent scheme URI, which can restrict a specific package to handle
// the intent. See https://developer.chrome.com/multidevice/android/intents.
this.mRedirectUri = "intent://" + REDIRECT_URI_PATH + "#Intent;" +
"package=" + mRedirectUriScheme + ";" +
"scheme=" + mRedirectUriScheme + ";end;";
}

/**
Expand Down Expand Up @@ -130,7 +136,7 @@ private boolean isValidIntent(Intent intent) {
if (data != null) {
return Intent.ACTION_VIEW.equals(action) &&
this.mRedirectUriScheme.equals(data.getScheme()) &&
REDIRECT_URI_HOST.equals(data.getHost());
REDIRECT_URI_PATH.equals(data.getPath());
}
return false;
}
Expand All @@ -143,12 +149,9 @@ public boolean handleTokenFetched(Intent intent) {
return false;
}

Uri data = intent.getData();
HashMap<String, String> params = getFragmentParameters(data);

String accessToken = params.get("access_token");
String code = params.get("code");
String state = params.get("state");
String accessToken = intent.getStringExtra("access_token");
String code = intent.getStringExtra("code");
String state = intent.getStringExtra("state");

if (!mState.equals(state)) {
failFetchToken("Ignoring redirect with invalid state.");
Expand Down Expand Up @@ -183,29 +186,9 @@ private static String generateXsrfToken() {
return Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
}

/** Parses the fragment string into a key value pair. */
private static HashMap<String, String> getFragmentParameters(Uri uri) {
assert uri != null;
HashMap<String, String> result = new HashMap<String, String>();

String fragment = uri.getFragment();

if (fragment != null) {
String[] parts = fragment.split("&");

for (String part : parts) {
String keyValuePair[] = part.split("=", 2);
if (keyValuePair.length == 2) {
result.put(keyValuePair[0], keyValuePair[1]);
}
}
}
return result;
};

/**
* In the OAuth2 implicit flow, the browser will be redirected to
* |REDIRECT_URI_SCHEME|://|REDIRECT_URI_HOST| upon a successful login. OAuthRedirectActivity
* intent://|REDIRECT_URI_PATH|#Intent;...end; upon a successful login. OAuthRedirectActivity
* uses an intent filter in the manifest to intercept the URL and launch the chromoting app.
*
* Unfortunately, most browsers on Android, e.g. chrome, reload the URL when a browser
Expand Down

0 comments on commit 31d2d54

Please sign in to comment.