Google+登录Python服务器 - “invalid_client - 找不到OAuth客户端”

时间:2015-06-24 11:32:05

标签: python google-api google-plus google-oauth2

我一直试图在我的Android应用中实施Google登录几周。我能够为Android应用程序执行登录操作,完美地获取访问令牌 但是,我有一个基于python的应用服务器,需要离线访问用户的谷歌帐户。我理解,为此我需要获取OAuth代码并将其发送到服务器。然后,服务器将使用OAuth代码获取刷新令牌 但是,这似乎不起作用。服务器永远无法获取刷新令牌。我经常收到错误,

  

invalid_client - 找不到OAuth客户端

这是android GooglePlusFragment.java,

package com.mywash.onboard;

import android.app.Activity;
import android.app.Fragment;
import android.content.Intent;
import android.content.IntentSender;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.crashlytics.android.Crashlytics;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableAuthException;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.Scopes;
import com.google.android.gms.common.SignInButton;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Scope;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.plus.Plus;
import com.google.android.gms.plus.model.people.Person;
import com.mywash.R;
import com.mywash.database.CurrentUserDBAdapter;
import com.mywash.server.ServerAsyncTask;
import com.mywash.server.ServerUrls;

import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;

import java.util.ArrayList;
import java.util.Set;

public class GooglePlusFragment extends Fragment implements
                                                        GoogleApiClient.ConnectionCallbacks,
                                                        GoogleApiClient.OnConnectionFailedListener,
                                                        GoogleApiClient.ServerAuthCodeCallbacks
{
    private final String TAG = "GooglePlusFragment";

    /* Request code used to invoke sign in existingUser interactions. */
    public static final int REQUEST_SIGNIN = 100;
    public static final int REQUEST_AUTHORIZATION = 200;
    public static final int REQUEST_AUTH_CODE = 300;

    GooglePlusFragment instance =  this;
    /* Client used to interact with Google APIs. */
    private static GoogleApiClient mGoogleApiClient;

    private UserBean existingUser = null;
    private String phoneNumber = "";
    private boolean isFirstLogin = false;

    /* A flag indicating that a PendingIntent is in progress and prevents
     * us from starting further intents.
     */
    private boolean mIntentInProgress;

    /* Track whether the sign-in button has been clicked so that we know to resolve
     * all issues preventing sign-in without waiting.
     */
    private boolean mSignInClicked;

    /* Store the connection result from onConnectionFailed callbacks so that we can
     * resolve them when the existingUser clicks sign-in.
     */
    private ConnectionResult mConnectionResult;

    private OnFragmentInteractionListener mListener;

    private Activity activity;

    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @return A new instance of fragment GooglePlusFragment.
     */
    public static GooglePlusFragment newInstance()
    {
        GooglePlusFragment fragment = new GooglePlusFragment();
        return fragment;
    }

    public GooglePlusFragment()
    {
        // Required empty public constructor
    }

    @Override
    public void onAttach(Activity activity)
    {
        super.onAttach(activity);
        this.activity = activity;

        try
        {
            mListener = (OnFragmentInteractionListener) activity;
        }
        catch (ClassCastException e)
        {
            Crashlytics.logException(e);
            throw new ClassCastException(activity.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        try
        {
            CurrentUserDBAdapter userDBA = new CurrentUserDBAdapter(getActivity());
            userDBA.open();
            existingUser = userDBA.getUser();
            phoneNumber = existingUser.getProfilePhone();
            userDBA.close();

            Log.d(TAG, "existingUser.isGooglePlusLogin() " + existingUser.isGooglePlusLogin());
            Log.d(TAG, "existingUser.getProfileEmail() "+existingUser.getProfileEmail());
        }
        catch (Exception e)
        {
            e.printStackTrace();
            Crashlytics.logException(e);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        Log.d(TAG, "onCreateView");
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_google_plus, container, false);

        mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(Plus.API)
                .addScope(Plus.SCOPE_PLUS_LOGIN)
                .addScope(Plus.SCOPE_PLUS_PROFILE)
                .build();

        SignInButton signInButton = (SignInButton) view.findViewById(R.id.google_plus_login_button);
        //signInButton.setVisibility(View.GONE);   // Temp code.
        signInButton.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View view)
            {
                Log.d(TAG, "mSignInClicked");
                if (view.getId() == R.id.google_plus_login_button && !mGoogleApiClient.isConnecting())
                {
                    mSignInClicked = true;
                    mGoogleApiClient.connect();
                    Log.d(TAG, "isConnecting "+mGoogleApiClient.isConnecting());
                }
            }
        });

        return view;
    }

    @Override
    public void onStart()
    {
        super.onStart();

        if(existingUser.isActive())
            mGoogleApiClient.connect();
        else
            googlePlusLogout();
    }

    @Override
    public void onStop()
    {
        super.onStop();

        if (mGoogleApiClient.isConnected())
        {
            mGoogleApiClient.disconnect();
        }
    }

    @Override
    public void onDetach()
    {
        super.onDetach();
        mListener = null;
    }
    @Override
    public void onConnected(Bundle connectionHint)
    {
        try
        {
            Log.d(TAG, "#### onConnected");
            if(mSignInClicked) {
                mSignInClicked = false;

                if (Plus.PeopleApi.getCurrentPerson(mGoogleApiClient) != null) {
                    Person person = Plus.PeopleApi.getCurrentPerson(mGoogleApiClient);

                    UserBean freshUserData = new UserBean();
                    freshUserData.setProfileName(person.getDisplayName());
                    freshUserData.setProfilePicUrl(person.getImage().getUrl());
                    freshUserData.setProfileEmail(Plus.AccountApi.getAccountName(mGoogleApiClient));
                    freshUserData.setGooglePlusLogin(true);

                    if (phoneNumber.length() > 0)
                        freshUserData.setProfilePhone(phoneNumber);

                    new LoginActivity.BitmapFromUrl().execute(freshUserData.getProfilePicUrl());

                    CurrentUserDBAdapter userDBA = new CurrentUserDBAdapter(getActivity());
                    userDBA.open();
                    userDBA.insertOrUpdateUser(freshUserData);
                    userDBA.close();

                    new AccessTokenTask().execute();
                }
            }
            else
                googlePlusLogout();
        }
        catch (Exception e)
        {
            e.printStackTrace();
            Crashlytics.logException(e);
        }

    }

    public void onConnectionFailed(ConnectionResult result)
    {
        if (!mIntentInProgress)
        {
            if (mSignInClicked && result.hasResolution())
            {
                // The existingUser has already clicked 'sign-in' so we attempt to resolve all
                // errors until the existingUser is signed in, or they cancel.
                try
                {
                    result.startResolutionForResult(getActivity(), REQUEST_SIGNIN);
                    mIntentInProgress = true;
                }
                catch (IntentSender.SendIntentException e)
                {
                    Crashlytics.logException(e);
                    // The intent was canceled before it was sent.  Return to the default
                    // state and attempt to connect to get an updated ConnectionResult.
                    mIntentInProgress = false;
                    mGoogleApiClient.connect();
                }
            }
        }
    }

    @Override
    public void onConnectionSuspended(int i)
    {

    }

    @Override
    public CheckResult onCheckServerAuthorization(String s, Set<Scope> set) {
        return null;
    }

    @Override
    public boolean onUploadServerAuthCode(String idToken, String serverAuthCode)
    {
        return true;
    }

    private class AccessTokenTask extends AsyncTask<Void, Void, String>
    {
        String freshEmail = "";

        @Override
        protected void onPreExecute()
        {
            super.onPreExecute();

            Log.d(TAG, "existingUser.getProfileEmail() >>"+existingUser.getProfileEmail() +"<<");
            Log.d(TAG, "Plus.AccountApi.getAccountName(mGoogleApiClient) >>"+Plus.AccountApi.getAccountName(mGoogleApiClient)+"<<");
            // if existing email id does not match with returned mail id, consider as first login.
            if(!existingUser.getProfileEmail().equals(Plus.AccountApi.getAccountName(mGoogleApiClient)))
                isFirstLogin = true;
        }

        @Override
        protected String doInBackground(Void... params)
        {
            Log.d(TAG, "Fetching .. GOOGLE ACCESS TOKEN");
            String accessToken = null;
            try
            {
                freshEmail  =   Plus.AccountApi.getAccountName(mGoogleApiClient);
                Log.d(TAG, "freshEmail .. "+freshEmail);
                Log.d(TAG, "isFirstLogin .. "+isFirstLogin);

                /*if(isFirstLogin)
                {*/
                    Bundle appActivities = new Bundle();
                    appActivities.putString(GoogleAuthUtil.KEY_REQUEST_VISIBLE_ACTIVITIES,
                            "http://schema.org/AddAction");
                    String serverID = getString(R.string.GOOGLE_SERVICE_CLIENT_ID);
                    String scopes = "oauth2:server:client_id:" + serverID
                            + ":api_scope: "
                            + "https://www.googleapis.com/auth/plus.login" + " "
                            + "https://www.googleapis.com/auth/plus.profile.emails.read" + " "
                            + "https://www.googleapis.com/auth/userinfo.email" + " "
                            + "https://www.googleapis.com/auth/userinfo.profile";

                    accessToken = GoogleAuthUtil.getToken(
                            getActivity(),                                     // Context context
                            freshEmail,                                        // String accountName
                            scopes,                                            // String scope
                            appActivities);                                    // Bundle bundle

                    mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
                            // other builder methods
                            .addApi(Plus.API)
                            .addScope(Plus.SCOPE_PLUS_LOGIN)
                            .addScope(Plus.SCOPE_PLUS_PROFILE)
                            .requestServerAuthCode(serverID, instance)
                            .build();
                /*}
                else
                {
                    String scope = "oauth2: " + Scopes.PLUS_LOGIN + " " + Scopes.PROFILE + " " + Scopes.PLUS_ME;
                    accessToken = GoogleAuthUtil.getToken(activity, freshEmail, scope);
                }*/
            }
            catch (UserRecoverableAuthException e)
            {
                Crashlytics.logException(e);
                startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
            }
            catch (Exception e)
            {
                Crashlytics.logException(e);
                e.printStackTrace();
            }

            return accessToken;
        }

        @Override
        protected void onPostExecute(String token)
        {
            super.onPostExecute(token);

            Log.d(TAG, "GOOGLE ACCESS TOKEN: " + token);

            try
            {
                if(token != null && !token.equals(""))
                {
                    ArrayList<NameValuePair> nameValuePairs = new ArrayList<>();
                    nameValuePairs.add(new BasicNameValuePair("type", "android"));
                    nameValuePairs.add(new BasicNameValuePair("device_id", "blah_blah"));
                    nameValuePairs.add(new BasicNameValuePair("email", freshEmail));
                    nameValuePairs.add(new BasicNameValuePair("access_token", token));

                    ServerAsyncTask serverTask = new ServerAsyncTask(getActivity());
                    serverTask.setNameValuePairList(nameValuePairs);
                    serverTask.setCancellable(false);
                    serverTask.execute(ServerAsyncTask.POST, ServerUrls.POST_LOGIN, "0");
                }
            }
            catch (Exception e)
            {
                e.printStackTrace();
                Crashlytics.logException(e);
            }
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        if (requestCode == REQUEST_SIGNIN)
        {
            if (resultCode != getActivity().RESULT_OK)
            {
                mSignInClicked = false;
            }

            mIntentInProgress = false;

            if (!mGoogleApiClient.isConnected())
            {
                mGoogleApiClient.reconnect();
            }
        }
        else if(requestCode == REQUEST_AUTHORIZATION)
        {
            new AccessTokenTask().execute();
        }
    }

    public static void googlePlusLogout()
    {
        if (mGoogleApiClient.isConnected())
        {
            Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
            Plus.AccountApi.revokeAccessAndDisconnect(mGoogleApiClient)
                    .setResultCallback(new ResultCallback<Status>() {
                        @Override
                        public void onResult(Status status) {

                        }
                    });

            mGoogleApiClient.disconnect();
        }
    }

    /**
     * This interface must be implemented by activities that contain this
     * fragment to allow an interaction in this fragment to be communicated
     * to the activity and potentially other fragments contained in that
     * activity.
     * <p/>
     * See the Android Training lesson <a href=
     * "http://developer.android.com/training/basics/fragments/communicating.html"
     * >Communicating with Other Fragments</a> for more information.
     */
    public interface OnFragmentInteractionListener
    {
        // TODO: Update argument type and name
        public void onGooglePlusFragmentInteraction(Uri uri);
    }

}

这是发生错误的应用服务器python代码,

def get_android_data(self, access_token, email=None):
    print access_token, email, "..........."
    gplus_auth = False
    if email:
        try:
            user = db.users.find_one({'email': email})
            if user and 'android_credentials' in user:
                print "creds found"
                credentials = Credentials.new_from_json(json.dumps(user['android_credentials']))
                user_info_service = build(
                    serviceName='oauth2',
                    version='v2',
                    http=credentials.authorize(httplib2.Http())
                )
                user_info = user_info_service.userinfo().get().execute()
            else:
                gplus_auth = True
        except AccessTokenRefreshError, e:
            gplus_auth = True
            print e

    if gplus_auth:
        try:
            oauth_flow = OAuth2WebServerFlow(
                client_id=app.config['GPLUS_CREDS']['client_id'],
                client_secret=app.config['GPLUS_CREDS']['client_secret'],
                scope=app.config['GPLUS_CREDS']['scope'],
                redirect_uri=app.config['GPLUS_CREDS']['redirect_uri']
            )
            credentials = oauth_flow.step2_exchange(access_token)
            user_info_service = build(
                serviceName='oauth2',
                version='v2',
                http=credentials.authorize(httplib2.Http())
            )
            user_info = user_info_service.userinfo().get().execute()

        except FlowExchangeError, e:
            print e
            return {'status': 'failure', 'error': str(e)}, 403

    insert_data = {}
    insert_data['android_credentials'] = json.loads(credentials.to_json())
    insert_data["pictureUrl"] = user_info.get('picture', "")
    insert_data["user_id"] = "g_" + user_info.get("id", "")
    insert_data["email"] = user_info.get("email", "")
    insert_data["name"] = user_info.get("name", "") + user_info.get("givenName", "")+" "+user_info.get("name", "") + user_info.get("familyName", "")

    insert_data['authData'] = {
        'id': user_info.get("id", ""),
        'type': 'google'
    }

    if self.user_exists(insert_data.get("user_id")):
        insert_data["updatedAt"] = datetime.datetime.now()
        device_id = set(db.users.find_one({'user_id': insert_data.get("user_id")}).get('device_id', []))
        if device_id:
            device_id.add(self.device_id)
        insert_data['device_id'] = list(device_id)
        result = db.users.update(
            {'user_id': insert_data.get("user_id")},
            {'$set': insert_data}
        )
    else:
        insert_data['device_id'] = [self.device_id]
        db.users.insert(insert_data)
        emails.email_welcome({
            "email": insert_data["email"],
            "name": insert_data["name"]
        })
    print credentials.to_json()
    print user_info
    return {'status': 'success', 'id': insert_data['user_id']}

1 个答案:

答案 0 :(得分:1)

问题似乎与您使用代码时使用的Google客户端凭据有关。您似乎已通过在 Google Developer Console-&gt;凭据 - >创建新客户ID

上选择“已安装的应用程序”为您的两个应用生成了一组凭据

您需要通过选择 Google Developer Console-&gt;凭据 - >创建新客户ID <上的“Web应用程序”,从Google Developer Console生成另一组Python服务器应用凭据/ p>

这就是为什么API抱怨它是“invalid_client”向Google服务器发出请求的原因。希望这可以解决您的问题。