web-dev-qa-db-ja.com

サイレントサインインしてGoogleApiClientでトークンを取得する

アプリで「Googleログイン」を使用しています。したがって、GoogleApiClientクラスを使用して、ユーザーのメールとバックエンドに必要なIDトークンを取得します。

ユーザーがサインインすると、(もちろん)アクティビティにアクセスでき、そのアクティビティを使用して、builder.enableAutoManage(myActivity、...)を呼び出すことにより、GoogleApiClientにUIライフサイクルの要素を処理させます。

これは正常に動作します。

ただし、後の段階(数日後)で、新しいトークンを取得する必要があります(何らかの理由で、ここではこれ以上説明しません)。ユーザーの操作なしでこのトークンをサイレントで取得したい。ただし、この新しいトークンが必要なコードの時点では、Activityインスタンスにアクセスできません。つまり、上記の呼び出し、つまり「builder.enableAutoManage」を実行できません。そして、私がそのように電話をかけないと、サイレントログインが機能していないように見えます。

以下のコードを添付しました。ここで、「silentLogin」メソッドを見てください。ユーザーが実際にサインインしたときに受け取ったトークンが1時間未満である限り、 "pendingResult.isDone"ステートメントはtrueを返し、キャッシュされたトークンを受け取ることができます。ただし、ユーザーが実際にサインインしたときに受け取ったトークンが1時間以上古い場合、「pendingResult.setResultCallback」の呼び出しが行われますが、「onResult」メソッドIS決して呼び出されず、新しいトークンを取得できないこの問題は、アクティビティからまったく同じことを行った場合には発生しません(それによって、「builder.enableAutoManage」も呼び出します)。

だから、誰かが私が間違っていること、そしてもっと重要なことを知っていますか?この問題を解決して、アクティビティインスタンスにアクセスせずに新しいトークンを取得する方法は?

私はcom.google.Android.gms:play-services-auth:8.4.0を使用しています

package com.google.samples.quickstart.signin;

import Android.content.Context;
import Android.os.Bundle;
import Android.util.Log;

import com.google.Android.gms.auth.api.Auth;
import com.google.Android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.Android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.Android.gms.auth.api.signin.GoogleSignInResult;
import com.google.Android.gms.common.ConnectionResult;
import com.google.Android.gms.common.Scopes;
import com.google.Android.gms.common.api.GoogleApiClient;
import com.google.Android.gms.common.api.OptionalPendingResult;
import com.google.Android.gms.common.api.ResultCallback;
import com.google.Android.gms.common.api.Scope;

/**
 * Use this class to login with google account using the OpenId oauth method.
 */
public class GoogleLoginStackOverflow {
    private static final String TAG = GoogleLoginIdToken.class.getName();
    private static final String SERVER_CLIENT_ID = "XXXXXXXXXXXXXXXXXXXXXXXX.apps.googleusercontent.com";

    private GoogleApiClient mGoogleApiClient;
    private Context mContext;

    private GoogleLoginStackOverflow(Context appContext) {
        this.mContext = appContext;
        createGoogleClient();
    }

    /**
     * Performs a silent sign in and fetch a token.
     *
     * @param appContext Application context
     */
    public static void silentLogin(Context appContext) {
        GoogleLoginStackOverflow googleLoginIdToken = new GoogleLoginStackOverflow(appContext);
        googleLoginIdToken.silentLogin();
    }

    private void createGoogleClient() {
        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestProfile()
                .requestScopes(new Scope(Scopes.PROFILE))
                .requestIdToken(SERVER_CLIENT_ID)
                .requestEmail()
                .build();

        mGoogleApiClient = new GoogleApiClient.Builder(mContext)
                .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
                    @Override
                    public void onConnectionFailed(ConnectionResult connectionResult) {
                        System.out.println("onConnectionFailed  = " + connectionResult);
                        onSilentLoginFinished(null);
                    }
                })
                .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
                    @Override
                    public void onConnected(Bundle bundle) {
                        System.out.println("onConnected bundle = " + bundle);
                        onSilentLoginFinished(null);
                    }

                    @Override
                    public void onConnectionSuspended(int i) {
                        System.out.println("onConnectionSuspended i = " + i);
                        onSilentLoginFinished(null);
                    }
                }).addApi(Auth.GOOGLE_SIGN_IN_API, gso)
                .build();
    }

    private void silentLogin() {
        OptionalPendingResult<GoogleSignInResult> pendingResult = Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient);
        if (pendingResult != null) {
            if (pendingResult.isDone()) {
                // If the user's cached credentials are valid, the OptionalPendingResult will be "done"
                // and the GoogleSignInResult will be available instantly.
                Log.d(TAG, " ----------------  CACHED SIGN-IN ------------");
                System.out.println("pendingResult is done = ");
                GoogleSignInResult signInResult = pendingResult.get();
                onSilentLoginFinished(signInResult);
            } else {
                System.out.println("Setting result callback");
                // If the user has not previously signed in on this device or the sign-in has expired,
                // this asynchronous branch will attempt to sign in the user silently.  Cross-device
                // single sign-on will occur in this branch.
                pendingResult.setResultCallback(new ResultCallback<GoogleSignInResult>() {
                    @Override
                    public void onResult(GoogleSignInResult googleSignInResult) {
                        System.out.println("googleSignInResult = " + googleSignInResult);
                        onSilentLoginFinished(googleSignInResult);
                    }
                });
            }
        } else {
            onSilentLoginFinished(null);
        }
    }

    private void onSilentLoginFinished(GoogleSignInResult signInResult) {
        System.out.println("GoogleLoginIdToken.onSilentLoginFinished");
        if (signInResult != null) {
            GoogleSignInAccount signInAccount = signInResult.getSignInAccount();
            if (signInAccount != null) {
                String emailAddress = signInAccount.getEmail();
                String token = signInAccount.getIdToken();
                System.out.println("token = " + token);
                System.out.println("emailAddress = " + emailAddress);
            }
        }
    }
}
21
Ola Melén

はい、上記の答えは正しいです。一般に、GoogleApiClientは、データを返す前に接続する必要があります。 enableAutoManageは、onStart()/ onStop()中にconnect()/ disconnect()を自動的に呼び出すのに役立ちます。 autoManageを使用しない場合は、手動でconnect()を実行する必要があります。

さらに良いことに、finallyブロックで切断する必要があります。

UIスレッドを使用していないと仮定します。

try {
    ConnectionResult result = mGoogleApiClient.blockingConnect();
    if (result.isSuccess()) {
        GoogleSignInResult googleSignInResult =
            Auth.GoogleSignInApi.silentSignIn(googleApiClient).await();
    ...
    }
} finally {
    mGoogleApiClient.disconnect();
}

また、コードを少しクリーンアップするには、次のようにします。1.以下の構成からビルドされたgsoは、上記の貼り付けられたコードと同じです。

GoogleSignInOptions gso =
   new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestIdToken(SERVER_CLIENT_ID)
        .requestEmail()
        .build();
  1. 現在のロジックに基づいて、addOnConnectionFailedListener/addConnectionCallbacksはadbログ以外には役立ちません。たぶん、それらを完全に削除しますか?
28
Isabella Chen

問題が見つかりました。機能という印象を受けました

OptionalPendingResult<GoogleSignInResult> pendingResult = Auth.GoogleSignInApi.silentSignIn(googleApiClient);

mGoogleApiClientを接続するつもりでした(pending結果を返すため)。しかし、それはそうではなく、上記を解決するために私は呼び出しを追加する必要がありました

ConnectionResult result = mGoogleApiClient.blockingConnect();

silentLoginメソッドの最初。 (そしてもちろん、後で後で切断し、呼び出しがメインスレッドとは異なるスレッドで行われることも確認してください)

多田

9
Ola Melén

上記のイザベラとオラによる2つの回答に追加するには、Firebaseでlibの新しいサインインを使用している場合:

_FirebaseAuth.getInstance().currentUser?.let{ 
    //create sign-in options the usual way
    val googleSignInClient = GoogleSignIn.getClient(context, gso)
    googleSignInClient.silentSignIn().addOnCompleteListener {
        val account: GoogleSignInAccount? = it.result
        //get user info from account object
    }
}
_

また、これはUIスレッドから呼び出すことができます。 FirebaseAuth.getInstance().currentUserは、以前にサインインしたことがある場合、常にユーザーオブジェクトを返します。

0
ravindu1024