/** * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, * copy, modify, and distribute this software in source code or binary form for use * in connection with the web services and APIs provided by Facebook. * * As with any software that integrates with the Facebook platform, your use of * this software is subject to the Facebook Developer Principles and Policies * [http://developers.facebook.com/policy/]. This copyright notice shall be * included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.facebook.login; import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.support.v4.app.FragmentActivity; import android.text.TextUtils; import android.webkit.CookieSyncManager; import com.facebook.AccessToken; import com.facebook.AccessTokenSource; import com.facebook.appevents.AppEventsConstants; import com.facebook.internal.FacebookDialogFragment; import com.facebook.FacebookException; import com.facebook.FacebookOperationCanceledException; import com.facebook.FacebookRequestError; import com.facebook.FacebookServiceException; import com.facebook.internal.ServerProtocol; import com.facebook.internal.Utility; import com.facebook.internal.WebDialog; import java.util.Locale; class WebViewLoginMethodHandler extends LoginMethodHandler { private static final String WEB_VIEW_AUTH_HANDLER_STORE = "com.facebook.login.AuthorizationClient.WebViewAuthHandler.TOKEN_STORE_KEY"; private static final String WEB_VIEW_AUTH_HANDLER_TOKEN_KEY = "TOKEN"; private WebDialog loginDialog; private String e2e; WebViewLoginMethodHandler(LoginClient loginClient) { super(loginClient); } @Override String getNameForLogging() { return "web_view"; } @Override boolean needsInternetPermission() { return true; } @Override void cancel() { if (loginDialog != null) { loginDialog.cancel(); loginDialog = null; } } @Override boolean tryAuthorize(final LoginClient.Request request) { 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); } DefaultAudience audience = request.getDefaultAudience(); parameters.putString( ServerProtocol.DIALOG_PARAM_DEFAULT_AUDIENCE, audience.getNativeProtocolAudience()); AccessToken previousToken = AccessToken.getCurrentAccessToken(); String previousTokenString = previousToken != null ? previousToken.getToken() : null; if (previousTokenString != null && (previousTokenString.equals(loadCookieToken()))) { parameters.putString( ServerProtocol.DIALOG_PARAM_ACCESS_TOKEN, previousTokenString); // 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(loginClient.getActivity()); 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 = LoginClient.getE2E(); addLoggingExtra(ServerProtocol.DIALOG_PARAM_E2E, e2e); FragmentActivity fragmentActivity = loginClient.getActivity(); WebDialog.Builder builder = new AuthDialogBuilder( fragmentActivity, request.getApplicationId(), parameters) .setE2E(e2e) .setIsRerequest(request.isRerequest()) .setOnCompleteListener(listener); loginDialog = builder.build(); FacebookDialogFragment dialogFragment = new FacebookDialogFragment(); dialogFragment.setRetainInstance(true); dialogFragment.setDialog(loginDialog); dialogFragment.show(fragmentActivity.getSupportFragmentManager(), FacebookDialogFragment.TAG); return true; } void onWebDialogComplete(LoginClient.Request request, Bundle values, FacebookException error) { LoginClient.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); } try { AccessToken token = createAccessTokenFromWebBundle( request.getPermissions(), values, AccessTokenSource.WEB_VIEW, request.getApplicationId()); outcome = LoginClient.Result.createTokenResult( loginClient.getPendingRequest(), 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(loginClient.getActivity()); syncManager.sync(); saveCookieToken(token.getToken()); } catch (FacebookException ex) { outcome = LoginClient.Result.createErrorResult( loginClient.getPendingRequest(), null, ex.getMessage()); } } else { if (error instanceof FacebookOperationCanceledException) { outcome = LoginClient.Result.createCancelResult(loginClient.getPendingRequest(), "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(Locale.ROOT, "%d", requestError.getErrorCode()); errorMessage = requestError.toString(); } outcome = LoginClient.Result.createErrorResult(loginClient.getPendingRequest(), null, errorMessage, errorCode); } } if (!Utility.isNullOrEmpty(e2e)) { logWebLoginCompleted(e2e); } loginClient.completeAndValidate(outcome); } private void saveCookieToken(String token) { Context context = loginClient.getActivity(); context.getSharedPreferences( WEB_VIEW_AUTH_HANDLER_STORE, Context.MODE_PRIVATE) .edit() .putString(WEB_VIEW_AUTH_HANDLER_TOKEN_KEY, token) .apply(); } private String loadCookieToken() { Context context = loginClient.getActivity(); SharedPreferences sharedPreferences = context.getSharedPreferences( WEB_VIEW_AUTH_HANDLER_STORE, Context.MODE_PRIVATE); return sharedPreferences.getString(WEB_VIEW_AUTH_HANDLER_TOKEN_KEY, ""); } 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_AND_SIGNED_REQUEST); parameters.putString( ServerProtocol.DIALOG_PARAM_RETURN_SCOPES, ServerProtocol.DIALOG_RETURN_SCOPES_TRUE); // Set the re-request auth type for requests if (isRerequest) { parameters.putString( ServerProtocol.DIALOG_PARAM_AUTH_TYPE, ServerProtocol.DIALOG_REREQUEST_AUTH_TYPE); } return new WebDialog(getContext(), OAUTH_DIALOG, parameters, getTheme(), getListener()); } } WebViewLoginMethodHandler(Parcel source) { super(source); e2e = source.readString(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeString(e2e); } public static final Parcelable.Creator<WebViewLoginMethodHandler> CREATOR = new Parcelable.Creator() { @Override public WebViewLoginMethodHandler createFromParcel(Parcel source) { return new WebViewLoginMethodHandler(source); } @Override public WebViewLoginMethodHandler[] newArray(int size) { return new WebViewLoginMethodHandler[size]; } };}