Android Open Source - Robook Authorization Client






From Project

Back to project page Robook.

License

The source code is released under:

MIT License

If you think the Android project Robook listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/**
 * Copyright 2010-present Facebook./* ww w  .ja v a 2 s  . c  o  m*/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook;

import android.Manifest;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;
import android.webkit.CookieSyncManager;
import com.facebook.android.R;
import com.facebook.internal.AnalyticsEvents;
import com.facebook.internal.NativeProtocol;
import com.facebook.internal.ServerProtocol;
import com.facebook.internal.Utility;
import com.facebook.model.GraphUser;
import com.facebook.widget.WebDialog;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class AuthorizationClient implements Serializable {
    private static final long serialVersionUID = 1L;
    private static final String TAG = "Facebook-AuthorizationClient";
    private static final String WEB_VIEW_AUTH_HANDLER_STORE =
            "com.facebook.AuthorizationClient.WebViewAuthHandler.TOKEN_STORE_KEY";
    private static final String WEB_VIEW_AUTH_HANDLER_TOKEN_KEY = "TOKEN";

    // Constants for logging login-related data. Some of these are only used by Session, but grouped here for
    // maintainability.
    private static final String EVENT_NAME_LOGIN_METHOD_START = "fb_mobile_login_method_start";
    private static final String EVENT_NAME_LOGIN_METHOD_COMPLETE = "fb_mobile_login_method_complete";
    private static final String EVENT_PARAM_METHOD_RESULT_SKIPPED = "skipped";
    static final String EVENT_NAME_LOGIN_START = "fb_mobile_login_start";
    static final String EVENT_NAME_LOGIN_COMPLETE = "fb_mobile_login_complete";
    // Note: to ensure stability of column mappings across the four different event types, we prepend a column
    // index to each name, and we log all columns with all events, even if they are empty.
    static final String EVENT_PARAM_AUTH_LOGGER_ID = "0_auth_logger_id";
    static final String EVENT_PARAM_TIMESTAMP = "1_timestamp_ms";
    static final String EVENT_PARAM_LOGIN_RESULT = "2_result";
    static final String EVENT_PARAM_METHOD = "3_method";
    static final String EVENT_PARAM_ERROR_CODE = "4_error_code";
    static final String EVENT_PARAM_ERROR_MESSAGE = "5_error_message";
    static final String EVENT_PARAM_EXTRAS = "6_extras";
    static final String EVENT_EXTRAS_TRY_LOGIN_ACTIVITY = "try_login_activity";
    static final String EVENT_EXTRAS_TRY_LEGACY = "try_legacy";
    static final String EVENT_EXTRAS_LOGIN_BEHAVIOR = "login_behavior";
    static final String EVENT_EXTRAS_REQUEST_CODE = "request_code";
    static final String EVENT_EXTRAS_IS_LEGACY = "is_legacy";
    static final String EVENT_EXTRAS_PERMISSIONS = "permissions";
    static final String EVENT_EXTRAS_DEFAULT_AUDIENCE = "default_audience";
    static final String EVENT_EXTRAS_MISSING_INTERNET_PERMISSION = "no_internet_permission";
    static final String EVENT_EXTRAS_NOT_TRIED = "not_tried";
    static final String EVENT_EXTRAS_NEW_PERMISSIONS = "new_permissions";

    List<AuthHandler> handlersToTry;
    AuthHandler currentHandler;
    transient Context context;
    transient StartActivityDelegate startActivityDelegate;
    transient OnCompletedListener onCompletedListener;
    transient BackgroundProcessingListener backgroundProcessingListener;
    transient boolean checkedInternetPermission;
    AuthorizationRequest pendingRequest;
    Map<String, String> loggingExtras;
    private transient AppEventsLogger appEventsLogger;

    interface OnCompletedListener {
        void onCompleted(Result result);
    }

    interface BackgroundProcessingListener {
        void onBackgroundProcessingStarted();

        void onBackgroundProcessingStopped();
    }

    interface StartActivityDelegate {
        public void startActivityForResult(Intent intent, int requestCode);

        public Activity getActivityContext();
    }

    void setContext(final Context context) {
        this.context = context;
        // We rely on individual requests to tell us how to start an activity.
        startActivityDelegate = null;
    }

    void setContext(final Activity activity) {
        this.context = activity;

        // If we are used in the context of an activity, we will always use that activity to
        // call startActivityForResult.
        startActivityDelegate = new StartActivityDelegate() {
            @Override
            public void startActivityForResult(Intent intent, int requestCode) {
                activity.startActivityForResult(intent, requestCode);
            }

            @Override
            public Activity getActivityContext() {
                return activity;
            }
        };
    }

    void startOrContinueAuth(AuthorizationRequest request) {
        if (getInProgress()) {
            continueAuth();
        } else {
            authorize(request);
        }
    }

    void authorize(AuthorizationRequest request) {
        if (request == null) {
            return;
        }

        if (pendingRequest != null) {
            throw new FacebookException("Attempted to authorize while a request is pending.");
        }

        if (request.needsNewTokenValidation() && !checkInternetPermission()) {
            // We're going to need INTERNET permission later and don't have it, so fail early.
            return;
        }
        pendingRequest = request;
        handlersToTry = getHandlerTypes(request);
        tryNextHandler();
    }

    void continueAuth() {
        if (pendingRequest == null || currentHandler == null) {
            throw new FacebookException("Attempted to continue authorization without a pending request.");
        }

        if (currentHandler.needsRestart()) {
            currentHandler.cancel();
            tryCurrentHandler();
        }
    }

    boolean getInProgress() {
        return pendingRequest != null && currentHandler != null;
    }

    void cancelCurrentHandler() {
        if (currentHandler != null) {
            currentHandler.cancel();
        }
    }

    boolean onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == pendingRequest.getRequestCode()) {
            return currentHandler.onActivityResult(requestCode, resultCode, data);
        }
        return false;
    }

    private List<AuthHandler> getHandlerTypes(AuthorizationRequest request) {
        ArrayList<AuthHandler> handlers = new ArrayList<AuthHandler>();

        final SessionLoginBehavior behavior = request.getLoginBehavior();
        if (behavior.allowsKatanaAuth()) {
            if (!request.isLegacy()) {
                handlers.add(new GetTokenAuthHandler());
            }
            handlers.add(new KatanaProxyAuthHandler());
        }

        if (behavior.allowsWebViewAuth()) {
            handlers.add(new WebViewAuthHandler());
        }

        return handlers;
    }

    boolean checkInternetPermission() {
        if (checkedInternetPermission) {
            return true;
        }

        int permissionCheck = checkPermission(Manifest.permission.INTERNET);
        if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
            String errorType = context.getString(R.string.com_facebook_internet_permission_error_title);
            String errorDescription = context.getString(R.string.com_facebook_internet_permission_error_message);
            complete(Result.createErrorResult(pendingRequest, errorType, errorDescription));

            return false;
        }

        checkedInternetPermission = true;
        return true;
    }

    void tryNextHandler() {
        if (currentHandler != null) {
            logAuthorizationMethodComplete(currentHandler.getNameForLogging(), EVENT_PARAM_METHOD_RESULT_SKIPPED,
                    null, null, currentHandler.methodLoggingExtras);
        }

        while (handlersToTry != null && !handlersToTry.isEmpty()) {
            currentHandler = handlersToTry.remove(0);

            boolean started = tryCurrentHandler();

            if (started) {
                return;
            }
        }

        if (pendingRequest != null) {
            // We went through all handlers without successfully attempting an auth.
            completeWithFailure();
        }
    }

    private void completeWithFailure() {
        complete(Result.createErrorResult(pendingRequest, "Login attempt failed.", null));
    }

    private void addLoggingExtra(String key, String value, boolean accumulate) {
        if (loggingExtras == null) {
            loggingExtras = new HashMap<String, String>();
        }
        if (loggingExtras.containsKey(key) && accumulate) {
            value = loggingExtras.get(key) + "," + value;
        }
        loggingExtras.put(key, value);
    }

    boolean tryCurrentHandler() {
        if (currentHandler.needsInternetPermission() && !checkInternetPermission()) {
            addLoggingExtra(EVENT_EXTRAS_MISSING_INTERNET_PERMISSION, AppEventsConstants.EVENT_PARAM_VALUE_YES,
                    false);
            return false;
        }

        boolean tried = currentHandler.tryAuthorize(pendingRequest);
        if (tried) {
            logAuthorizationMethodStart(currentHandler.getNameForLogging());
        } else {
            // We didn't try it, so we don't get any other completion notification -- log that we skipped it.
            addLoggingExtra(EVENT_EXTRAS_NOT_TRIED, currentHandler.getNameForLogging(), true);
        }

        return tried;
    }

    void completeAndValidate(Result outcome) {
        // Do we need to validate a successful result (as in the case of a reauth)?
        if (outcome.token != null && pendingRequest.needsNewTokenValidation()) {
            validateSameFbidAndFinish(outcome);
        } else {
            // We're done, just notify the listener.
            complete(outcome);
        }
    }

    void complete(Result outcome) {
        // This might be null if, for some reason, none of the handlers were successfully tried (in which case
        // we already logged that).
        if (currentHandler != null) {
            logAuthorizationMethodComplete(currentHandler.getNameForLogging(), outcome,
                    currentHandler.methodLoggingExtras);
        }

        if (loggingExtras != null) {
            // Pass this back to the caller for logging at the aggregate level.
            outcome.loggingExtras = loggingExtras;
        }

        handlersToTry = null;
        currentHandler = null;
        pendingRequest = null;
        loggingExtras = null;

        notifyOnCompleteListener(outcome);
    }

    OnCompletedListener getOnCompletedListener() {
        return onCompletedListener;
    }

    void setOnCompletedListener(OnCompletedListener onCompletedListener) {
        this.onCompletedListener = onCompletedListener;
    }

    BackgroundProcessingListener getBackgroundProcessingListener() {
        return backgroundProcessingListener;
    }

    void setBackgroundProcessingListener(BackgroundProcessingListener backgroundProcessingListener) {
        this.backgroundProcessingListener = backgroundProcessingListener;
    }

    StartActivityDelegate getStartActivityDelegate() {
        if (startActivityDelegate != null) {
            return startActivityDelegate;
        } else if (pendingRequest != null) {
            // Wrap the request's delegate in our own.
            return new StartActivityDelegate() {
                @Override
                public void startActivityForResult(Intent intent, int requestCode) {
                    pendingRequest.getStartActivityDelegate().startActivityForResult(intent, requestCode);
                }

                @Override
                public Activity getActivityContext() {
                    return pendingRequest.getStartActivityDelegate().getActivityContext();
                }
            };
        }
        return null;
    }

    int checkPermission(String permission) {
        return context.checkCallingOrSelfPermission(permission);
    }

    void validateSameFbidAndFinish(Result pendingResult) {
        if (pendingResult.token == null) {
            throw new FacebookException("Can't validate without a token");
        }

        RequestBatch batch = createReauthValidationBatch(pendingResult);

        notifyBackgroundProcessingStart();

        batch.executeAsync();
    }

    RequestBatch createReauthValidationBatch(final Result pendingResult) {
        // We need to ensure that the token we got represents the same fbid as the old one. We issue
        // a "me" request using the current token, a "me" request using the new token, and a "me/permissions"
        // request using the current token to get the permissions of the user.

        final ArrayList<String> fbids = new ArrayList<String>();
        final ArrayList<String> grantedPermissions = new ArrayList<String>();
        final ArrayList<String> declinedPermissions = new ArrayList<String>();
        final String newToken = pendingResult.token.getToken();

        Request.Callback meCallback = new Request.Callback() {
            @Override
            public void onCompleted(Response response) {
                try {
                    GraphUser user = response.getGraphObjectAs(GraphUser.class);
                    if (user != null) {
                        fbids.add(user.getId());
                    }
                } catch (Exception ex) {
                }
            }
        };

        String validateSameFbidAsToken = pendingRequest.getPreviousAccessToken();
        Request requestCurrentTokenMe = createGetProfileIdRequest(validateSameFbidAsToken);
        requestCurrentTokenMe.setCallback(meCallback);

        Request requestNewTokenMe = createGetProfileIdRequest(newToken);
        requestNewTokenMe.setCallback(meCallback);

        Request requestCurrentTokenPermissions = createGetPermissionsRequest(validateSameFbidAsToken);
        requestCurrentTokenPermissions.setCallback(new Request.Callback() {
            @Override
            public void onCompleted(Response response) {
                try {
                    Session.PermissionsPair permissionsPair = Session.handlePermissionResponse(response);
                    if (permissionsPair != null) {
                        grantedPermissions.addAll(permissionsPair.getGrantedPermissions());
                        declinedPermissions.addAll(permissionsPair.getDeclinedPermissions());
                    }
                } catch (Exception ex) {
                }
            }
        });

        RequestBatch batch = new RequestBatch(requestCurrentTokenMe, requestNewTokenMe,
                requestCurrentTokenPermissions);
        batch.setBatchApplicationId(pendingRequest.getApplicationId());
        batch.addCallback(new RequestBatch.Callback() {
            @Override
            public void onBatchCompleted(RequestBatch batch) {
                try {
                    Result result = null;
                    if (fbids.size() == 2 && fbids.get(0) != null && fbids.get(1) != null &&
                            fbids.get(0).equals(fbids.get(1))) {
                        // Modify the token to have the right permission set.
                        AccessToken tokenWithPermissions = AccessToken
                                .createFromTokenWithRefreshedPermissions(pendingResult.token,
                                        grantedPermissions, declinedPermissions);
                        result = Result.createTokenResult(pendingRequest, tokenWithPermissions);
                    } else {
                        result = Result
                                .createErrorResult(pendingRequest, "User logged in as different Facebook user.", null);
                    }
                    complete(result);
                } catch (Exception ex) {
                    complete(Result.createErrorResult(pendingRequest, "Caught exception", ex.getMessage()));
                } finally {
                    notifyBackgroundProcessingStop();
                }
            }
        });

        return batch;
    }

    Request createGetPermissionsRequest(String accessToken) {
        Bundle parameters = new Bundle();
        parameters.putString("access_token", accessToken);
        return new Request(null, "me/permissions", parameters, HttpMethod.GET, null);
    }

    Request createGetProfileIdRequest(String accessToken) {
        Bundle parameters = new Bundle();
        parameters.putString("fields", "id");
        parameters.putString("access_token", accessToken);
        return new Request(null, "me", parameters, HttpMethod.GET, null);
    }

    private AppEventsLogger getAppEventsLogger() {
        if (appEventsLogger == null || !appEventsLogger.getApplicationId().equals(pendingRequest.getApplicationId())) {
            appEventsLogger = AppEventsLogger.newLogger(context, pendingRequest.getApplicationId());
        }
        return appEventsLogger;
    }

    private void notifyOnCompleteListener(Result outcome) {
        if (onCompletedListener != null) {
            onCompletedListener.onCompleted(outcome);
        }
    }

    private void notifyBackgroundProcessingStart() {
        if (backgroundProcessingListener != null) {
            backgroundProcessingListener.onBackgroundProcessingStarted();
        }
    }

    private void notifyBackgroundProcessingStop() {
        if (backgroundProcessingListener != null) {
            backgroundProcessingListener.onBackgroundProcessingStopped();
        }
    }

    private void logAuthorizationMethodStart(String method) {
        Bundle bundle = newAuthorizationLoggingBundle(pendingRequest.getAuthId());
        bundle.putLong(EVENT_PARAM_TIMESTAMP, System.currentTimeMillis());
        bundle.putString(EVENT_PARAM_METHOD, method);

        getAppEventsLogger().logSdkEvent(EVENT_NAME_LOGIN_METHOD_START, null, bundle);
    }

    private void logAuthorizationMethodComplete(String method, Result result, Map<String, String> loggingExtras) {
        logAuthorizationMethodComplete(method, result.code.getLoggingValue(), result.errorMessage, result.errorCode,
                loggingExtras);
    }

    private void logAuthorizationMethodComplete(String method, String result, String errorMessage, String errorCode,
            Map<String, String> loggingExtras) {
        Bundle bundle = null;
        if (pendingRequest == null) {
            // We don't expect this to happen, but if it does, log an event for diagnostic purposes.
            bundle = newAuthorizationLoggingBundle("");
            bundle.putString(EVENT_PARAM_LOGIN_RESULT, Result.Code.ERROR.getLoggingValue());
            bundle.putString(EVENT_PARAM_ERROR_MESSAGE,
                    "Unexpected call to logAuthorizationMethodComplete with null pendingRequest.");
        } else {
            bundle = newAuthorizationLoggingBundle(pendingRequest.getAuthId());
            if (result != null) {
                bundle.putString(EVENT_PARAM_LOGIN_RESULT, result);
            }
            if (errorMessage != null) {
                bundle.putString(EVENT_PARAM_ERROR_MESSAGE, errorMessage);
            }
            if (errorCode != null) {
                bundle.putString(EVENT_PARAM_ERROR_CODE, errorCode);
            }
            if (loggingExtras != null && !loggingExtras.isEmpty()) {
                JSONObject jsonObject = new JSONObject(loggingExtras);
                bundle.putString(EVENT_PARAM_EXTRAS, jsonObject.toString());
            }
        }
        bundle.putString(EVENT_PARAM_METHOD, method);
        bundle.putLong(EVENT_PARAM_TIMESTAMP, System.currentTimeMillis());

        getAppEventsLogger().logSdkEvent(EVENT_NAME_LOGIN_METHOD_COMPLETE, null, bundle);
    }

    static Bundle newAuthorizationLoggingBundle(String authLoggerId) {
        // We want to log all parameters for all events, to ensure stability of columns across different event types.
        Bundle bundle = new Bundle();
        bundle.putLong(EVENT_PARAM_TIMESTAMP, System.currentTimeMillis());
        bundle.putString(EVENT_PARAM_AUTH_LOGGER_ID, authLoggerId);
        bundle.putString(EVENT_PARAM_METHOD, "");
        bundle.putString(EVENT_PARAM_LOGIN_RESULT, "");
        bundle.putString(EVENT_PARAM_ERROR_MESSAGE, "");
        bundle.putString(EVENT_PARAM_ERROR_CODE, "");
        bundle.putString(EVENT_PARAM_EXTRAS, "");
        return bundle;
    }

    abstract class AuthHandler implements Serializable {
        private static final long serialVersionUID = 1L;

        Map<String, String> methodLoggingExtras;

        abstract boolean tryAuthorize(AuthorizationRequest request);
        abstract String getNameForLogging();

        boolean onActivityResult(int requestCode, int resultCode, Intent data) {
            return false;
        }

        boolean needsRestart() {
            return false;
        }

        boolean needsInternetPermission() {
            return false;
        }

        void cancel() {
        }

        protected void addLoggingExtra(String key, Object value) {
            if (methodLoggingExtras == null) {
                methodLoggingExtras = new HashMap<String, String>();
            }
            methodLoggingExtras.put(key, value == null ? null : value.toString());
        }
    }

    class WebViewAuthHandler extends AuthHandler {
        private static final long serialVersionUID = 1L;
        private transient WebDialog loginDialog;
        private String applicationId;
        private String e2e;

        @Override
        String getNameForLogging() {
            return "web_view";
        }

        @Override
        boolean needsRestart() {
            // Because we are presenting WebView UI within the current context, we need to explicitly
            // restart the process if the context goes away and is recreated.
            return true;
        }

        @Override
        boolean needsInternetPermission() {
            return true;
        }

        @Override
        void cancel() {
            if (loginDialog != null) {
                loginDialog.dismiss();
                loginDialog = null;
            }
        }

        @Override
        boolean tryAuthorize(final AuthorizationRequest request) {
            applicationId = request.getApplicationId();
            Bundle parameters = new Bundle();
            if (!Utility.isNullOrEmpty(request.getPermissions())) {
                String scope = TextUtils.join(",", request.getPermissions());
                parameters.putString(ServerProtocol.DIALOG_PARAM_SCOPE, scope);
                addLoggingExtra(ServerProtocol.DIALOG_PARAM_SCOPE, scope);
            }

            SessionDefaultAudience audience = request.getDefaultAudience();
            parameters.putString(ServerProtocol.DIALOG_PARAM_DEFAULT_AUDIENCE, audience.getNativeProtocolAudience());

            String previousToken = request.getPreviousAccessToken();
            if (!Utility.isNullOrEmpty(previousToken) && (previousToken.equals(loadCookieToken()))) {
                parameters.putString(ServerProtocol.DIALOG_PARAM_ACCESS_TOKEN, previousToken);
                // Don't log the actual access token, just its presence or absence.
                addLoggingExtra(ServerProtocol.DIALOG_PARAM_ACCESS_TOKEN, AppEventsConstants.EVENT_PARAM_VALUE_YES);
            } else {
                // The call to clear cookies will create the first instance of CookieSyncManager if necessary
                Utility.clearFacebookCookies(context);
                addLoggingExtra(ServerProtocol.DIALOG_PARAM_ACCESS_TOKEN, AppEventsConstants.EVENT_PARAM_VALUE_NO);
            }

            WebDialog.OnCompleteListener listener = new WebDialog.OnCompleteListener() {
                @Override
                public void onComplete(Bundle values, FacebookException error) {
                    onWebDialogComplete(request, values, error);
                }
            };

            e2e = getE2E();
            addLoggingExtra(ServerProtocol.DIALOG_PARAM_E2E, e2e);

            WebDialog.Builder builder =
                    new AuthDialogBuilder(getStartActivityDelegate().getActivityContext(), applicationId, parameters)
                            .setE2E(e2e)
                            .setIsRerequest(request.isRerequest())
                            .setOnCompleteListener(listener);
            loginDialog = builder.build();
            loginDialog.show();

            return true;
        }

        void onWebDialogComplete(AuthorizationRequest request, Bundle values,
                FacebookException error) {
            Result outcome;
            if (values != null) {
                // Actual e2e we got from the dialog should be used for logging.
                if (values.containsKey(ServerProtocol.DIALOG_PARAM_E2E)) {
                    e2e = values.getString(ServerProtocol.DIALOG_PARAM_E2E);
                }

                AccessToken token = AccessToken
                        .createFromWebBundle(request.getPermissions(), values, AccessTokenSource.WEB_VIEW);
                outcome = Result.createTokenResult(pendingRequest, token);

                // Ensure any cookies set by the dialog are saved
                // This is to work around a bug where CookieManager may fail to instantiate if CookieSyncManager
                // has never been created.
                CookieSyncManager syncManager = CookieSyncManager.createInstance(context);
                syncManager.sync();
                saveCookieToken(token.getToken());
            } else {
                if (error instanceof FacebookOperationCanceledException) {
                    outcome = Result.createCancelResult(pendingRequest, "User canceled log in.");
                } else {
                    // Something went wrong, don't log a completion event since it will skew timing results.
                    e2e = null;

                    String errorCode = null;
                    String errorMessage = error.getMessage();
                    if (error instanceof FacebookServiceException) {
                        FacebookRequestError requestError = ((FacebookServiceException)error).getRequestError();
                        errorCode = String.format("%d", requestError.getErrorCode());
                        errorMessage = requestError.toString();
                    }
                    outcome = Result.createErrorResult(pendingRequest, null, errorMessage, errorCode);
                }
            }

            if (!Utility.isNullOrEmpty(e2e)) {
                logWebLoginCompleted(applicationId, e2e);
            }

            completeAndValidate(outcome);
        }

        private void saveCookieToken(String token) {
            Context context = getStartActivityDelegate().getActivityContext();
            SharedPreferences sharedPreferences = context.getSharedPreferences(
                    WEB_VIEW_AUTH_HANDLER_STORE,
                    Context.MODE_PRIVATE);
            SharedPreferences.Editor editor = sharedPreferences.edit();
            editor.putString(WEB_VIEW_AUTH_HANDLER_TOKEN_KEY, token);
            if (!editor.commit()) {
                Utility.logd(TAG, "Could not update saved web view auth handler token.");
            }
        }

        private String loadCookieToken() {
            Context context = getStartActivityDelegate().getActivityContext();
            SharedPreferences sharedPreferences = context.getSharedPreferences(
                    WEB_VIEW_AUTH_HANDLER_STORE,
                    Context.MODE_PRIVATE);
            return sharedPreferences.getString(WEB_VIEW_AUTH_HANDLER_TOKEN_KEY, "");
        }
    }

    class GetTokenAuthHandler extends AuthHandler {
        private static final long serialVersionUID = 1L;
        private transient GetTokenClient getTokenClient;

        @Override
        String getNameForLogging() {
            return "get_token";
        }

        @Override
        void cancel() {
            if (getTokenClient != null) {
                getTokenClient.cancel();
                getTokenClient = null;
            }
        }

        @Override
        boolean needsRestart() {
            // if the getTokenClient is null, that means an orientation change has occurred, and we need
            // to recreate the GetTokenClient, so return true to indicate we need a restart
            return getTokenClient == null;
        }

        boolean tryAuthorize(final AuthorizationRequest request) {
            getTokenClient = new GetTokenClient(context, request.getApplicationId());
            if (!getTokenClient.start()) {
                return false;
            }

            notifyBackgroundProcessingStart();

            GetTokenClient.CompletedListener callback = new GetTokenClient.CompletedListener() {
                @Override
                public void completed(Bundle result) {
                    getTokenCompleted(request, result);
                }
            };

            getTokenClient.setCompletedListener(callback);
            return true;
        }

        void getTokenCompleted(AuthorizationRequest request, Bundle result) {
            getTokenClient = null;

            notifyBackgroundProcessingStop();

            if (result != null) {
                ArrayList<String> currentPermissions = result.getStringArrayList(NativeProtocol.EXTRA_PERMISSIONS);
                List<String> permissions = request.getPermissions();
                if ((currentPermissions != null) &&
                        ((permissions == null) || currentPermissions.containsAll(permissions))) {
                    // We got all the permissions we needed, so we can complete the auth now.
                    AccessToken token = AccessToken
                            .createFromNativeLogin(result, AccessTokenSource.FACEBOOK_APPLICATION_SERVICE);
                    Result outcome = Result.createTokenResult(pendingRequest, token);
                    completeAndValidate(outcome);
                    return;
                }

                // We didn't get all the permissions we wanted, so update the request with just the permissions
                // we still need.
                List<String> newPermissions = new ArrayList<String>();
                for (String permission : permissions) {
                    if (!currentPermissions.contains(permission)) {
                        newPermissions.add(permission);
                    }
                }
                if (!newPermissions.isEmpty()) {
                    addLoggingExtra(EVENT_EXTRAS_NEW_PERMISSIONS, TextUtils.join(",", newPermissions));
                }

                request.setPermissions(newPermissions);
            }

            tryNextHandler();
        }
    }

    abstract class KatanaAuthHandler extends AuthHandler {
        private static final long serialVersionUID = 1L;

        protected boolean tryIntent(Intent intent, int requestCode) {
            if (intent == null) {
                return false;
            }

            try {
                getStartActivityDelegate().startActivityForResult(intent, requestCode);
            } catch (ActivityNotFoundException e) {
                // We don't expect this to happen, since we've already validated the intent and bailed out before
                // now if it couldn't be resolved.
                return false;
            }

            return true;
        }
    }

    class KatanaProxyAuthHandler extends KatanaAuthHandler {
        private static final long serialVersionUID = 1L;
        private String applicationId;

        @Override
        String getNameForLogging() {
            return "katana_proxy_auth";
        }

        @Override
        boolean tryAuthorize(AuthorizationRequest request) {
            applicationId = request.getApplicationId();

            String e2e = getE2E();
            Intent intent = NativeProtocol.createProxyAuthIntent(context, request.getApplicationId(),
                    request.getPermissions(), e2e, request.isRerequest(), request.getDefaultAudience());

            addLoggingExtra(ServerProtocol.DIALOG_PARAM_E2E, e2e);

            return tryIntent(intent, request.getRequestCode());
        }

        @Override
        boolean onActivityResult(int requestCode, int resultCode, Intent data) {
            // Handle stuff
            Result outcome;

            if (data == null) {
                // This happens if the user presses 'Back'.
                outcome = Result.createCancelResult(pendingRequest, "Operation canceled");
            } else if (resultCode == Activity.RESULT_CANCELED) {
                outcome = Result.createCancelResult(pendingRequest, data.getStringExtra("error"));
            } else if (resultCode != Activity.RESULT_OK) {
                outcome = Result.createErrorResult(pendingRequest, "Unexpected resultCode from authorization.", null);
            } else {
                outcome = handleResultOk(data);
            }

            if (outcome != null) {
                completeAndValidate(outcome);
            } else {
                tryNextHandler();
            }
            return true;
        }

        private Result handleResultOk(Intent data) {
            Bundle extras = data.getExtras();
            String error = extras.getString("error");
            if (error == null) {
                error = extras.getString("error_type");
            }
            String errorCode = extras.getString("error_code");
            String errorMessage = extras.getString("error_message");
            if (errorMessage == null) {
                errorMessage = extras.getString("error_description");
            }

            String e2e = extras.getString(NativeProtocol.FACEBOOK_PROXY_AUTH_E2E_KEY);
            if (!Utility.isNullOrEmpty(e2e)) {
                logWebLoginCompleted(applicationId, e2e);
            }

            if (error == null && errorCode == null && errorMessage == null) {
                AccessToken token = AccessToken.createFromWebBundle(pendingRequest.getPermissions(), extras,
                        AccessTokenSource.FACEBOOK_APPLICATION_WEB);
                return Result.createTokenResult(pendingRequest, token);
            } else if (ServerProtocol.errorsProxyAuthDisabled.contains(error)) {
                return null;
            } else if (ServerProtocol.errorsUserCanceled.contains(error)) {
                return Result.createCancelResult(pendingRequest, null);
            } else {
                return Result.createErrorResult(pendingRequest, error, errorMessage, errorCode);
            }
        }
    }

    private static String getE2E() {
        JSONObject e2e = new JSONObject();
        try {
            e2e.put("init", System.currentTimeMillis());
        } catch (JSONException e) {
        }
        return e2e.toString();
    }

    private void logWebLoginCompleted(String applicationId, String e2e) {
        AppEventsLogger appEventsLogger = AppEventsLogger.newLogger(context, applicationId);

        Bundle parameters = new Bundle();
        parameters.putString(AnalyticsEvents.PARAMETER_WEB_LOGIN_E2E, e2e);
        parameters.putLong(AnalyticsEvents.PARAMETER_WEB_LOGIN_SWITCHBACK_TIME, System.currentTimeMillis());
        parameters.putString(AnalyticsEvents.PARAMETER_APP_ID, applicationId);

        appEventsLogger.logSdkEvent(AnalyticsEvents.EVENT_WEB_LOGIN_COMPLETE, null, parameters);
    }

    static class AuthDialogBuilder extends WebDialog.Builder {
        private static final String OAUTH_DIALOG = "oauth";
        static final String REDIRECT_URI = "fbconnect://success";
        private String e2e;
        private boolean isRerequest;

        public AuthDialogBuilder(Context context, String applicationId, Bundle parameters) {
            super(context, applicationId, OAUTH_DIALOG, parameters);
        }

        public AuthDialogBuilder setE2E(String e2e) {
            this.e2e = e2e;
            return this;
        }

        public AuthDialogBuilder setIsRerequest(boolean isRerequest) {
            this.isRerequest = isRerequest;
            return this;
        }

        @Override
        public WebDialog build() {
            Bundle parameters = getParameters();
            parameters.putString(ServerProtocol.DIALOG_PARAM_REDIRECT_URI, REDIRECT_URI);
            parameters.putString(ServerProtocol.DIALOG_PARAM_CLIENT_ID, getApplicationId());
            parameters.putString(ServerProtocol.DIALOG_PARAM_E2E, e2e);
            parameters.putString(ServerProtocol.DIALOG_PARAM_RESPONSE_TYPE, ServerProtocol.DIALOG_RESPONSE_TYPE_TOKEN);
            parameters.putString(ServerProtocol.DIALOG_PARAM_RETURN_SCOPES, ServerProtocol.DIALOG_RETURN_SCOPES_TRUE);

            // Only set the rerequest auth type for non legacy requests
            if (isRerequest && !Settings.getPlatformCompatibilityEnabled()) {
                parameters.putString(ServerProtocol.DIALOG_PARAM_AUTH_TYPE, ServerProtocol.DIALOG_REREQUEST_AUTH_TYPE);
            }

            return new WebDialog(getContext(), OAUTH_DIALOG, parameters, getTheme(), getListener());
        }
    }

    static class AuthorizationRequest implements Serializable {
        private static final long serialVersionUID = 1L;

        private transient final StartActivityDelegate startActivityDelegate;
        private final SessionLoginBehavior loginBehavior;
        private final int requestCode;
        private boolean isLegacy = false;
        private List<String> permissions;
        private final SessionDefaultAudience defaultAudience;
        private final String applicationId;
        private final String previousAccessToken;
        private final String authId;
        private boolean isRerequest = false;

        AuthorizationRequest(SessionLoginBehavior loginBehavior, int requestCode, boolean isLegacy,
                List<String> permissions, SessionDefaultAudience defaultAudience, String applicationId,
                String validateSameFbidAsToken, StartActivityDelegate startActivityDelegate, String authId) {
            this.loginBehavior = loginBehavior;
            this.requestCode = requestCode;
            this.isLegacy = isLegacy;
            this.permissions = permissions;
            this.defaultAudience = defaultAudience;
            this.applicationId = applicationId;
            this.previousAccessToken = validateSameFbidAsToken;
            this.startActivityDelegate = startActivityDelegate;
            this.authId = authId;
        }

        StartActivityDelegate getStartActivityDelegate() {
            return startActivityDelegate;
        }

        List<String> getPermissions() {
            return permissions;
        }

        void setPermissions(List<String> permissions) {
            this.permissions = permissions;
        }

        SessionLoginBehavior getLoginBehavior() {
            return loginBehavior;
        }

        int getRequestCode() {
            return requestCode;
        }

        SessionDefaultAudience getDefaultAudience() {
            return defaultAudience;
        }

        String getApplicationId() {
            return applicationId;
        }

        boolean isLegacy() {
            return isLegacy;
        }

        void setIsLegacy(boolean isLegacy) {
            this.isLegacy = isLegacy;
        }

        String getPreviousAccessToken() {
            return previousAccessToken;
        }

        boolean needsNewTokenValidation() {
            return previousAccessToken != null && !isLegacy;
        }

        String getAuthId() {
            return authId;
        }

        boolean isRerequest() {
            return isRerequest;
        }

        void setRerequest(boolean isRerequest) {
            this.isRerequest = isRerequest;
        }
    }


    static class Result implements Serializable {
        private static final long serialVersionUID = 1L;

        enum Code {
            SUCCESS("success"),
            CANCEL("cancel"),
            ERROR("error");

            private final String loggingValue;

            Code(String loggingValue) {
                this.loggingValue = loggingValue;
            }

            // For consistency across platforms, we want to use specific string values when logging these results.
            String getLoggingValue() {
                return loggingValue;
            }
        }

        final Code code;
        final AccessToken token;
        final String errorMessage;
        final String errorCode;
        final AuthorizationRequest request;
        Map<String, String> loggingExtras;

        private Result(AuthorizationRequest request, Code code, AccessToken token, String errorMessage,
                String errorCode) {
            this.request = request;
            this.token = token;
            this.errorMessage = errorMessage;
            this.code = code;
            this.errorCode = errorCode;
        }

        static Result createTokenResult(AuthorizationRequest request, AccessToken token) {
            return new Result(request, Code.SUCCESS, token, null, null);
        }

        static Result createCancelResult(AuthorizationRequest request, String message) {
            return new Result(request, Code.CANCEL, null, message, null);
        }

        static Result createErrorResult(AuthorizationRequest request, String errorType, String errorDescription) {
            return createErrorResult(request, errorType, errorDescription, null);
        }

        static Result createErrorResult(AuthorizationRequest request, String errorType, String errorDescription,
                String errorCode) {
            String message = TextUtils.join(": ", Utility.asListNoNulls(errorType, errorDescription));
            return new Result(request, Code.ERROR, null, message, errorCode);
        }
    }
}




Java Source Code List

com.facebook.AccessTokenSource.java
com.facebook.AccessToken.java
com.facebook.AppEventsConstants.java
com.facebook.AppEventsLogger.java
com.facebook.AppLinkData.java
com.facebook.AuthorizationClient.java
com.facebook.BoltsMeasurementEventListener.java
com.facebook.FacebookAppLinkResolver.java
com.facebook.FacebookAuthorizationException.java
com.facebook.FacebookBroadcastReceiver.java
com.facebook.FacebookDialogException.java
com.facebook.FacebookException.java
com.facebook.FacebookGraphObjectException.java
com.facebook.FacebookOperationCanceledException.java
com.facebook.FacebookRequestError.java
com.facebook.FacebookSdkVersion.java
com.facebook.FacebookServiceException.java
com.facebook.FacebookTimeSpentData.java
com.facebook.GetTokenClient.java
com.facebook.HttpMethod.java
com.facebook.InsightsLogger.java
com.facebook.LegacyHelper.java
com.facebook.LoggingBehavior.java
com.facebook.LoginActivity.java
com.facebook.NativeAppCallAttachmentStore.java
com.facebook.NativeAppCallContentProvider.java
com.facebook.NonCachingTokenCachingStrategy.java
com.facebook.ProgressNoopOutputStream.java
com.facebook.ProgressOutputStream.java
com.facebook.RequestAsyncTask.java
com.facebook.RequestBatch.java
com.facebook.RequestOutputStream.java
com.facebook.RequestProgress.java
com.facebook.Request.java
com.facebook.Response.java
com.facebook.SessionDefaultAudience.java
com.facebook.SessionLoginBehavior.java
com.facebook.SessionState.java
com.facebook.Session.java
com.facebook.Settings.java
com.facebook.SharedPreferencesTokenCachingStrategy.java
com.facebook.TestSession.java
com.facebook.TokenCachingStrategy.java
com.facebook.UiLifecycleHelper.java
com.facebook.android.AsyncFacebookRunner.java
com.facebook.android.DialogError.java
com.facebook.android.FacebookError.java
com.facebook.android.Facebook.java
com.facebook.android.FbDialog.java
com.facebook.android.Util.java
com.facebook.internal.AnalyticsEvents.java
com.facebook.internal.AttributionIdentifiers.java
com.facebook.internal.CacheableRequestBatch.java
com.facebook.internal.FileLruCache.java
com.facebook.internal.ImageDownloader.java
com.facebook.internal.ImageRequest.java
com.facebook.internal.ImageResponseCache.java
com.facebook.internal.ImageResponse.java
com.facebook.internal.Logger.java
com.facebook.internal.NativeProtocol.java
com.facebook.internal.PlatformServiceClient.java
com.facebook.internal.ServerProtocol.java
com.facebook.internal.SessionAuthorizationType.java
com.facebook.internal.SessionTracker.java
com.facebook.internal.UrlRedirectCache.java
com.facebook.internal.Utility.java
com.facebook.internal.Validate.java
com.facebook.internal.WorkQueue.java
com.facebook.internal.package-info.java
com.facebook.model.CreateGraphObject.java
com.facebook.model.GraphLocation.java
com.facebook.model.GraphMultiResult.java
com.facebook.model.GraphObjectList.java
com.facebook.model.GraphObject.java
com.facebook.model.GraphPlace.java
com.facebook.model.GraphUser.java
com.facebook.model.JsonUtil.java
com.facebook.model.OpenGraphAction.java
com.facebook.model.OpenGraphObject.java
com.facebook.model.PropertyName.java
com.facebook.widget.FacebookDialog.java
com.facebook.widget.FacebookFragment.java
com.facebook.widget.FriendPickerFragment.java
com.facebook.widget.GraphObjectAdapter.java
com.facebook.widget.GraphObjectCursor.java
com.facebook.widget.GraphObjectPagingLoader.java
com.facebook.widget.LoginButton.java
com.facebook.widget.PickerFragment.java
com.facebook.widget.PlacePickerFragment.java
com.facebook.widget.ProfilePictureView.java
com.facebook.widget.SimpleGraphObjectCursor.java
com.facebook.widget.ToolTipPopup.java
com.facebook.widget.UserSettingsFragment.java
com.facebook.widget.WebDialog.java
unipg.dmi.robook.AbstractAdkActivity.java
unipg.dmi.robook.MainActivity.java
unipg.dmi.robook.Preview.java
unipg.dmi.robook.UpdaterService.java
unipg.dmi.robook.prefs.java