web-dev-qa-db-fra.com

Connexion silencieuse pour récupérer le jeton avec GoogleApiClient

J'utilise "Connexion Google" dans mon application. Par conséquent, j'utilise la classe GoogleApiClient pour obtenir l'e-mail utilisateur et le jeton ID dont j'ai besoin pour mon backend.

Lorsque l'utilisateur se connecte, j'ai accès à une activité (bien sûr) et j'utilise cette activité pour laisser GoogleApiClient gérer le contenu du cycle de vie de l'interface utilisateur en appelant builder.enableAutoManage (myActivity, ...)

Cela fonctionne bien.

Cependant, à un stade ultérieur (plusieurs jours plus tard), je dois obtenir un nouveau jeton (pour une raison que je n'irai pas plus loin ici). Je veux obtenir ce jeton en silence sans interaction avec l'utilisateur. Cependant, au point de mon code où j'ai besoin de ce nouveau jeton, je n'ai accès à aucune instance d'activité. Cela signifie que je ne suis pas en mesure d'effectuer l'appel mentionné ci-dessus, c'est-à-dire "builder.enableAutoManage". Et j'ai constaté que si je ne fais pas cet appel, la connexion silencieuse ne semble pas fonctionner.

J'ai joint le code ci-dessous. Maintenant, jetez un œil à la méthode "silentLogin". Tant que le jeton que j'ai reçu, car l'utilisateur a fait la connexion réelle, est inférieur à une heure, alors la déclaration "pendingResult.isDone" renverra true et le jeton mis en cache pourra être reçu. Cependant, si le jeton que j'ai reçu lorsque l'utilisateur s'est connecté a plus d'une heure, l'appel "en attenteResult.setResultCallback" est effectué MAIS LA méthode "onResult" IS JAMAIS APPELÉ et je ne peux pas obtenir un nouveau jeton. Ce problème ne se produit pas si je fais exactement la même chose à partir d'une activité (et que j'appelle également "builder.enableAutoManage").

Alors, quelqu'un sait-il ce que je fais mal et plus important encore - comment résoudre ce problème et obtenir un nouveau jeton sans accès à une instance d'activité?

J'utilise 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

Oui, la réponse ci-dessus est correcte. En général, tout GoogleApiClient doit être connecté avant de pouvoir vous renvoyer des données. enableAutoManage vous aide à appeler automatiquement connect ()/déconnect () pendant onStart ()/onStop (). Si vous n'utilisez pas autoManage, vous devrez vous connecter () manuellement.

Et encore mieux, vous devez vous déconnecter dans un bloc enfin.

En supposant que vous n'êtes pas sur le thread d'interface utilisateur.

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

Et aussi, pour nettoyer un peu votre code: 1. gso construit à partir de la configuration ci-dessous est identique à votre code collé ci-dessus:

GoogleSignInOptions gso =
   new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestIdToken(SERVER_CLIENT_ID)
        .requestEmail()
        .build();
  1. En fonction de votre logique actuelle, addOnConnectionFailedListener/addConnectionCallbacks n'aide que le journal adb. Peut-être simplement les supprimer complètement?
28
Isabella Chen

J'ai trouvé le problème. J'avais l'impression que la fonction

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

allait connecter le mGoogleApiClient pour moi (car il renvoie un résultat en attente). Cependant, ce n'était pas le cas et pour résoudre ce qui précède, j'avais juste besoin d'ajouter l'appel

ConnectionResult result = mGoogleApiClient.blockingConnect();

au début de la méthode silentLogin. (et bien sûr, déconnectez-vous plus tard, et assurez-vous également que l'appel est effectué dans un thread différent du thread principal)

tada '

9
Ola Melén

Pour ajouter aux deux réponses ci-dessus d'Isabella et Ola, si vous utilisez la nouvelle bibliothèque de connexion avec Firebase:

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
    }
}

En outre, cela peut être appelé à partir du thread d'interface utilisateur. FirebaseAuth.getInstance().currentUser renverra toujours l'objet utilisateur si vous vous êtes connecté une fois auparavant.

0
ravindu1024