| 14792 |
manas |
1 |
/**
|
|
|
2 |
* Copyright 2010-present Facebook.
|
|
|
3 |
*
|
|
|
4 |
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
5 |
* you may not use this file except in compliance with the License.
|
|
|
6 |
* You may obtain a copy of the License at
|
|
|
7 |
*
|
|
|
8 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
9 |
*
|
|
|
10 |
* Unless required by applicable law or agreed to in writing, software
|
|
|
11 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
12 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
13 |
* See the License for the specific language governing permissions and
|
|
|
14 |
* limitations under the License.
|
|
|
15 |
*/
|
|
|
16 |
|
|
|
17 |
package com.facebook;
|
|
|
18 |
|
|
|
19 |
import android.Manifest;
|
|
|
20 |
import android.app.Activity;
|
|
|
21 |
import android.content.ActivityNotFoundException;
|
|
|
22 |
import android.content.Context;
|
|
|
23 |
import android.content.Intent;
|
|
|
24 |
import android.content.SharedPreferences;
|
|
|
25 |
import android.content.pm.PackageManager;
|
|
|
26 |
import android.os.Bundle;
|
|
|
27 |
import android.text.TextUtils;
|
|
|
28 |
import android.webkit.CookieSyncManager;
|
|
|
29 |
import com.facebook.android.R;
|
|
|
30 |
import com.facebook.internal.AnalyticsEvents;
|
|
|
31 |
import com.facebook.internal.NativeProtocol;
|
|
|
32 |
import com.facebook.internal.ServerProtocol;
|
|
|
33 |
import com.facebook.internal.Utility;
|
|
|
34 |
import com.facebook.model.GraphUser;
|
|
|
35 |
import com.facebook.widget.WebDialog;
|
|
|
36 |
import org.json.JSONException;
|
|
|
37 |
import org.json.JSONObject;
|
|
|
38 |
|
|
|
39 |
import java.io.Serializable;
|
|
|
40 |
import java.util.ArrayList;
|
|
|
41 |
import java.util.HashMap;
|
|
|
42 |
import java.util.List;
|
|
|
43 |
import java.util.Map;
|
|
|
44 |
|
|
|
45 |
class AuthorizationClient implements Serializable {
|
|
|
46 |
private static final long serialVersionUID = 1L;
|
|
|
47 |
private static final String TAG = "Facebook-AuthorizationClient";
|
|
|
48 |
private static final String WEB_VIEW_AUTH_HANDLER_STORE =
|
|
|
49 |
"com.facebook.AuthorizationClient.WebViewAuthHandler.TOKEN_STORE_KEY";
|
|
|
50 |
private static final String WEB_VIEW_AUTH_HANDLER_TOKEN_KEY = "TOKEN";
|
|
|
51 |
|
|
|
52 |
// Constants for logging login-related data. Some of these are only used by Session, but grouped here for
|
|
|
53 |
// maintainability.
|
|
|
54 |
private static final String EVENT_NAME_LOGIN_METHOD_START = "fb_mobile_login_method_start";
|
|
|
55 |
private static final String EVENT_NAME_LOGIN_METHOD_COMPLETE = "fb_mobile_login_method_complete";
|
|
|
56 |
private static final String EVENT_PARAM_METHOD_RESULT_SKIPPED = "skipped";
|
|
|
57 |
static final String EVENT_NAME_LOGIN_START = "fb_mobile_login_start";
|
|
|
58 |
static final String EVENT_NAME_LOGIN_COMPLETE = "fb_mobile_login_complete";
|
|
|
59 |
// Note: to ensure stability of column mappings across the four different event types, we prepend a column
|
|
|
60 |
// index to each name, and we log all columns with all events, even if they are empty.
|
|
|
61 |
static final String EVENT_PARAM_AUTH_LOGGER_ID = "0_auth_logger_id";
|
|
|
62 |
static final String EVENT_PARAM_TIMESTAMP = "1_timestamp_ms";
|
|
|
63 |
static final String EVENT_PARAM_LOGIN_RESULT = "2_result";
|
|
|
64 |
static final String EVENT_PARAM_METHOD = "3_method";
|
|
|
65 |
static final String EVENT_PARAM_ERROR_CODE = "4_error_code";
|
|
|
66 |
static final String EVENT_PARAM_ERROR_MESSAGE = "5_error_message";
|
|
|
67 |
static final String EVENT_PARAM_EXTRAS = "6_extras";
|
|
|
68 |
static final String EVENT_EXTRAS_TRY_LOGIN_ACTIVITY = "try_login_activity";
|
|
|
69 |
static final String EVENT_EXTRAS_TRY_LEGACY = "try_legacy";
|
|
|
70 |
static final String EVENT_EXTRAS_LOGIN_BEHAVIOR = "login_behavior";
|
|
|
71 |
static final String EVENT_EXTRAS_REQUEST_CODE = "request_code";
|
|
|
72 |
static final String EVENT_EXTRAS_IS_LEGACY = "is_legacy";
|
|
|
73 |
static final String EVENT_EXTRAS_PERMISSIONS = "permissions";
|
|
|
74 |
static final String EVENT_EXTRAS_DEFAULT_AUDIENCE = "default_audience";
|
|
|
75 |
static final String EVENT_EXTRAS_MISSING_INTERNET_PERMISSION = "no_internet_permission";
|
|
|
76 |
static final String EVENT_EXTRAS_NOT_TRIED = "not_tried";
|
|
|
77 |
static final String EVENT_EXTRAS_NEW_PERMISSIONS = "new_permissions";
|
|
|
78 |
|
|
|
79 |
List<AuthHandler> handlersToTry;
|
|
|
80 |
AuthHandler currentHandler;
|
|
|
81 |
transient Context context;
|
|
|
82 |
transient StartActivityDelegate startActivityDelegate;
|
|
|
83 |
transient OnCompletedListener onCompletedListener;
|
|
|
84 |
transient BackgroundProcessingListener backgroundProcessingListener;
|
|
|
85 |
transient boolean checkedInternetPermission;
|
|
|
86 |
AuthorizationRequest pendingRequest;
|
|
|
87 |
Map<String, String> loggingExtras;
|
|
|
88 |
private transient AppEventsLogger appEventsLogger;
|
|
|
89 |
|
|
|
90 |
interface OnCompletedListener {
|
|
|
91 |
void onCompleted(Result result);
|
|
|
92 |
}
|
|
|
93 |
|
|
|
94 |
interface BackgroundProcessingListener {
|
|
|
95 |
void onBackgroundProcessingStarted();
|
|
|
96 |
|
|
|
97 |
void onBackgroundProcessingStopped();
|
|
|
98 |
}
|
|
|
99 |
|
|
|
100 |
interface StartActivityDelegate {
|
|
|
101 |
public void startActivityForResult(Intent intent, int requestCode);
|
|
|
102 |
|
|
|
103 |
public Activity getActivityContext();
|
|
|
104 |
}
|
|
|
105 |
|
|
|
106 |
void setContext(final Context context) {
|
|
|
107 |
this.context = context;
|
|
|
108 |
// We rely on individual requests to tell us how to start an activity.
|
|
|
109 |
startActivityDelegate = null;
|
|
|
110 |
}
|
|
|
111 |
|
|
|
112 |
void setContext(final Activity activity) {
|
|
|
113 |
this.context = activity;
|
|
|
114 |
|
|
|
115 |
// If we are used in the context of an activity, we will always use that activity to
|
|
|
116 |
// call startActivityForResult.
|
|
|
117 |
startActivityDelegate = new StartActivityDelegate() {
|
|
|
118 |
@Override
|
|
|
119 |
public void startActivityForResult(Intent intent, int requestCode) {
|
|
|
120 |
activity.startActivityForResult(intent, requestCode);
|
|
|
121 |
}
|
|
|
122 |
|
|
|
123 |
@Override
|
|
|
124 |
public Activity getActivityContext() {
|
|
|
125 |
return activity;
|
|
|
126 |
}
|
|
|
127 |
};
|
|
|
128 |
}
|
|
|
129 |
|
|
|
130 |
void startOrContinueAuth(AuthorizationRequest request) {
|
|
|
131 |
if (getInProgress()) {
|
|
|
132 |
continueAuth();
|
|
|
133 |
} else {
|
|
|
134 |
authorize(request);
|
|
|
135 |
}
|
|
|
136 |
}
|
|
|
137 |
|
|
|
138 |
void authorize(AuthorizationRequest request) {
|
|
|
139 |
if (request == null) {
|
|
|
140 |
return;
|
|
|
141 |
}
|
|
|
142 |
|
|
|
143 |
if (pendingRequest != null) {
|
|
|
144 |
throw new FacebookException("Attempted to authorize while a request is pending.");
|
|
|
145 |
}
|
|
|
146 |
|
|
|
147 |
if (request.needsNewTokenValidation() && !checkInternetPermission()) {
|
|
|
148 |
// We're going to need INTERNET permission later and don't have it, so fail early.
|
|
|
149 |
return;
|
|
|
150 |
}
|
|
|
151 |
pendingRequest = request;
|
|
|
152 |
handlersToTry = getHandlerTypes(request);
|
|
|
153 |
tryNextHandler();
|
|
|
154 |
}
|
|
|
155 |
|
|
|
156 |
void continueAuth() {
|
|
|
157 |
if (pendingRequest == null || currentHandler == null) {
|
|
|
158 |
throw new FacebookException("Attempted to continue authorization without a pending request.");
|
|
|
159 |
}
|
|
|
160 |
|
|
|
161 |
if (currentHandler.needsRestart()) {
|
|
|
162 |
currentHandler.cancel();
|
|
|
163 |
tryCurrentHandler();
|
|
|
164 |
}
|
|
|
165 |
}
|
|
|
166 |
|
|
|
167 |
boolean getInProgress() {
|
|
|
168 |
return pendingRequest != null && currentHandler != null;
|
|
|
169 |
}
|
|
|
170 |
|
|
|
171 |
void cancelCurrentHandler() {
|
|
|
172 |
if (currentHandler != null) {
|
|
|
173 |
currentHandler.cancel();
|
|
|
174 |
}
|
|
|
175 |
}
|
|
|
176 |
|
|
|
177 |
boolean onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
|
178 |
if (pendingRequest != null && requestCode == pendingRequest.getRequestCode()) {
|
|
|
179 |
return currentHandler.onActivityResult(requestCode, resultCode, data);
|
|
|
180 |
}
|
|
|
181 |
return false;
|
|
|
182 |
}
|
|
|
183 |
|
|
|
184 |
private List<AuthHandler> getHandlerTypes(AuthorizationRequest request) {
|
|
|
185 |
ArrayList<AuthHandler> handlers = new ArrayList<AuthHandler>();
|
|
|
186 |
|
|
|
187 |
final SessionLoginBehavior behavior = request.getLoginBehavior();
|
|
|
188 |
if (behavior.allowsKatanaAuth()) {
|
|
|
189 |
if (!request.isLegacy()) {
|
|
|
190 |
handlers.add(new GetTokenAuthHandler());
|
|
|
191 |
}
|
|
|
192 |
handlers.add(new KatanaProxyAuthHandler());
|
|
|
193 |
}
|
|
|
194 |
|
|
|
195 |
if (behavior.allowsWebViewAuth()) {
|
|
|
196 |
handlers.add(new WebViewAuthHandler());
|
|
|
197 |
}
|
|
|
198 |
|
|
|
199 |
return handlers;
|
|
|
200 |
}
|
|
|
201 |
|
|
|
202 |
boolean checkInternetPermission() {
|
|
|
203 |
if (checkedInternetPermission) {
|
|
|
204 |
return true;
|
|
|
205 |
}
|
|
|
206 |
|
|
|
207 |
int permissionCheck = checkPermission(Manifest.permission.INTERNET);
|
|
|
208 |
if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
|
|
|
209 |
String errorType = context.getString(R.string.com_facebook_internet_permission_error_title);
|
|
|
210 |
String errorDescription = context.getString(R.string.com_facebook_internet_permission_error_message);
|
|
|
211 |
complete(Result.createErrorResult(pendingRequest, errorType, errorDescription));
|
|
|
212 |
|
|
|
213 |
return false;
|
|
|
214 |
}
|
|
|
215 |
|
|
|
216 |
checkedInternetPermission = true;
|
|
|
217 |
return true;
|
|
|
218 |
}
|
|
|
219 |
|
|
|
220 |
void tryNextHandler() {
|
|
|
221 |
if (currentHandler != null) {
|
|
|
222 |
logAuthorizationMethodComplete(currentHandler.getNameForLogging(), EVENT_PARAM_METHOD_RESULT_SKIPPED,
|
|
|
223 |
null, null, currentHandler.methodLoggingExtras);
|
|
|
224 |
}
|
|
|
225 |
|
|
|
226 |
while (handlersToTry != null && !handlersToTry.isEmpty()) {
|
|
|
227 |
currentHandler = handlersToTry.remove(0);
|
|
|
228 |
|
|
|
229 |
boolean started = tryCurrentHandler();
|
|
|
230 |
|
|
|
231 |
if (started) {
|
|
|
232 |
return;
|
|
|
233 |
}
|
|
|
234 |
}
|
|
|
235 |
|
|
|
236 |
if (pendingRequest != null) {
|
|
|
237 |
// We went through all handlers without successfully attempting an auth.
|
|
|
238 |
completeWithFailure();
|
|
|
239 |
}
|
|
|
240 |
}
|
|
|
241 |
|
|
|
242 |
private void completeWithFailure() {
|
|
|
243 |
complete(Result.createErrorResult(pendingRequest, "Login attempt failed.", null));
|
|
|
244 |
}
|
|
|
245 |
|
|
|
246 |
private void addLoggingExtra(String key, String value, boolean accumulate) {
|
|
|
247 |
if (loggingExtras == null) {
|
|
|
248 |
loggingExtras = new HashMap<String, String>();
|
|
|
249 |
}
|
|
|
250 |
if (loggingExtras.containsKey(key) && accumulate) {
|
|
|
251 |
value = loggingExtras.get(key) + "," + value;
|
|
|
252 |
}
|
|
|
253 |
loggingExtras.put(key, value);
|
|
|
254 |
}
|
|
|
255 |
|
|
|
256 |
boolean tryCurrentHandler() {
|
|
|
257 |
if (currentHandler.needsInternetPermission() && !checkInternetPermission()) {
|
|
|
258 |
addLoggingExtra(EVENT_EXTRAS_MISSING_INTERNET_PERMISSION, AppEventsConstants.EVENT_PARAM_VALUE_YES,
|
|
|
259 |
false);
|
|
|
260 |
return false;
|
|
|
261 |
}
|
|
|
262 |
|
|
|
263 |
boolean tried = currentHandler.tryAuthorize(pendingRequest);
|
|
|
264 |
if (tried) {
|
|
|
265 |
logAuthorizationMethodStart(currentHandler.getNameForLogging());
|
|
|
266 |
} else {
|
|
|
267 |
// We didn't try it, so we don't get any other completion notification -- log that we skipped it.
|
|
|
268 |
addLoggingExtra(EVENT_EXTRAS_NOT_TRIED, currentHandler.getNameForLogging(), true);
|
|
|
269 |
}
|
|
|
270 |
|
|
|
271 |
return tried;
|
|
|
272 |
}
|
|
|
273 |
|
|
|
274 |
void completeAndValidate(Result outcome) {
|
|
|
275 |
// Do we need to validate a successful result (as in the case of a reauth)?
|
|
|
276 |
if (outcome.token != null && pendingRequest.needsNewTokenValidation()) {
|
|
|
277 |
validateSameFbidAndFinish(outcome);
|
|
|
278 |
} else {
|
|
|
279 |
// We're done, just notify the listener.
|
|
|
280 |
complete(outcome);
|
|
|
281 |
}
|
|
|
282 |
}
|
|
|
283 |
|
|
|
284 |
void complete(Result outcome) {
|
|
|
285 |
// This might be null if, for some reason, none of the handlers were successfully tried (in which case
|
|
|
286 |
// we already logged that).
|
|
|
287 |
if (currentHandler != null) {
|
|
|
288 |
logAuthorizationMethodComplete(currentHandler.getNameForLogging(), outcome,
|
|
|
289 |
currentHandler.methodLoggingExtras);
|
|
|
290 |
}
|
|
|
291 |
|
|
|
292 |
if (loggingExtras != null) {
|
|
|
293 |
// Pass this back to the caller for logging at the aggregate level.
|
|
|
294 |
outcome.loggingExtras = loggingExtras;
|
|
|
295 |
}
|
|
|
296 |
|
|
|
297 |
handlersToTry = null;
|
|
|
298 |
currentHandler = null;
|
|
|
299 |
pendingRequest = null;
|
|
|
300 |
loggingExtras = null;
|
|
|
301 |
|
|
|
302 |
notifyOnCompleteListener(outcome);
|
|
|
303 |
}
|
|
|
304 |
|
|
|
305 |
OnCompletedListener getOnCompletedListener() {
|
|
|
306 |
return onCompletedListener;
|
|
|
307 |
}
|
|
|
308 |
|
|
|
309 |
void setOnCompletedListener(OnCompletedListener onCompletedListener) {
|
|
|
310 |
this.onCompletedListener = onCompletedListener;
|
|
|
311 |
}
|
|
|
312 |
|
|
|
313 |
BackgroundProcessingListener getBackgroundProcessingListener() {
|
|
|
314 |
return backgroundProcessingListener;
|
|
|
315 |
}
|
|
|
316 |
|
|
|
317 |
void setBackgroundProcessingListener(BackgroundProcessingListener backgroundProcessingListener) {
|
|
|
318 |
this.backgroundProcessingListener = backgroundProcessingListener;
|
|
|
319 |
}
|
|
|
320 |
|
|
|
321 |
StartActivityDelegate getStartActivityDelegate() {
|
|
|
322 |
if (startActivityDelegate != null) {
|
|
|
323 |
return startActivityDelegate;
|
|
|
324 |
} else if (pendingRequest != null) {
|
|
|
325 |
// Wrap the request's delegate in our own.
|
|
|
326 |
return new StartActivityDelegate() {
|
|
|
327 |
@Override
|
|
|
328 |
public void startActivityForResult(Intent intent, int requestCode) {
|
|
|
329 |
pendingRequest.getStartActivityDelegate().startActivityForResult(intent, requestCode);
|
|
|
330 |
}
|
|
|
331 |
|
|
|
332 |
@Override
|
|
|
333 |
public Activity getActivityContext() {
|
|
|
334 |
return pendingRequest.getStartActivityDelegate().getActivityContext();
|
|
|
335 |
}
|
|
|
336 |
};
|
|
|
337 |
}
|
|
|
338 |
return null;
|
|
|
339 |
}
|
|
|
340 |
|
|
|
341 |
int checkPermission(String permission) {
|
|
|
342 |
return context.checkCallingOrSelfPermission(permission);
|
|
|
343 |
}
|
|
|
344 |
|
|
|
345 |
void validateSameFbidAndFinish(Result pendingResult) {
|
|
|
346 |
if (pendingResult.token == null) {
|
|
|
347 |
throw new FacebookException("Can't validate without a token");
|
|
|
348 |
}
|
|
|
349 |
|
|
|
350 |
RequestBatch batch = createReauthValidationBatch(pendingResult);
|
|
|
351 |
|
|
|
352 |
notifyBackgroundProcessingStart();
|
|
|
353 |
|
|
|
354 |
batch.executeAsync();
|
|
|
355 |
}
|
|
|
356 |
|
|
|
357 |
RequestBatch createReauthValidationBatch(final Result pendingResult) {
|
|
|
358 |
// We need to ensure that the token we got represents the same fbid as the old one. We issue
|
|
|
359 |
// a "me" request using the current token, a "me" request using the new token, and a "me/permissions"
|
|
|
360 |
// request using the current token to get the permissions of the user.
|
|
|
361 |
|
|
|
362 |
final ArrayList<String> fbids = new ArrayList<String>();
|
|
|
363 |
final ArrayList<String> grantedPermissions = new ArrayList<String>();
|
|
|
364 |
final ArrayList<String> declinedPermissions = new ArrayList<String>();
|
|
|
365 |
final String newToken = pendingResult.token.getToken();
|
|
|
366 |
|
|
|
367 |
Request.Callback meCallback = new Request.Callback() {
|
|
|
368 |
@Override
|
|
|
369 |
public void onCompleted(Response response) {
|
|
|
370 |
try {
|
|
|
371 |
GraphUser user = response.getGraphObjectAs(GraphUser.class);
|
|
|
372 |
if (user != null) {
|
|
|
373 |
fbids.add(user.getId());
|
|
|
374 |
}
|
|
|
375 |
} catch (Exception ex) {
|
|
|
376 |
}
|
|
|
377 |
}
|
|
|
378 |
};
|
|
|
379 |
|
|
|
380 |
String validateSameFbidAsToken = pendingRequest.getPreviousAccessToken();
|
|
|
381 |
Request requestCurrentTokenMe = createGetProfileIdRequest(validateSameFbidAsToken);
|
|
|
382 |
requestCurrentTokenMe.setCallback(meCallback);
|
|
|
383 |
|
|
|
384 |
Request requestNewTokenMe = createGetProfileIdRequest(newToken);
|
|
|
385 |
requestNewTokenMe.setCallback(meCallback);
|
|
|
386 |
|
|
|
387 |
Request requestCurrentTokenPermissions = createGetPermissionsRequest(validateSameFbidAsToken);
|
|
|
388 |
requestCurrentTokenPermissions.setCallback(new Request.Callback() {
|
|
|
389 |
@Override
|
|
|
390 |
public void onCompleted(Response response) {
|
|
|
391 |
try {
|
|
|
392 |
Session.PermissionsPair permissionsPair = Session.handlePermissionResponse(response);
|
|
|
393 |
if (permissionsPair != null) {
|
|
|
394 |
grantedPermissions.addAll(permissionsPair.getGrantedPermissions());
|
|
|
395 |
declinedPermissions.addAll(permissionsPair.getDeclinedPermissions());
|
|
|
396 |
}
|
|
|
397 |
} catch (Exception ex) {
|
|
|
398 |
}
|
|
|
399 |
}
|
|
|
400 |
});
|
|
|
401 |
|
|
|
402 |
RequestBatch batch = new RequestBatch(requestCurrentTokenMe, requestNewTokenMe,
|
|
|
403 |
requestCurrentTokenPermissions);
|
|
|
404 |
batch.setBatchApplicationId(pendingRequest.getApplicationId());
|
|
|
405 |
batch.addCallback(new RequestBatch.Callback() {
|
|
|
406 |
@Override
|
|
|
407 |
public void onBatchCompleted(RequestBatch batch) {
|
|
|
408 |
try {
|
|
|
409 |
Result result = null;
|
|
|
410 |
if (fbids.size() == 2 && fbids.get(0) != null && fbids.get(1) != null &&
|
|
|
411 |
fbids.get(0).equals(fbids.get(1))) {
|
|
|
412 |
// Modify the token to have the right permission set.
|
|
|
413 |
AccessToken tokenWithPermissions = AccessToken
|
|
|
414 |
.createFromTokenWithRefreshedPermissions(pendingResult.token,
|
|
|
415 |
grantedPermissions, declinedPermissions);
|
|
|
416 |
result = Result.createTokenResult(pendingRequest, tokenWithPermissions);
|
|
|
417 |
} else {
|
|
|
418 |
result = Result
|
|
|
419 |
.createErrorResult(pendingRequest, "User logged in as different Facebook user.", null);
|
|
|
420 |
}
|
|
|
421 |
complete(result);
|
|
|
422 |
} catch (Exception ex) {
|
|
|
423 |
complete(Result.createErrorResult(pendingRequest, "Caught exception", ex.getMessage()));
|
|
|
424 |
} finally {
|
|
|
425 |
notifyBackgroundProcessingStop();
|
|
|
426 |
}
|
|
|
427 |
}
|
|
|
428 |
});
|
|
|
429 |
|
|
|
430 |
return batch;
|
|
|
431 |
}
|
|
|
432 |
|
|
|
433 |
Request createGetPermissionsRequest(String accessToken) {
|
|
|
434 |
Bundle parameters = new Bundle();
|
|
|
435 |
parameters.putString("access_token", accessToken);
|
|
|
436 |
return new Request(null, "me/permissions", parameters, HttpMethod.GET, null);
|
|
|
437 |
}
|
|
|
438 |
|
|
|
439 |
Request createGetProfileIdRequest(String accessToken) {
|
|
|
440 |
Bundle parameters = new Bundle();
|
|
|
441 |
parameters.putString("fields", "id");
|
|
|
442 |
parameters.putString("access_token", accessToken);
|
|
|
443 |
return new Request(null, "me", parameters, HttpMethod.GET, null);
|
|
|
444 |
}
|
|
|
445 |
|
|
|
446 |
private AppEventsLogger getAppEventsLogger() {
|
|
|
447 |
if (appEventsLogger == null || !appEventsLogger.getApplicationId().equals(pendingRequest.getApplicationId())) {
|
|
|
448 |
appEventsLogger = AppEventsLogger.newLogger(context, pendingRequest.getApplicationId());
|
|
|
449 |
}
|
|
|
450 |
return appEventsLogger;
|
|
|
451 |
}
|
|
|
452 |
|
|
|
453 |
private void notifyOnCompleteListener(Result outcome) {
|
|
|
454 |
if (onCompletedListener != null) {
|
|
|
455 |
onCompletedListener.onCompleted(outcome);
|
|
|
456 |
}
|
|
|
457 |
}
|
|
|
458 |
|
|
|
459 |
private void notifyBackgroundProcessingStart() {
|
|
|
460 |
if (backgroundProcessingListener != null) {
|
|
|
461 |
backgroundProcessingListener.onBackgroundProcessingStarted();
|
|
|
462 |
}
|
|
|
463 |
}
|
|
|
464 |
|
|
|
465 |
private void notifyBackgroundProcessingStop() {
|
|
|
466 |
if (backgroundProcessingListener != null) {
|
|
|
467 |
backgroundProcessingListener.onBackgroundProcessingStopped();
|
|
|
468 |
}
|
|
|
469 |
}
|
|
|
470 |
|
|
|
471 |
private void logAuthorizationMethodStart(String method) {
|
|
|
472 |
Bundle bundle = newAuthorizationLoggingBundle(pendingRequest.getAuthId());
|
|
|
473 |
bundle.putLong(EVENT_PARAM_TIMESTAMP, System.currentTimeMillis());
|
|
|
474 |
bundle.putString(EVENT_PARAM_METHOD, method);
|
|
|
475 |
|
|
|
476 |
getAppEventsLogger().logSdkEvent(EVENT_NAME_LOGIN_METHOD_START, null, bundle);
|
|
|
477 |
}
|
|
|
478 |
|
|
|
479 |
private void logAuthorizationMethodComplete(String method, Result result, Map<String, String> loggingExtras) {
|
|
|
480 |
logAuthorizationMethodComplete(method, result.code.getLoggingValue(), result.errorMessage, result.errorCode,
|
|
|
481 |
loggingExtras);
|
|
|
482 |
}
|
|
|
483 |
|
|
|
484 |
private void logAuthorizationMethodComplete(String method, String result, String errorMessage, String errorCode,
|
|
|
485 |
Map<String, String> loggingExtras) {
|
|
|
486 |
Bundle bundle = null;
|
|
|
487 |
if (pendingRequest == null) {
|
|
|
488 |
// We don't expect this to happen, but if it does, log an event for diagnostic purposes.
|
|
|
489 |
bundle = newAuthorizationLoggingBundle("");
|
|
|
490 |
bundle.putString(EVENT_PARAM_LOGIN_RESULT, Result.Code.ERROR.getLoggingValue());
|
|
|
491 |
bundle.putString(EVENT_PARAM_ERROR_MESSAGE,
|
|
|
492 |
"Unexpected call to logAuthorizationMethodComplete with null pendingRequest.");
|
|
|
493 |
} else {
|
|
|
494 |
bundle = newAuthorizationLoggingBundle(pendingRequest.getAuthId());
|
|
|
495 |
if (result != null) {
|
|
|
496 |
bundle.putString(EVENT_PARAM_LOGIN_RESULT, result);
|
|
|
497 |
}
|
|
|
498 |
if (errorMessage != null) {
|
|
|
499 |
bundle.putString(EVENT_PARAM_ERROR_MESSAGE, errorMessage);
|
|
|
500 |
}
|
|
|
501 |
if (errorCode != null) {
|
|
|
502 |
bundle.putString(EVENT_PARAM_ERROR_CODE, errorCode);
|
|
|
503 |
}
|
|
|
504 |
if (loggingExtras != null && !loggingExtras.isEmpty()) {
|
|
|
505 |
JSONObject jsonObject = new JSONObject(loggingExtras);
|
|
|
506 |
bundle.putString(EVENT_PARAM_EXTRAS, jsonObject.toString());
|
|
|
507 |
}
|
|
|
508 |
}
|
|
|
509 |
bundle.putString(EVENT_PARAM_METHOD, method);
|
|
|
510 |
bundle.putLong(EVENT_PARAM_TIMESTAMP, System.currentTimeMillis());
|
|
|
511 |
|
|
|
512 |
getAppEventsLogger().logSdkEvent(EVENT_NAME_LOGIN_METHOD_COMPLETE, null, bundle);
|
|
|
513 |
}
|
|
|
514 |
|
|
|
515 |
static Bundle newAuthorizationLoggingBundle(String authLoggerId) {
|
|
|
516 |
// We want to log all parameters for all events, to ensure stability of columns across different event types.
|
|
|
517 |
Bundle bundle = new Bundle();
|
|
|
518 |
bundle.putLong(EVENT_PARAM_TIMESTAMP, System.currentTimeMillis());
|
|
|
519 |
bundle.putString(EVENT_PARAM_AUTH_LOGGER_ID, authLoggerId);
|
|
|
520 |
bundle.putString(EVENT_PARAM_METHOD, "");
|
|
|
521 |
bundle.putString(EVENT_PARAM_LOGIN_RESULT, "");
|
|
|
522 |
bundle.putString(EVENT_PARAM_ERROR_MESSAGE, "");
|
|
|
523 |
bundle.putString(EVENT_PARAM_ERROR_CODE, "");
|
|
|
524 |
bundle.putString(EVENT_PARAM_EXTRAS, "");
|
|
|
525 |
return bundle;
|
|
|
526 |
}
|
|
|
527 |
|
|
|
528 |
abstract class AuthHandler implements Serializable {
|
|
|
529 |
private static final long serialVersionUID = 1L;
|
|
|
530 |
|
|
|
531 |
Map<String, String> methodLoggingExtras;
|
|
|
532 |
|
|
|
533 |
abstract boolean tryAuthorize(AuthorizationRequest request);
|
|
|
534 |
abstract String getNameForLogging();
|
|
|
535 |
|
|
|
536 |
boolean onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
|
537 |
return false;
|
|
|
538 |
}
|
|
|
539 |
|
|
|
540 |
boolean needsRestart() {
|
|
|
541 |
return false;
|
|
|
542 |
}
|
|
|
543 |
|
|
|
544 |
boolean needsInternetPermission() {
|
|
|
545 |
return false;
|
|
|
546 |
}
|
|
|
547 |
|
|
|
548 |
void cancel() {
|
|
|
549 |
}
|
|
|
550 |
|
|
|
551 |
protected void addLoggingExtra(String key, Object value) {
|
|
|
552 |
if (methodLoggingExtras == null) {
|
|
|
553 |
methodLoggingExtras = new HashMap<String, String>();
|
|
|
554 |
}
|
|
|
555 |
methodLoggingExtras.put(key, value == null ? null : value.toString());
|
|
|
556 |
}
|
|
|
557 |
}
|
|
|
558 |
|
|
|
559 |
class WebViewAuthHandler extends AuthHandler {
|
|
|
560 |
private static final long serialVersionUID = 1L;
|
|
|
561 |
private transient WebDialog loginDialog;
|
|
|
562 |
private String applicationId;
|
|
|
563 |
private String e2e;
|
|
|
564 |
|
|
|
565 |
@Override
|
|
|
566 |
String getNameForLogging() {
|
|
|
567 |
return "web_view";
|
|
|
568 |
}
|
|
|
569 |
|
|
|
570 |
@Override
|
|
|
571 |
boolean needsRestart() {
|
|
|
572 |
// Because we are presenting WebView UI within the current context, we need to explicitly
|
|
|
573 |
// restart the process if the context goes away and is recreated.
|
|
|
574 |
return true;
|
|
|
575 |
}
|
|
|
576 |
|
|
|
577 |
@Override
|
|
|
578 |
boolean needsInternetPermission() {
|
|
|
579 |
return true;
|
|
|
580 |
}
|
|
|
581 |
|
|
|
582 |
@Override
|
|
|
583 |
void cancel() {
|
|
|
584 |
if (loginDialog != null) {
|
|
|
585 |
// Since we are calling dismiss explicitly, we need to remove the completion listener to prevent
|
|
|
586 |
// responding to the upcoming "Cancel" result.
|
|
|
587 |
loginDialog.setOnCompleteListener(null);
|
|
|
588 |
loginDialog.dismiss();
|
|
|
589 |
loginDialog = null;
|
|
|
590 |
}
|
|
|
591 |
}
|
|
|
592 |
|
|
|
593 |
@Override
|
|
|
594 |
boolean tryAuthorize(final AuthorizationRequest request) {
|
|
|
595 |
applicationId = request.getApplicationId();
|
|
|
596 |
Bundle parameters = new Bundle();
|
|
|
597 |
if (!Utility.isNullOrEmpty(request.getPermissions())) {
|
|
|
598 |
String scope = TextUtils.join(",", request.getPermissions());
|
|
|
599 |
parameters.putString(ServerProtocol.DIALOG_PARAM_SCOPE, scope);
|
|
|
600 |
addLoggingExtra(ServerProtocol.DIALOG_PARAM_SCOPE, scope);
|
|
|
601 |
}
|
|
|
602 |
|
|
|
603 |
SessionDefaultAudience audience = request.getDefaultAudience();
|
|
|
604 |
parameters.putString(ServerProtocol.DIALOG_PARAM_DEFAULT_AUDIENCE, audience.getNativeProtocolAudience());
|
|
|
605 |
|
|
|
606 |
String previousToken = request.getPreviousAccessToken();
|
|
|
607 |
if (!Utility.isNullOrEmpty(previousToken) && (previousToken.equals(loadCookieToken()))) {
|
|
|
608 |
parameters.putString(ServerProtocol.DIALOG_PARAM_ACCESS_TOKEN, previousToken);
|
|
|
609 |
// Don't log the actual access token, just its presence or absence.
|
|
|
610 |
addLoggingExtra(ServerProtocol.DIALOG_PARAM_ACCESS_TOKEN, AppEventsConstants.EVENT_PARAM_VALUE_YES);
|
|
|
611 |
} else {
|
|
|
612 |
// The call to clear cookies will create the first instance of CookieSyncManager if necessary
|
|
|
613 |
Utility.clearFacebookCookies(context);
|
|
|
614 |
addLoggingExtra(ServerProtocol.DIALOG_PARAM_ACCESS_TOKEN, AppEventsConstants.EVENT_PARAM_VALUE_NO);
|
|
|
615 |
}
|
|
|
616 |
|
|
|
617 |
WebDialog.OnCompleteListener listener = new WebDialog.OnCompleteListener() {
|
|
|
618 |
@Override
|
|
|
619 |
public void onComplete(Bundle values, FacebookException error) {
|
|
|
620 |
onWebDialogComplete(request, values, error);
|
|
|
621 |
}
|
|
|
622 |
};
|
|
|
623 |
|
|
|
624 |
e2e = getE2E();
|
|
|
625 |
addLoggingExtra(ServerProtocol.DIALOG_PARAM_E2E, e2e);
|
|
|
626 |
|
|
|
627 |
WebDialog.Builder builder =
|
|
|
628 |
new AuthDialogBuilder(getStartActivityDelegate().getActivityContext(), applicationId, parameters)
|
|
|
629 |
.setE2E(e2e)
|
|
|
630 |
.setIsRerequest(request.isRerequest())
|
|
|
631 |
.setOnCompleteListener(listener);
|
|
|
632 |
loginDialog = builder.build();
|
|
|
633 |
loginDialog.show();
|
|
|
634 |
|
|
|
635 |
return true;
|
|
|
636 |
}
|
|
|
637 |
|
|
|
638 |
void onWebDialogComplete(AuthorizationRequest request, Bundle values,
|
|
|
639 |
FacebookException error) {
|
|
|
640 |
Result outcome;
|
|
|
641 |
if (values != null) {
|
|
|
642 |
// Actual e2e we got from the dialog should be used for logging.
|
|
|
643 |
if (values.containsKey(ServerProtocol.DIALOG_PARAM_E2E)) {
|
|
|
644 |
e2e = values.getString(ServerProtocol.DIALOG_PARAM_E2E);
|
|
|
645 |
}
|
|
|
646 |
|
|
|
647 |
AccessToken token = AccessToken
|
|
|
648 |
.createFromWebBundle(request.getPermissions(), values, AccessTokenSource.WEB_VIEW);
|
|
|
649 |
outcome = Result.createTokenResult(pendingRequest, token);
|
|
|
650 |
|
|
|
651 |
// Ensure any cookies set by the dialog are saved
|
|
|
652 |
// This is to work around a bug where CookieManager may fail to instantiate if CookieSyncManager
|
|
|
653 |
// has never been created.
|
|
|
654 |
CookieSyncManager syncManager = CookieSyncManager.createInstance(context);
|
|
|
655 |
syncManager.sync();
|
|
|
656 |
saveCookieToken(token.getToken());
|
|
|
657 |
} else {
|
|
|
658 |
if (error instanceof FacebookOperationCanceledException) {
|
|
|
659 |
outcome = Result.createCancelResult(pendingRequest, "User canceled log in.");
|
|
|
660 |
} else {
|
|
|
661 |
// Something went wrong, don't log a completion event since it will skew timing results.
|
|
|
662 |
e2e = null;
|
|
|
663 |
|
|
|
664 |
String errorCode = null;
|
|
|
665 |
String errorMessage = error.getMessage();
|
|
|
666 |
if (error instanceof FacebookServiceException) {
|
|
|
667 |
FacebookRequestError requestError = ((FacebookServiceException)error).getRequestError();
|
|
|
668 |
errorCode = String.format("%d", requestError.getErrorCode());
|
|
|
669 |
errorMessage = requestError.toString();
|
|
|
670 |
}
|
|
|
671 |
outcome = Result.createErrorResult(pendingRequest, null, errorMessage, errorCode);
|
|
|
672 |
}
|
|
|
673 |
}
|
|
|
674 |
|
|
|
675 |
if (!Utility.isNullOrEmpty(e2e)) {
|
|
|
676 |
logWebLoginCompleted(applicationId, e2e);
|
|
|
677 |
}
|
|
|
678 |
|
|
|
679 |
completeAndValidate(outcome);
|
|
|
680 |
}
|
|
|
681 |
|
|
|
682 |
private void saveCookieToken(String token) {
|
|
|
683 |
Context context = getStartActivityDelegate().getActivityContext();
|
|
|
684 |
context.getSharedPreferences(
|
|
|
685 |
WEB_VIEW_AUTH_HANDLER_STORE,
|
|
|
686 |
Context.MODE_PRIVATE)
|
|
|
687 |
.edit()
|
|
|
688 |
.putString(WEB_VIEW_AUTH_HANDLER_TOKEN_KEY, token)
|
|
|
689 |
.apply();
|
|
|
690 |
}
|
|
|
691 |
|
|
|
692 |
private String loadCookieToken() {
|
|
|
693 |
Context context = getStartActivityDelegate().getActivityContext();
|
|
|
694 |
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
|
|
695 |
WEB_VIEW_AUTH_HANDLER_STORE,
|
|
|
696 |
Context.MODE_PRIVATE);
|
|
|
697 |
return sharedPreferences.getString(WEB_VIEW_AUTH_HANDLER_TOKEN_KEY, "");
|
|
|
698 |
}
|
|
|
699 |
}
|
|
|
700 |
|
|
|
701 |
class GetTokenAuthHandler extends AuthHandler {
|
|
|
702 |
private static final long serialVersionUID = 1L;
|
|
|
703 |
private transient GetTokenClient getTokenClient;
|
|
|
704 |
|
|
|
705 |
@Override
|
|
|
706 |
String getNameForLogging() {
|
|
|
707 |
return "get_token";
|
|
|
708 |
}
|
|
|
709 |
|
|
|
710 |
@Override
|
|
|
711 |
void cancel() {
|
|
|
712 |
if (getTokenClient != null) {
|
|
|
713 |
getTokenClient.cancel();
|
|
|
714 |
getTokenClient = null;
|
|
|
715 |
}
|
|
|
716 |
}
|
|
|
717 |
|
|
|
718 |
@Override
|
|
|
719 |
boolean needsRestart() {
|
|
|
720 |
// if the getTokenClient is null, that means an orientation change has occurred, and we need
|
|
|
721 |
// to recreate the GetTokenClient, so return true to indicate we need a restart
|
|
|
722 |
return getTokenClient == null;
|
|
|
723 |
}
|
|
|
724 |
|
|
|
725 |
boolean tryAuthorize(final AuthorizationRequest request) {
|
|
|
726 |
getTokenClient = new GetTokenClient(context, request.getApplicationId());
|
|
|
727 |
if (!getTokenClient.start()) {
|
|
|
728 |
return false;
|
|
|
729 |
}
|
|
|
730 |
|
|
|
731 |
notifyBackgroundProcessingStart();
|
|
|
732 |
|
|
|
733 |
GetTokenClient.CompletedListener callback = new GetTokenClient.CompletedListener() {
|
|
|
734 |
@Override
|
|
|
735 |
public void completed(Bundle result) {
|
|
|
736 |
getTokenCompleted(request, result);
|
|
|
737 |
}
|
|
|
738 |
};
|
|
|
739 |
|
|
|
740 |
getTokenClient.setCompletedListener(callback);
|
|
|
741 |
return true;
|
|
|
742 |
}
|
|
|
743 |
|
|
|
744 |
void getTokenCompleted(AuthorizationRequest request, Bundle result) {
|
|
|
745 |
getTokenClient = null;
|
|
|
746 |
|
|
|
747 |
notifyBackgroundProcessingStop();
|
|
|
748 |
|
|
|
749 |
if (result != null) {
|
|
|
750 |
ArrayList<String> currentPermissions = result.getStringArrayList(NativeProtocol.EXTRA_PERMISSIONS);
|
|
|
751 |
List<String> permissions = request.getPermissions();
|
|
|
752 |
if ((currentPermissions != null) &&
|
|
|
753 |
((permissions == null) || currentPermissions.containsAll(permissions))) {
|
|
|
754 |
// We got all the permissions we needed, so we can complete the auth now.
|
|
|
755 |
AccessToken token = AccessToken
|
|
|
756 |
.createFromNativeLogin(result, AccessTokenSource.FACEBOOK_APPLICATION_SERVICE);
|
|
|
757 |
Result outcome = Result.createTokenResult(pendingRequest, token);
|
|
|
758 |
completeAndValidate(outcome);
|
|
|
759 |
return;
|
|
|
760 |
}
|
|
|
761 |
|
|
|
762 |
// We didn't get all the permissions we wanted, so update the request with just the permissions
|
|
|
763 |
// we still need.
|
|
|
764 |
List<String> newPermissions = new ArrayList<String>();
|
|
|
765 |
for (String permission : permissions) {
|
|
|
766 |
if (!currentPermissions.contains(permission)) {
|
|
|
767 |
newPermissions.add(permission);
|
|
|
768 |
}
|
|
|
769 |
}
|
|
|
770 |
if (!newPermissions.isEmpty()) {
|
|
|
771 |
addLoggingExtra(EVENT_EXTRAS_NEW_PERMISSIONS, TextUtils.join(",", newPermissions));
|
|
|
772 |
}
|
|
|
773 |
|
|
|
774 |
request.setPermissions(newPermissions);
|
|
|
775 |
}
|
|
|
776 |
|
|
|
777 |
tryNextHandler();
|
|
|
778 |
}
|
|
|
779 |
}
|
|
|
780 |
|
|
|
781 |
abstract class KatanaAuthHandler extends AuthHandler {
|
|
|
782 |
private static final long serialVersionUID = 1L;
|
|
|
783 |
|
|
|
784 |
protected boolean tryIntent(Intent intent, int requestCode) {
|
|
|
785 |
if (intent == null) {
|
|
|
786 |
return false;
|
|
|
787 |
}
|
|
|
788 |
|
|
|
789 |
try {
|
|
|
790 |
getStartActivityDelegate().startActivityForResult(intent, requestCode);
|
|
|
791 |
} catch (ActivityNotFoundException e) {
|
|
|
792 |
// We don't expect this to happen, since we've already validated the intent and bailed out before
|
|
|
793 |
// now if it couldn't be resolved.
|
|
|
794 |
return false;
|
|
|
795 |
}
|
|
|
796 |
|
|
|
797 |
return true;
|
|
|
798 |
}
|
|
|
799 |
}
|
|
|
800 |
|
|
|
801 |
class KatanaProxyAuthHandler extends KatanaAuthHandler {
|
|
|
802 |
private static final long serialVersionUID = 1L;
|
|
|
803 |
private String applicationId;
|
|
|
804 |
|
|
|
805 |
@Override
|
|
|
806 |
String getNameForLogging() {
|
|
|
807 |
return "katana_proxy_auth";
|
|
|
808 |
}
|
|
|
809 |
|
|
|
810 |
@Override
|
|
|
811 |
boolean tryAuthorize(AuthorizationRequest request) {
|
|
|
812 |
applicationId = request.getApplicationId();
|
|
|
813 |
|
|
|
814 |
String e2e = getE2E();
|
|
|
815 |
Intent intent = NativeProtocol.createProxyAuthIntent(context, request.getApplicationId(),
|
|
|
816 |
request.getPermissions(), e2e, request.isRerequest(), request.getDefaultAudience());
|
|
|
817 |
|
|
|
818 |
addLoggingExtra(ServerProtocol.DIALOG_PARAM_E2E, e2e);
|
|
|
819 |
|
|
|
820 |
return tryIntent(intent, request.getRequestCode());
|
|
|
821 |
}
|
|
|
822 |
|
|
|
823 |
@Override
|
|
|
824 |
boolean onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
|
825 |
// Handle stuff
|
|
|
826 |
Result outcome;
|
|
|
827 |
|
|
|
828 |
if (data == null) {
|
|
|
829 |
// This happens if the user presses 'Back'.
|
|
|
830 |
outcome = Result.createCancelResult(pendingRequest, "Operation canceled");
|
|
|
831 |
} else if (resultCode == Activity.RESULT_CANCELED) {
|
|
|
832 |
outcome = Result.createCancelResult(pendingRequest, data.getStringExtra("error"));
|
|
|
833 |
} else if (resultCode != Activity.RESULT_OK) {
|
|
|
834 |
outcome = Result.createErrorResult(pendingRequest, "Unexpected resultCode from authorization.", null);
|
|
|
835 |
} else {
|
|
|
836 |
outcome = handleResultOk(data);
|
|
|
837 |
}
|
|
|
838 |
|
|
|
839 |
if (outcome != null) {
|
|
|
840 |
completeAndValidate(outcome);
|
|
|
841 |
} else {
|
|
|
842 |
tryNextHandler();
|
|
|
843 |
}
|
|
|
844 |
return true;
|
|
|
845 |
}
|
|
|
846 |
|
|
|
847 |
private Result handleResultOk(Intent data) {
|
|
|
848 |
Bundle extras = data.getExtras();
|
|
|
849 |
String error = extras.getString("error");
|
|
|
850 |
if (error == null) {
|
|
|
851 |
error = extras.getString("error_type");
|
|
|
852 |
}
|
|
|
853 |
String errorCode = extras.getString("error_code");
|
|
|
854 |
String errorMessage = extras.getString("error_message");
|
|
|
855 |
if (errorMessage == null) {
|
|
|
856 |
errorMessage = extras.getString("error_description");
|
|
|
857 |
}
|
|
|
858 |
|
|
|
859 |
String e2e = extras.getString(NativeProtocol.FACEBOOK_PROXY_AUTH_E2E_KEY);
|
|
|
860 |
if (!Utility.isNullOrEmpty(e2e)) {
|
|
|
861 |
logWebLoginCompleted(applicationId, e2e);
|
|
|
862 |
}
|
|
|
863 |
|
|
|
864 |
if (error == null && errorCode == null && errorMessage == null) {
|
|
|
865 |
AccessToken token = AccessToken.createFromWebBundle(pendingRequest.getPermissions(), extras,
|
|
|
866 |
AccessTokenSource.FACEBOOK_APPLICATION_WEB);
|
|
|
867 |
return Result.createTokenResult(pendingRequest, token);
|
|
|
868 |
} else if (ServerProtocol.errorsProxyAuthDisabled.contains(error)) {
|
|
|
869 |
return null;
|
|
|
870 |
} else if (ServerProtocol.errorsUserCanceled.contains(error)) {
|
|
|
871 |
return Result.createCancelResult(pendingRequest, null);
|
|
|
872 |
} else {
|
|
|
873 |
return Result.createErrorResult(pendingRequest, error, errorMessage, errorCode);
|
|
|
874 |
}
|
|
|
875 |
}
|
|
|
876 |
}
|
|
|
877 |
|
|
|
878 |
private static String getE2E() {
|
|
|
879 |
JSONObject e2e = new JSONObject();
|
|
|
880 |
try {
|
|
|
881 |
e2e.put("init", System.currentTimeMillis());
|
|
|
882 |
} catch (JSONException e) {
|
|
|
883 |
}
|
|
|
884 |
return e2e.toString();
|
|
|
885 |
}
|
|
|
886 |
|
|
|
887 |
private void logWebLoginCompleted(String applicationId, String e2e) {
|
|
|
888 |
AppEventsLogger appEventsLogger = AppEventsLogger.newLogger(context, applicationId);
|
|
|
889 |
|
|
|
890 |
Bundle parameters = new Bundle();
|
|
|
891 |
parameters.putString(AnalyticsEvents.PARAMETER_WEB_LOGIN_E2E, e2e);
|
|
|
892 |
parameters.putLong(AnalyticsEvents.PARAMETER_WEB_LOGIN_SWITCHBACK_TIME, System.currentTimeMillis());
|
|
|
893 |
parameters.putString(AnalyticsEvents.PARAMETER_APP_ID, applicationId);
|
|
|
894 |
|
|
|
895 |
appEventsLogger.logSdkEvent(AnalyticsEvents.EVENT_WEB_LOGIN_COMPLETE, null, parameters);
|
|
|
896 |
}
|
|
|
897 |
|
|
|
898 |
static class AuthDialogBuilder extends WebDialog.Builder {
|
|
|
899 |
private static final String OAUTH_DIALOG = "oauth";
|
|
|
900 |
static final String REDIRECT_URI = "fbconnect://success";
|
|
|
901 |
private String e2e;
|
|
|
902 |
private boolean isRerequest;
|
|
|
903 |
|
|
|
904 |
public AuthDialogBuilder(Context context, String applicationId, Bundle parameters) {
|
|
|
905 |
super(context, applicationId, OAUTH_DIALOG, parameters);
|
|
|
906 |
}
|
|
|
907 |
|
|
|
908 |
public AuthDialogBuilder setE2E(String e2e) {
|
|
|
909 |
this.e2e = e2e;
|
|
|
910 |
return this;
|
|
|
911 |
}
|
|
|
912 |
|
|
|
913 |
public AuthDialogBuilder setIsRerequest(boolean isRerequest) {
|
|
|
914 |
this.isRerequest = isRerequest;
|
|
|
915 |
return this;
|
|
|
916 |
}
|
|
|
917 |
|
|
|
918 |
@Override
|
|
|
919 |
public WebDialog build() {
|
|
|
920 |
Bundle parameters = getParameters();
|
|
|
921 |
parameters.putString(ServerProtocol.DIALOG_PARAM_REDIRECT_URI, REDIRECT_URI);
|
|
|
922 |
parameters.putString(ServerProtocol.DIALOG_PARAM_CLIENT_ID, getApplicationId());
|
|
|
923 |
parameters.putString(ServerProtocol.DIALOG_PARAM_E2E, e2e);
|
|
|
924 |
parameters.putString(ServerProtocol.DIALOG_PARAM_RESPONSE_TYPE, ServerProtocol.DIALOG_RESPONSE_TYPE_TOKEN);
|
|
|
925 |
parameters.putString(ServerProtocol.DIALOG_PARAM_RETURN_SCOPES, ServerProtocol.DIALOG_RETURN_SCOPES_TRUE);
|
|
|
926 |
|
|
|
927 |
// Only set the rerequest auth type for non legacy requests
|
|
|
928 |
if (isRerequest && !Settings.getPlatformCompatibilityEnabled()) {
|
|
|
929 |
parameters.putString(ServerProtocol.DIALOG_PARAM_AUTH_TYPE, ServerProtocol.DIALOG_REREQUEST_AUTH_TYPE);
|
|
|
930 |
}
|
|
|
931 |
|
|
|
932 |
return new WebDialog(getContext(), OAUTH_DIALOG, parameters, getTheme(), getListener());
|
|
|
933 |
}
|
|
|
934 |
}
|
|
|
935 |
|
|
|
936 |
static class AuthorizationRequest implements Serializable {
|
|
|
937 |
private static final long serialVersionUID = 1L;
|
|
|
938 |
|
|
|
939 |
private transient final StartActivityDelegate startActivityDelegate;
|
|
|
940 |
private final SessionLoginBehavior loginBehavior;
|
|
|
941 |
private final int requestCode;
|
|
|
942 |
private boolean isLegacy = false;
|
|
|
943 |
private List<String> permissions;
|
|
|
944 |
private final SessionDefaultAudience defaultAudience;
|
|
|
945 |
private final String applicationId;
|
|
|
946 |
private final String previousAccessToken;
|
|
|
947 |
private final String authId;
|
|
|
948 |
private boolean isRerequest = false;
|
|
|
949 |
|
|
|
950 |
AuthorizationRequest(SessionLoginBehavior loginBehavior, int requestCode, boolean isLegacy,
|
|
|
951 |
List<String> permissions, SessionDefaultAudience defaultAudience, String applicationId,
|
|
|
952 |
String validateSameFbidAsToken, StartActivityDelegate startActivityDelegate, String authId) {
|
|
|
953 |
this.loginBehavior = loginBehavior;
|
|
|
954 |
this.requestCode = requestCode;
|
|
|
955 |
this.isLegacy = isLegacy;
|
|
|
956 |
this.permissions = permissions;
|
|
|
957 |
this.defaultAudience = defaultAudience;
|
|
|
958 |
this.applicationId = applicationId;
|
|
|
959 |
this.previousAccessToken = validateSameFbidAsToken;
|
|
|
960 |
this.startActivityDelegate = startActivityDelegate;
|
|
|
961 |
this.authId = authId;
|
|
|
962 |
}
|
|
|
963 |
|
|
|
964 |
StartActivityDelegate getStartActivityDelegate() {
|
|
|
965 |
return startActivityDelegate;
|
|
|
966 |
}
|
|
|
967 |
|
|
|
968 |
List<String> getPermissions() {
|
|
|
969 |
return permissions;
|
|
|
970 |
}
|
|
|
971 |
|
|
|
972 |
void setPermissions(List<String> permissions) {
|
|
|
973 |
this.permissions = permissions;
|
|
|
974 |
}
|
|
|
975 |
|
|
|
976 |
SessionLoginBehavior getLoginBehavior() {
|
|
|
977 |
return loginBehavior;
|
|
|
978 |
}
|
|
|
979 |
|
|
|
980 |
int getRequestCode() {
|
|
|
981 |
return requestCode;
|
|
|
982 |
}
|
|
|
983 |
|
|
|
984 |
SessionDefaultAudience getDefaultAudience() {
|
|
|
985 |
return defaultAudience;
|
|
|
986 |
}
|
|
|
987 |
|
|
|
988 |
String getApplicationId() {
|
|
|
989 |
return applicationId;
|
|
|
990 |
}
|
|
|
991 |
|
|
|
992 |
boolean isLegacy() {
|
|
|
993 |
return isLegacy;
|
|
|
994 |
}
|
|
|
995 |
|
|
|
996 |
void setIsLegacy(boolean isLegacy) {
|
|
|
997 |
this.isLegacy = isLegacy;
|
|
|
998 |
}
|
|
|
999 |
|
|
|
1000 |
String getPreviousAccessToken() {
|
|
|
1001 |
return previousAccessToken;
|
|
|
1002 |
}
|
|
|
1003 |
|
|
|
1004 |
boolean needsNewTokenValidation() {
|
|
|
1005 |
return previousAccessToken != null && !isLegacy;
|
|
|
1006 |
}
|
|
|
1007 |
|
|
|
1008 |
String getAuthId() {
|
|
|
1009 |
return authId;
|
|
|
1010 |
}
|
|
|
1011 |
|
|
|
1012 |
boolean isRerequest() {
|
|
|
1013 |
return isRerequest;
|
|
|
1014 |
}
|
|
|
1015 |
|
|
|
1016 |
void setRerequest(boolean isRerequest) {
|
|
|
1017 |
this.isRerequest = isRerequest;
|
|
|
1018 |
}
|
|
|
1019 |
}
|
|
|
1020 |
|
|
|
1021 |
|
|
|
1022 |
static class Result implements Serializable {
|
|
|
1023 |
private static final long serialVersionUID = 1L;
|
|
|
1024 |
|
|
|
1025 |
enum Code {
|
|
|
1026 |
SUCCESS("success"),
|
|
|
1027 |
CANCEL("cancel"),
|
|
|
1028 |
ERROR("error");
|
|
|
1029 |
|
|
|
1030 |
private final String loggingValue;
|
|
|
1031 |
|
|
|
1032 |
Code(String loggingValue) {
|
|
|
1033 |
this.loggingValue = loggingValue;
|
|
|
1034 |
}
|
|
|
1035 |
|
|
|
1036 |
// For consistency across platforms, we want to use specific string values when logging these results.
|
|
|
1037 |
String getLoggingValue() {
|
|
|
1038 |
return loggingValue;
|
|
|
1039 |
}
|
|
|
1040 |
}
|
|
|
1041 |
|
|
|
1042 |
final Code code;
|
|
|
1043 |
final AccessToken token;
|
|
|
1044 |
final String errorMessage;
|
|
|
1045 |
final String errorCode;
|
|
|
1046 |
final AuthorizationRequest request;
|
|
|
1047 |
Map<String, String> loggingExtras;
|
|
|
1048 |
|
|
|
1049 |
private Result(AuthorizationRequest request, Code code, AccessToken token, String errorMessage,
|
|
|
1050 |
String errorCode) {
|
|
|
1051 |
this.request = request;
|
|
|
1052 |
this.token = token;
|
|
|
1053 |
this.errorMessage = errorMessage;
|
|
|
1054 |
this.code = code;
|
|
|
1055 |
this.errorCode = errorCode;
|
|
|
1056 |
}
|
|
|
1057 |
|
|
|
1058 |
static Result createTokenResult(AuthorizationRequest request, AccessToken token) {
|
|
|
1059 |
return new Result(request, Code.SUCCESS, token, null, null);
|
|
|
1060 |
}
|
|
|
1061 |
|
|
|
1062 |
static Result createCancelResult(AuthorizationRequest request, String message) {
|
|
|
1063 |
return new Result(request, Code.CANCEL, null, message, null);
|
|
|
1064 |
}
|
|
|
1065 |
|
|
|
1066 |
static Result createErrorResult(AuthorizationRequest request, String errorType, String errorDescription) {
|
|
|
1067 |
return createErrorResult(request, errorType, errorDescription, null);
|
|
|
1068 |
}
|
|
|
1069 |
|
|
|
1070 |
static Result createErrorResult(AuthorizationRequest request, String errorType, String errorDescription,
|
|
|
1071 |
String errorCode) {
|
|
|
1072 |
String message = TextUtils.join(": ", Utility.asListNoNulls(errorType, errorDescription));
|
|
|
1073 |
return new Result(request, Code.ERROR, null, message, errorCode);
|
|
|
1074 |
}
|
|
|
1075 |
}
|
|
|
1076 |
}
|