it-swarm.com.de

Wie erhalte ich ein Zugriffstoken, nachdem der Benutzer über Google Mail in Android angemeldet ist?

Ich folge Google Anmeldung für Android . Jetzt kann ich das idToken abrufen, aber mein Backend-Server, den ich zuvor verwendet habe, erwartet ein access-Token, da ich zuvor den Google+ Login verwendet habe. Jetzt möchte ich meine Serverseite nicht ändern. Wie kann ich jedoch Google verwenden? Melden Sie sich an und erhalten Sie das Zugriffstoken in meiner Android-App, damit ich meinen Benutzer auf meinem Back-End-Server überprüfen kann.

Ich habe zuvor GooglePlay Service 7.5.0 verwendet und jetzt verwende ich GooglePlay Service spätestens 8.3.0.

23
aman.nepid

Für Ihre Anforderungen können Sie den folgenden Code verwenden:

Stellen Sie zunächst sicher, dass Sie über eine gültige Web OAuth 2.0-Client-ID verfügen:

<!-- Server Client ID.  This should be a valid Web OAuth 2.0 Client ID obtained
         from https://console.developers.google.com/ -->
    <string name="server_client_id">...e4p8.apps.googleusercontent.com</string>

Dann in der Aktivitätsklasse:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ...

    // For sample only: make sure there is a valid server client ID.
    validateServerClientID();

    // [START configure_signin]
    // Configure sign-in to request offline access to the user's ID, basic
    // profile, and Google Drive. The first time you request a code you will
    // be able to exchange it for an access token and refresh token, which
    // you should store. In subsequent calls, the code will only result in
    // an access token. By asking for profile access (through
    // DEFAULT_SIGN_IN) you will also get an ID Token as a result of the
    // code exchange.
    String serverClientId = getString(R.string.server_client_id);
    GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestScopes(new Scope(Scopes.DRIVE_APPFOLDER))
            .requestServerAuthCode(serverClientId)
            .requestEmail()
            .build();
    // [END configure_signin]

    // Build GoogleAPIClient with the Google Sign-In API and the above options.
    mGoogleApiClient = new GoogleApiClient.Builder(this)
            .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
            .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
            .build();
}

private void getAuthCode() {
    // Start the retrieval process for a server auth code.  If requested, ask for a refresh
    // token.  Otherwise, only get an access token if a refresh token has been previously
    // retrieved.  Getting a new access token for an existing grant does not require
    // user consent.
    Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
    startActivityForResult(signInIntent, RC_GET_AUTH_CODE);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == RC_GET_AUTH_CODE) {
        GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
        Log.d(TAG, "onActivityResult:GET_AUTH_CODE:success:" + result.getStatus().isSuccess());

        if (result.isSuccess()) {
            // [START get_auth_code]
            GoogleSignInAccount acct = result.getSignInAccount();
            String authCode = acct.getServerAuthCode();

            // Show signed-in UI.
            mAuthCodeTextView.setText(getString(R.string.auth_code_fmt, authCode));
            updateUI(true);

            // TODO(user): send code to server and exchange for access/refresh/ID tokens.
            // [END get_auth_code]
        } else {
            // Show signed-out UI.
            updateUI(false);
        }
    }
}

Den gesamten Code finden Sie unter ServerAuthCodeActivity.Java

Wenn Sie dieses Beispiel verwenden, sieht das Ergebnis folgendermaßen aus:

BNK's screenshot

Anschließend können Sie die Schritte ausführen, die in der folgenden Dokumentation von Google beschrieben werden (ab Schritt # 3. Senden Sie den Authentifizierungscode mithilfe von HTTPS POST an das Backend Ihrer App):

Google-Anmeldung für Android - Aktivieren des serverseitigen Zugriffs


UPDATE: Wenn Sie von den Kommentaren aus direkt auf die Android-Client-App zugreifen möchten, verwenden Sie bitte den folgenden Beispielcode (ersetzt durch Ihre client_id, client_secret und den Auth-Code). 

OkHttpClient client = new OkHttpClient();
    RequestBody requestBody = new FormEncodingBuilder()
            .add("grant_type", "authorization_code")
            .add("client_id", "812741506391-h38jh0j4fv0ce1krdkiq0hfvt6n5amrf.apps.googleusercontent.com")
            .add("client_secret", "{clientSecret}")
            .add("redirect_uri","")
            .add("code", "4/4-GMMhmHCXhWEzkobqIHGG_EnNYYsAkukHspeYUk9E8")
            .build();
    final Request request = new Request.Builder()
            .url("https://www.googleapis.com/oauth2/v4/token")
            .post(requestBody)
            .build();
    client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(final Request request, final IOException e) {
            Log.e(LOG_TAG, e.toString());                
        }

        @Override
        public void onResponse(Response response) throws IOException {
            try {
                JSONObject jsonObject = new JSONObject(response.body().string());
                final String message = jsonObject.toString(5);
                Log.i(LOG_TAG, message);                    
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    });

Bitte verwenden Sie compile 'com.squareup.okhttp:okhttp:2.6.0' (ver 3-RC1 wird unterschiedliche Klassen haben)

Mit einer erfolgreichen Antwort erhalten Sie folgende Informationen in logcat:

I/onResponse: {
              "expires_in": 3600,
              "token_type": "Bearer",
              "refresh_token": "1\/xz1eb0XU3....nxoALEVQ",
              "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjQxMWY1Ym......yWVsUA",
              "access_token": "ya29.bQKKYah-........_tkt980_qAGIo9yeWEG4"
         }
40
BNK

BNK hat es größtenteils vor Ort. Die Activity-Klasse entspricht der Antwort von BNK nur, wenn Sie den OkHttp-Part hinzufügen, sobald Sie die Variable GoogleSignInAccount in der Methode onActivityResult() abrufen.

Beim OkHttp-Anforderungsteil wurden jedoch immer noch Fehler angezeigt. Nach einigem Testen (und ein bisschen Glück) in Postman fand ich schließlich heraus, dass mir der Parameter id_token fehlte. Bei der OkHttp-Anforderung fehlte ein Parameter, d. H. Das id_token. Verwenden Sie das ID-Token, das Sie von GoogleSignInAccount erhalten

GoogleSignInAccount acct = result.getSignInAccount();
String idTokenString = acct.getIdToken();

Verwenden Sie nun diesen idTokenString zusammen mit allen Parametern im OkHttp-Teil der Antwort von BNK in etwa wie folgt

...

RequestBody requestBody = new FormEncodingBuilder()
            .add("grant_type", "authorization_code")
            .add("client_id", "812741506391-h38jh0j4fv0ce1krdkiq0hfvt6n5amrf.apps.googleusercontent.com")
            .add("client_secret", "{clientSecret}")
            .add("redirect_uri","")
            .add("code", "4/4-GMMhmHCXhWEzkobqIHGG_EnNYYsAkukHspeYUk9E8")
            .add("id_token", idTokenString) // Added this extra parameter here
            .build();

...

Die Antwort, die man bekommt, ist die gleiche wie die Antwort von BNK

{
  "access_token": "ya29.CjBgA_I58IabCJ...remainingAccessTokenHere",
  "token_type": "Bearer",
  "expires_in": 3577,
  "id_token": "eyJhbGciOiJS...veryLongStringHere"
}

Senden Sie nun dieses access_token an Ihren Backend-Server, um sich zu authentifizieren, wie Sie es zu Zeiten von GoogleAuthUtil und PlusAPI getan haben.

Hoffe das hilft :) Besonderer Dank geht an BNK!

3
Narayan Acharya

Für den Fall, dass ein anderer Benutzer Probleme hat, die letzte Anforderung zum Zugriff auf das Zugriffstoken von Google zu stellen. Im Folgenden finden Sie einen getesteten und funktionierenden Ansatz ab dem 11-01-2018. Retrofit2 verwenden. 

Zunächst einmal hier ein Link zu Google Doc über den Tokenaustausch-Endpunkt: https://developers.google.com/identity/protocols/OAuth2WebServer#exchange-authorization-code

public interface GoogleService {

@POST("token")
@FormUrlEncoded
@Headers("Content-Type:application/x-www-form-urlencoded")
Call<GoogleAuthData> getToken(
        @Field("grant_type") String grantType,
        @Field("client_id") String clientId,
        @Field("client_secret") String clientSecret,
        @Field("redirect_uri") String redirectUri,
        @Field("code") String code);
}

Dann nennen Sie es so:

Call<GoogleAuthData> call = RetroClient.getGoogleService().getToken(
            "authorization_code", context.getString(R.string.server_client_id),
            context.getString(R.string.server_client_secret), "", authCode);

Ich habe einen Weg gefunden, Zugriffstoken ohne idToken, Code, geheim oder irgendwelche Anforderungen zu erhalten (z. B. " https://www.googleapis.com/oauth2/v4/token ") ..__ Alles was Sie brauchen ist nur "Client-ID" . Führen Sie die folgenden Schritte aus:

  1. Verwenden Sie "GoogleSignIn", um sich anzumelden und das Objekt "Konto" zu erhalten.

    GoogleSignIn.getClient(
            ctx,
            GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                    .requestEmail()
                    .requestProfile()
                    .requestIdToken(KEY.GOOGLE_CLIENT_ID)
                    .requestServerAuthCode(KEY.GOOGLE_CLIENT_ID, true)
                    .build())
            .let { client ->
                client.signOut()
                    .let { task ->
                        Observable.create<GoogleSignInClient> { ob ->
                            task.addOnCompleteListener { ob.onNext(client) }
                        }
                    }
            }
            .flatMap {
                ctx.startActivityForResult(it.signInIntent, RC_SIGN_IN)
                ctx.activityResultObservable
            }
            .filter { it.requestCode == RC_SIGN_IN }
            .map {
                GoogleSignIn
                        .getSignedInAccountFromIntent(it.data)
                        .getResult(ApiException::class.Java)
            }
    

Hier verwende ich RxJava, um den Code zu schreiben. Sie können Ihren Code auch ohne schreiben.

  1. Innerhalb des Objekts "Konto" können Sie das Zugriffstoken mithilfe von "GoogleAuthUtil" abrufen.

            .flatMap { result ->
                Observable.create<AuthData> {
                    val scope = "oauth2:https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/userinfo.profile"
                    val accessToken = GoogleAuthUtil.getToken(context, result.account, scope)
                    // now you can use this token
                    it.onNext(accessToken)
                }
            }
    

Die Funktion "GoogleAuthUtil :: getToken" stellt eine Anforderung, sodass Sie sie nicht im UI-Thread ausführen können. Nun können Sie dieses Token an Ihren Server senden. ????

0
yuriel

Dank @BNK hat er die funktionierende Lösung bereitgestellt. Und hier ist eine offizielle Anleitung, wie Sie unter 'Authentifizierungscode' Zugriffstoken erhalten: https://developers.google.com/identity/protocols/OAuth2WebServer#exchange-authorization-code

Hier möchte ich meine Lösung mit reinen Android SDK-Klassen versorgen. Falls Sie nicht nur zu diesem Zweck eine fantastische Bibliothek hinzufügen möchten:

private String mAccessToken;
private long mTokenExpired;

private String requestAccessToken(GoogleSignInAccount googleAccount) {
    if (mAccessToken != null && SystemClock.elapsedRealtime() < mTokenExpired) return mAccessToken;
    mTokenExpired = 0;
    mAccessToken = null;

    HttpURLConnection conn = null;
    OutputStream os = null;
    InputStream is = null;
    InputStreamReader isr = null;
    BufferedReader br = null;

    try {
        final URL url = new URL("https://www.googleapis.com/oauth2/v4/token");
        conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setUseCaches(false);
        conn.setDoInput(true);
        conn.setDoOutput(true);
        conn.setConnectTimeout(3000);
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

        final StringBuilder b = new StringBuilder();
        b.append("code=").append(googleAccount.getServerAuthCode()).append('&')
         .append("client_id=").append(getString(R.string.default_web_client_id)).append('&')
         .append("client_secret=").append(getString(R.string.client_secret)).append('&')
         .append("redirect_uri=").append("").append('&')
         .append("grant_type=").append("authorization_code");

        final byte[] postData = b.toString().getBytes("UTF-8");

        os = conn.getOutputStream();
        os.write(postData);

        final int responseCode = conn.getResponseCode();
        if (200 <= responseCode && responseCode <= 299) {
            is = conn.getInputStream();
            isr = new InputStreamReader(is);
            br = new BufferedReader(isr);
        } else {
            Log.d("Error:", conn.getResponseMessage());
            return null;
        }

        b.setLength(0);
        String output;
        while ((output = br.readLine()) != null) {
            b.append(output);
        }

        final JSONObject jsonResponse = new JSONObject(b.toString());
        mAccessToken = jsonResponse.getString("access_token");
        mTokenExpired = SystemClock.elapsedRealtime() + jsonResponse.getLong("expires_in") * 1000;
        return mAccessToken;
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
            }
        }
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
            }
        }
        if (isr != null) {
            try {
                isr.close();
            } catch (IOException e) {
            }
        }
        if (br != null) {
            try {
                br.close();
            } catch (IOException e) {
            }
        }
        if (conn != null) {
            conn.disconnect();
        }
    }
    return null;
}

Führen Sie diese Methode im Hintergrundthread aus. Außerdem müssen Sie client_id und client_secret von der Google API-Konsole abrufen.

 Google APIs console id and secret

0
Oleksandr Albul