| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chromoting; | 5 package org.chromium.chromoting; |
| 6 | 6 |
| 7 import android.app.Activity; | 7 import android.app.Activity; |
| 8 import android.content.ActivityNotFoundException; | 8 import android.content.ActivityNotFoundException; |
| 9 import android.content.ComponentName; | 9 import android.content.ComponentName; |
| 10 import android.content.Intent; | 10 import android.content.Intent; |
| 11 import android.content.pm.PackageManager; | 11 import android.content.pm.PackageManager; |
| 12 import android.net.Uri; | 12 import android.net.Uri; |
| 13 import android.text.TextUtils; |
| 13 import android.util.Base64; | 14 import android.util.Base64; |
| 14 import android.util.Log; | 15 import android.util.Log; |
| 15 | 16 |
| 16 import java.security.SecureRandom; | 17 import java.security.SecureRandom; |
| 18 import java.util.ArrayList; |
| 17 import java.util.HashMap; | 19 import java.util.HashMap; |
| 18 | 20 |
| 19 /** | 21 /** |
| 20 * This class is responsible for fetching a third party token from the user usin
g the OAuth2 | 22 * This class is responsible for fetching a third party token from the user usin
g the OAuth2 |
| 21 * implicit flow. It pops up a third party login page located at |tokenurl|. I
t relies on the | 23 * implicit flow. It pops up a third party login page located at |tokenurl|. I
t relies on the |
| 22 * |ThirdPartyTokenFetcher$OAuthRedirectActivity| to intercept the access token
from the redirect at | 24 * |ThirdPartyTokenFetcher$OAuthRedirectActivity| to intercept the access token
from the redirect at |
| 23 * |REDIRECT_URI_SCHEME|://|REDIRECT_URI_HOST| upon successful login. | 25 * |REDIRECT_URI_SCHEME|://|REDIRECT_URI_HOST| upon successful login. |
| 24 */ | 26 */ |
| 25 public class ThirdPartyTokenFetcher { | 27 public class ThirdPartyTokenFetcher { |
| 26 /** Callback for receiving the token. */ | 28 /** Callback for receiving the token. */ |
| (...skipping 17 matching lines...) Expand all Loading... |
| 44 private Activity mContext; | 46 private Activity mContext; |
| 45 | 47 |
| 46 /** | 48 /** |
| 47 * An opaque value used by the client to maintain state between the request
and callback. The | 49 * An opaque value used by the client to maintain state between the request
and callback. The |
| 48 * authorization server includes this value when redirecting the user-agent
back to the client. | 50 * authorization server includes this value when redirecting the user-agent
back to the client. |
| 49 * The parameter is used for preventing cross-site request forgery. See | 51 * The parameter is used for preventing cross-site request forgery. See |
| 50 * http://tools.ietf.org/html/rfc6749#section-10.12. | 52 * http://tools.ietf.org/html/rfc6749#section-10.12. |
| 51 */ | 53 */ |
| 52 private final String mState; | 54 private final String mState; |
| 53 | 55 |
| 54 /** URL of the third party login page. */ | 56 private final Callback mCallback; |
| 55 private final String mTokenUrl; | |
| 56 | 57 |
| 57 /** The client identifier. See http://tools.ietf.org/html/rfc6749#section-2.
2. */ | 58 /** The list of TokenUrls allowed by the domain. */ |
| 58 private final String mClientId; | 59 private final ArrayList<String> mTokenUrlPatterns; |
| 59 | |
| 60 /** The scope of access request. See http://tools.ietf.org/html/rfc6749#sect
ion-3.3. */ | |
| 61 private final String mScope; | |
| 62 | |
| 63 private final Callback mCallback; | |
| 64 | 60 |
| 65 private final String mRedirectUriScheme; | 61 private final String mRedirectUriScheme; |
| 66 | 62 |
| 67 private final String mRedirectUri; | 63 private final String mRedirectUri; |
| 68 | 64 |
| 69 public ThirdPartyTokenFetcher(Activity context, | 65 public ThirdPartyTokenFetcher(Activity context, |
| 70 String tokenUrl, | 66 ArrayList<String> tokenUrlPatterns, |
| 71 String clientId, | |
| 72 String scope, | |
| 73 Callback callback) { | 67 Callback callback) { |
| 74 this.mContext = context; | 68 this.mContext = context; |
| 75 this.mTokenUrl = tokenUrl; | |
| 76 this.mClientId = clientId; | |
| 77 this.mState = generateXsrfToken(); | 69 this.mState = generateXsrfToken(); |
| 78 this.mScope = scope; | |
| 79 this.mCallback = callback; | 70 this.mCallback = callback; |
| 71 this.mTokenUrlPatterns = tokenUrlPatterns; |
| 80 | 72 |
| 81 this.mRedirectUriScheme = context.getApplicationContext().getPackageName
(); | 73 this.mRedirectUriScheme = context.getApplicationContext().getPackageName
(); |
| 82 this.mRedirectUri = mRedirectUriScheme + "://" + REDIRECT_URI_HOST; | 74 this.mRedirectUri = mRedirectUriScheme + "://" + REDIRECT_URI_HOST; |
| 83 } | 75 } |
| 84 | 76 |
| 85 public void fetchToken() { | 77 /** |
| 86 Uri.Builder uriBuilder = Uri.parse(mTokenUrl).buildUpon(); | 78 * @param tokenUrl URL of the third party login page. |
| 87 uriBuilder.appendQueryParameter("redirect_uri", this.mRedirectUri); | 79 * @param clientId The client identifier. See http://tools.ietf.org/html/rfc
6749#section-2.2. |
| 88 uriBuilder.appendQueryParameter("scope", mScope); | 80 * @param scope The scope of access request. See http://tools.ietf.org/html/
rfc6749#section-3.3. |
| 89 uriBuilder.appendQueryParameter("client_id", mClientId); | 81 */ |
| 90 uriBuilder.appendQueryParameter("state", mState); | 82 public void fetchToken(String tokenUrl, String clientId, String scope) { |
| 91 uriBuilder.appendQueryParameter("response_type", RESPONSE_TYPE); | 83 if (!isValidTokenUrl(tokenUrl)) { |
| 84 failFetchToken( |
| 85 "Token URL does not match the domain\'s allowed URL patterns
." + |
| 86 " URL: " + tokenUrl + |
| 87 ", patterns: " + TextUtils.join(",", this.mTokenUrlPatterns)
); |
| 88 return; |
| 89 } |
| 92 | 90 |
| 93 Uri uri = uriBuilder.build(); | 91 Uri uri = buildRequestUri(tokenUrl, clientId, scope); |
| 94 Intent intent = new Intent(Intent.ACTION_VIEW, uri); | 92 Intent intent = new Intent(Intent.ACTION_VIEW, uri); |
| 95 Log.i("ThirdPartyAuth", "fetchToken() url:" + uri); | 93 Log.i("ThirdPartyAuth", "fetchToken() url:" + uri); |
| 96 OAuthRedirectActivity.setEnabled(mContext, true); | 94 OAuthRedirectActivity.setEnabled(mContext, true); |
| 97 | 95 |
| 98 try { | 96 try { |
| 99 mContext.startActivity(intent); | 97 mContext.startActivity(intent); |
| 100 } catch (ActivityNotFoundException e) { | 98 } catch (ActivityNotFoundException e) { |
| 101 failFetchToken("No browser is installed to open the third party auth
entication page."); | 99 failFetchToken("No browser is installed to open the third party auth
entication page."); |
| 102 } | 100 } |
| 103 } | 101 } |
| 104 | 102 |
| 103 private Uri buildRequestUri(String tokenUrl, String clientId, String scope)
{ |
| 104 Uri.Builder uriBuilder = Uri.parse(tokenUrl).buildUpon(); |
| 105 uriBuilder.appendQueryParameter("redirect_uri", this.mRedirectUri); |
| 106 uriBuilder.appendQueryParameter("scope", scope); |
| 107 uriBuilder.appendQueryParameter("client_id", clientId); |
| 108 uriBuilder.appendQueryParameter("state", mState); |
| 109 uriBuilder.appendQueryParameter("response_type", RESPONSE_TYPE); |
| 110 |
| 111 return uriBuilder.build(); |
| 112 } |
| 113 |
| 114 /** Verifies the host-supplied URL matches the domain's allowed URL patterns
. */ |
| 115 private boolean isValidTokenUrl(String tokenUrl) { |
| 116 for (String pattern : mTokenUrlPatterns) { |
| 117 if (tokenUrl.matches(pattern)) { |
| 118 return true; |
| 119 } |
| 120 } |
| 121 return false; |
| 122 } |
| 123 |
| 105 private boolean isValidIntent(Intent intent) { | 124 private boolean isValidIntent(Intent intent) { |
| 106 assert intent != null; | 125 assert intent != null; |
| 107 | 126 |
| 108 String action = intent.getAction(); | 127 String action = intent.getAction(); |
| 109 | 128 |
| 110 Uri data = intent.getData(); | 129 Uri data = intent.getData(); |
| 111 if (data != null) { | 130 if (data != null) { |
| 112 return Intent.ACTION_VIEW.equals(action) && | 131 return Intent.ACTION_VIEW.equals(action) && |
| 113 this.mRedirectUriScheme.equals(data.getScheme()) && | 132 this.mRedirectUriScheme.equals(data.getScheme()) && |
| 114 REDIRECT_URI_HOST.equals(data.getHost()); | 133 REDIRECT_URI_HOST.equals(data.getHost()); |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 216 ComponentName component = new ComponentName( | 235 ComponentName component = new ComponentName( |
| 217 context.getApplicationContext(), | 236 context.getApplicationContext(), |
| 218 ThirdPartyTokenFetcher.OAuthRedirectActivity.class); | 237 ThirdPartyTokenFetcher.OAuthRedirectActivity.class); |
| 219 context.getPackageManager().setComponentEnabledSetting( | 238 context.getPackageManager().setComponentEnabledSetting( |
| 220 component, | 239 component, |
| 221 enabledState, | 240 enabledState, |
| 222 PackageManager.DONT_KILL_APP); | 241 PackageManager.DONT_KILL_APP); |
| 223 } | 242 } |
| 224 } | 243 } |
| 225 } | 244 } |
| OLD | NEW |