Zugriff auf Google-Nutzerdaten autorisieren

Bei der Authentifizierung wird die Identität einer Person festgestellt. Sie wird im Allgemeinen als Nutzerregistrierung oder -anmeldung bezeichnet. Bei der Autorisierung wird der Zugriff auf Daten oder Ressourcen gewährt oder abgelehnt. Ihre App fordert beispielsweise die Einwilligung eines Nutzers an, auf sein Google Drive zuzugreifen.

Authentifizierungs- und Autorisierungsaufrufe müssen zwei separate und unterschiedliche Abläufe sein, die auf den Anforderungen der App basieren.

Wenn Ihre App Funktionen hat, die Google API-Daten nutzen können, die aber nicht zu den Hauptfunktionen Ihrer App gehören, sollten Sie Ihre App so gestalten, dass sie auch dann funktioniert, wenn nicht auf API-Daten zugegriffen werden kann. Sie können beispielsweise eine Liste der zuletzt gespeicherten Dateien ausblenden, wenn der Nutzer keinen Zugriff auf Drive gewährt hat.

Sie sollten den Zugriff auf Bereiche, die Sie für den Zugriff auf Google APIs benötigen, nur dann anfordern, wenn der Nutzer eine Aktion ausführt, die den Zugriff auf eine bestimmte API erfordert. Sie sollten beispielsweise die Berechtigung zum Zugriff auf das Drive des Nutzers anfordern, wenn der Nutzer auf den Button In Drive speichern tippt.

Wenn Sie die Autorisierung von der Authentifizierung trennen, können Sie vermeiden, dass neue Nutzer überfordert werden oder sich fragen, warum sie um bestimmte Berechtigungen gebeten werden.

Für die Authentifizierung empfehlen wir die Verwendung der Credential Manager API. Für die Autorisierung von Aktionen, die Zugriff auf von Google gespeicherte Nutzerdaten erfordern, empfehlen wir die Verwendung von AuthorizationClient.

Google Cloud Console-Projekt einrichten

  1. Öffnen Sie Ihr Projekt in der Cloud Console oder erstellen Sie ein Projekt, falls Sie noch keines haben.
  2. Prüfen Sie auf der Seite Branding, ob alle Informationen vollständig und korrekt sind.
    1. Achten Sie darauf, dass Ihrer App ein korrekter App-Name, ein korrektes App-Logo und eine korrekte App-Startseite zugewiesen sind. Diese Werte werden Nutzern bei der Registrierung auf dem Zustimmungsbildschirm für „Über Google anmelden“ und auf dem Bildschirm Drittanbieter-Apps und ‑Dienste angezeigt.
    2. Achten Sie darauf, dass Sie die URLs der Datenschutzerklärung und der Nutzungsbedingungen Ihrer App angegeben haben.
  3. Erstellen Sie auf der Seite „Clients“ eine Android-Client-ID für Ihre App, falls Sie noch keine haben. Sie müssen den Paketnamen und die SHA-1-Signatur Ihrer App angeben.
    1. Rufen Sie die Seite „Clients“ auf.
    2. Klicken Sie auf Client erstellen.
    3. Wählen Sie den Anwendungstyp Android aus.
    4. Geben Sie einen Namen für den OAuth-Client ein. Dieser Name wird auf der Seite „Clients“ Ihres Projekts angezeigt, um den Client zu identifizieren.
    5. Geben Sie den Paketnamen Ihrer Android-App ein. Dieser Wert ist im Attribut package des Elements <manifest> in Ihrer AndroidManifest.xml-Datei definiert.
    6. Geben Sie den SHA-1-Signaturzertifikat-Fingerabdruck der App-Bereitstellung ein.
    7. Wenn deine App die App-Signatur von Google Play verwendet, kopiere den SHA-1-Fingerabdruck von der Seite „App-Signatur“ in der Play Console.
    8. Wenn Sie Ihren eigenen Schlüsselspeicher und Ihre eigenen Signaturschlüssel verwalten, verwenden Sie das in Java enthaltene Dienstprogramm keytool, um Zertifikatsinformationen in einem menschenlesbaren Format auszugeben. Kopieren Sie den Wert SHA-1 im Abschnitt Certificate fingerprints der keytool-Ausgabe. Weitere Informationen finden Sie in der Dokumentation zu Google APIs for Android unter Authenticating Your Client.
    9. (Optional) Bestätigen Sie die Inhaberschaft Ihrer Android-Anwendung.
  4. Erstellen Sie auf der Seite „Clients“ eine neue Client-ID für die Webanwendung, falls Sie das noch nicht getan haben. Die Felder „Autorisierte JavaScript-Quellen“ und „Autorisierte Weiterleitungs-URIs“ können Sie vorerst ignorieren. Diese Client-ID wird verwendet, um Ihren Backend-Server zu identifizieren, wenn er mit den Authentifizierungsdiensten von Google kommuniziert.
    1. Rufen Sie die Seite „Clients“ auf.
    2. Klicken Sie auf Client erstellen.
    3. Wählen Sie den Typ Webanwendung aus.

App-Inhaberschaft bestätigen

Sie können die Inhaberschaft Ihrer Anwendung bestätigen, um das Risiko einer Identitätsübernahme zu verringern.

Wenn Sie ein Google Play-Entwicklerkonto haben und Ihre App in der Google Play Console registriert ist, können Sie dieses Konto für die Überprüfung verwenden. Für eine erfolgreiche Überprüfung müssen die folgenden Anforderungen erfüllt sein:

  • Sie müssen eine registrierte Anwendung in der Google Play Console mit demselben Paketnamen und SHA-1-Signaturzertifikat-Fingerabdruck wie der Android-OAuth-Client haben, für den Sie die Überprüfung durchführen.
  • Sie benötigen die Administratorberechtigung für die App in der Google Play Console. Weitere Informationen zur Zugriffsverwaltung in der Google Play Console

Klicken Sie im Abschnitt Inhaberschaft der App bestätigen des Android-Clients auf die Schaltfläche Inhaberschaft bestätigen, um die Bestätigung abzuschließen.

Wenn die Überprüfung erfolgreich ist, wird eine Benachrichtigung angezeigt, die den Erfolg des Überprüfungsprozesses bestätigt. Andernfalls wird eine Fehlermeldung angezeigt.

Wenn die Bestätigung fehlgeschlagen ist, versuchen Sie Folgendes:

  • Die App, die Sie bestätigen, muss in der Google Play Console registriert sein.
  • Sie benötigen die Administratorberechtigung für die App in der Google Play Console.

Abhängigkeiten deklarieren

Deklarieren Sie in der Datei build.gradle Ihres Moduls Abhängigkeiten mit der neuesten Version der Google Identity Services-Bibliothek.

dependencies {
  // ... other dependencies

  implementation "com.google.android.gms:play-services-auth:22.0.0"
}

Berechtigungen anfordern, die für Nutzeraktionen erforderlich sind

Wenn ein Nutzer eine Aktion ausführt, die einen zusätzlichen Bereich erfordert, rufen Sie AuthorizationClient.authorize() auf. Wenn ein Nutzer beispielsweise eine Aktion ausführt, für die Zugriff auf seinen Drive-App-Speicher erforderlich ist, gehen Sie so vor:

Kotlin

val requestedScopes: List<Scope> = listOf(DriveScopes.DRIVE_FILE)
val authorizationRequest = AuthorizationRequest.builder()
    .setRequestedScopes(requestedScopes)
    .build()

Identity.getAuthorizationClient(activity)
    .authorize(authorizationRequestBuilder.build())
    .addOnSuccessListener { authorizationResult ->
        if (authorizationResult.hasResolution()) {
            val pendingIntent = authorizationResult.pendingIntent
            // Access needs to be granted by the user
            startAuthorizationIntent.launch(IntentSenderRequest.Builder(pendingIntent!!.intentSender).build())
        } else {
            // Access was previously granted, continue with user action
            saveToDriveAppFolder(authorizationResult);
        }
    }
    .addOnFailureListener { e -> Log.e(TAG, "Failed to authorize", e) }

Java

List<Scopes> requestedScopes = Arrays.asList(DriveScopes.DRIVE_FILE);
AuthorizationRequest authorizationRequest = AuthorizationRequest.builder()
    .setRequestedScopes(requestedScopes)
    .build();

Identity.getAuthorizationClient(activity)
    .authorize(authorizationRequest)
    .addOnSuccessListener(authorizationResult -> {
        if (authorizationResult.hasResolution()) {
            // Access needs to be granted by the user
            startAuthorizationIntent.launch(
                new IntentSenderRequest.Builder(
                    authorizationResult.getPendingIntent().getIntentSender()
                ).build()
            );
        } else {
            // Access was previously granted, continue with user action
            saveToDriveAppFolder(authorizationResult);
        }
    })
    .addOnFailureListener(e -> Log.e(TAG, "Failed to authorize", e));

Wenn Sie ActivityResultLauncher definieren, verarbeiten Sie die Antwort wie im folgenden Snippet gezeigt. Wir gehen davon aus, dass dies in einem Fragment geschieht. Der Code prüft, ob die erforderlichen Berechtigungen gewährt wurden, und führt dann die Nutzeraktion aus.

Kotlin

private lateinit var startAuthorizationIntent: ActivityResultLauncher<IntentSenderRequest>

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?,
): View? {
    // ...
    startAuthorizationIntent =
        registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { activityResult ->
            try {
                // extract the result
                val authorizationResult = Identity.getAuthorizationClient(requireContext())
                    .getAuthorizationResultFromIntent(activityResult.data)
                // continue with user action
                saveToDriveAppFolder(authorizationResult);
            } catch (e: ApiException) {
                // log exception
            }
        }
}

Java

private ActivityResultLauncher<IntentSenderRequest> startAuthorizationIntent;

@Override
public View onCreateView(
    @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// ...
startAuthorizationIntent =
    registerForActivityResult(
        new ActivityResultContracts.StartIntentSenderForResult(),
        activityResult -> {
            try {
            // extract the result
            AuthorizationResult authorizationResult =
                Identity.getAuthorizationClient(requireActivity())
                    .getAuthorizationResultFromIntent(activityResult.getData());
            // continue with user action
            saveToDriveAppFolder(authorizationResult);
            } catch (ApiException e) {
            // log exception
            }
        });
}

Wenn Sie serverseitig auf Google APIs zugreifen, rufen Sie die Methode getServerAuthCode() von AuthorizationResult auf, um einen Autorisierungscode zu erhalten. Diesen senden Sie an Ihr Backend, um ihn gegen ein Zugriffs- und ein Aktualisierungstoken einzutauschen. Weitere Informationen finden Sie unter Kontinuierlichen Zugriff auf die Nutzerdaten aufrechterhalten.

Berechtigungen für Nutzerdaten oder ‑ressourcen widerrufen

Wenn Sie zuvor gewährten Zugriff widerrufen möchten, rufen Sie AuthorizationClient.revokeAccess() auf. Wenn der Nutzer beispielsweise sein Konto aus Ihrer App entfernt und Ihrer App zuvor Zugriff auf DriveScopes.DRIVE_FILE gewährt wurde, verwenden Sie den folgenden Code, um den Zugriff zu widerrufen:

Kotlin

val requestedScopes: MutableList<Scope> = mutableListOf(DriveScopes.DRIVE_FILE)
RevokeAccessRequest revokeAccessRequest = RevokeAccessRequest.builder()
    .setAccount(account)
    .setScopes(requestedScopes)
    .build()

Identity.getAuthorizationClient(activity)
    .revokeAccess(revokeAccessRequest)
    .addOnSuccessListener { Log.i(TAG, "Successfully revoked access") }
    .addOnFailureListener { e -> Log.e(TAG, "Failed to revoke access", e) }

Java

List<Scopes> requestedScopes = Arrays.asList(DriveScopes.DRIVE_FILE);
RevokeAccessRequest revokeAccessRequest = RevokeAccessRequest.builder()
    .setAccount(account)
    .setScopes(requestedScopes)
    .build();

Identity.getAuthorizationClient(activity)
    .revokeAccess(revokeAccessRequest)
    .addOnSuccessListener(unused -> Log.i(TAG, "Successfully revoked access"))
    .addOnFailureListener(e -> Log.e(TAG, "Failed to revoke access", e));

Token-Cache leeren

OAuth-Zugriffstokens werden nach dem Empfang vom Server lokal im Cache gespeichert, was den Zugriff beschleunigt und die Anzahl der Netzwerkaufrufe reduziert. Diese Tokens werden automatisch aus dem Cache gelöscht, wenn sie ablaufen. Sie können aber auch aus anderen Gründen ungültig werden. Wenn Sie bei der Verwendung eines Tokens einen IllegalStateException erhalten, leeren Sie den lokalen Cache, damit die nächste Autorisierungsanfrage für ein Zugriffstoken an den OAuth-Server gesendet wird. Mit dem folgenden Snippet wird invalidAccessToken aus dem lokalen Cache entfernt:

Kotlin

Identity.getAuthorizationClient(activity)
    .clearToken(ClearTokenRequest.builder().setToken(invalidAccessToken).build())
    .addOnSuccessListener { Log.i(TAG, "Successfully removed the token from the cache") }
    .addOnFailureListener{ e -> Log.e(TAG, "Failed to clear token", e) }

Java

Identity.getAuthorizationClient(activity)
    .clearToken(ClearTokenRequest.builder().setToken(invalidAccessToken).build())
    .addOnSuccessListener(unused -> Log.i(TAG, "Successfully removed the token from the cache"))
    .addOnFailureListener(e -> Log.e(TAG, "Failed to clear the token cache", e));

Nutzerinformationen während der Autorisierung abrufen

Die Autorisierungsantwort enthält keine Informationen zum verwendeten Nutzerkonto, sondern nur ein Token für die angeforderten Bereiche. Beispielsweise wird in der Antwort zum Abrufen eines Zugriffstokens für den Zugriff auf das Google Drive eines Nutzers nicht die Identität des vom Nutzer ausgewählten Kontos offengelegt, obwohl damit auf Dateien im Drive des Nutzers zugegriffen werden kann. Wenn Sie Informationen wie den Namen oder die E-Mail-Adresse des Nutzers abrufen möchten, haben Sie folgende Möglichkeiten:

  • Melden Sie den Nutzer mit seinem Google-Konto über die Credential Manager APIs an, bevor Sie die Autorisierung anfordern. Die Authentifizierungsantwort von Credential Manager enthält Nutzerinformationen wie die E-Mail-Adresse und legt das Standardkonto der App auf das ausgewählte Konto fest. Bei Bedarf können Sie dieses Konto in Ihrer App verfolgen. Bei einer nachfolgenden Autorisierungsanfrage wird das Konto als Standardkonto verwendet und der Schritt zur Kontoauswahl im Autorisierungsablauf übersprungen. Wenn Sie ein anderes Konto für die Autorisierung verwenden möchten, lesen Sie den Abschnitt Autorisierung über ein Nicht-Standardkonto.

  • Fordern Sie in Ihrer Autorisierungsanfrage zusätzlich zu den gewünschten Bereichen (z. B. Drive scope) die Bereiche userinfo, profile und openid an. Nachdem ein Zugriffstoken zurückgegeben wurde, rufen Sie die Nutzerinformationen ab. Senden Sie dazu eine GET-HTTP-Anfrage an den OAuth-Endpunkt „userinfo“ (https://www.googleapis.com/oauth2/v3/userinfo). Verwenden Sie dazu Ihre bevorzugte HTTP-Bibliothek und fügen Sie das Zugriffstoken, das Sie im Header erhalten haben, ein. Dies entspricht dem folgenden curl-Befehl:

    curl -X GET \ "https://www.googleapis.com/oauth2/v1/userinfo?alt=json" \ -H "Authorization: Bearer $TOKEN"
    

    Die Antwort ist das UserInfo, beschränkt auf die angeforderten Bereiche und im JSON-Format.

Autorisierung über ein Nicht-Standardkonto

Wenn Sie Credential Manager zur Authentifizierung verwenden und AuthorizationClient.authorize() ausführen, wird das Standardkonto Ihrer App auf das vom Nutzer ausgewählte Konto festgelegt. Das bedeutet, dass alle nachfolgenden Autorisierungsaufrufe dieses Standardkonto verwenden. Wenn Sie die Kontoauswahl erzwingen möchten, melden Sie den Nutzer mit der clearCredentialState() API aus dem Credential Manager ab.

Kontinuierlichen Zugriff auf die Daten des Nutzers beibehalten

Wenn Sie über Ihre App auf die Daten des Nutzers zugreifen müssen, rufen Sie AuthorizationClient.authorize() einmal auf. Rufen Sie in nachfolgenden Sitzungen und solange die erteilten Berechtigungen nicht vom Nutzer entfernt werden, dieselbe Methode auf, um ein Zugriffstoken zu erhalten, mit dem Sie Ihre Ziele ohne Nutzerinteraktion erreichen können. Wenn Sie dagegen im Offlinemodus von Ihrem Backend-Server aus auf die Daten des Nutzers zugreifen müssen, müssen Sie einen anderen Tokentyp anfordern, der als „Aktualisierungstoken“ bezeichnet wird.

Zugriffstokens sind absichtlich kurzlebig und haben eine Lebensdauer von einer Stunde. Wenn ein Zugriffstoken abgefangen oder manipuliert wird, wird durch die begrenzte Gültigkeit ein potenzieller Missbrauch minimiert. Nach Ablauf wird das Token ungültig und alle Versuche, es zu verwenden, werden vom Ressourcenserver abgelehnt. Da Zugriffstokens nur kurzlebig sind, verwenden Server Aktualisierungstokens, um den Zugriff auf die Daten eines Nutzers aufrechtzuerhalten. Aktualisierungstokens sind Tokens mit einer langen Lebensdauer, die von einem Client verwendet werden, um ein kurzlebiges Zugriffstoken vom Autorisierungsserver anzufordern, wenn das alte Zugriffstoken abgelaufen ist, ohne dass eine Nutzerinteraktion erforderlich ist.

Um ein Aktualisierungstoken zu erhalten, müssen Sie zuerst während des Autorisierungsschritts in Ihrer App einen Authentifizierungscode (oder Autorisierungscode) anfordern, indem Sie „Offlinezugriff“ anfordern. Anschließend tauschen Sie den Authentifizierungscode auf Ihrem Server gegen ein Aktualisierungstoken aus. Es ist wichtig, langlebige Aktualisierungstokens sicher auf Ihrem Server zu speichern, da sie wiederholt verwendet werden können, um neue Zugriffstokens abzurufen. Aus Sicherheitsgründen wird daher dringend davon abgeraten, Aktualisierungstokens auf dem Gerät zu speichern. Stattdessen sollten sie auf den Backend-Servern der App gespeichert werden, wo der Austausch gegen ein Zugriffstoken stattfindet.

Nachdem der Autorisierungscode an den Backend-Server Ihrer App gesendet wurde, können Sie ihn gegen ein kurzlebiges Zugriffstoken auf dem Server und ein langlebiges Aktualisierungstoken eintauschen. Folgen Sie dazu der Anleitung im Leitfaden zur Kontoautorisierung. Dieser Austausch sollte nur im Backend Ihrer App erfolgen.

Kotlin

// Ask for offline access during the first authorization request
val authorizationRequest = AuthorizationRequest.builder()
    .setRequestedScopes(requestedScopes)
    .requestOfflineAccess(serverClientId)
    .build()

Identity.getAuthorizationClient(activity)
    .authorize(authorizationRequest)
    .addOnSuccessListener { authorizationResult ->
        startAuthorizationIntent.launch(IntentSenderRequest.Builder(
            pendingIntent!!.intentSender
        ).build())
    }
    .addOnFailureListener { e -> Log.e(TAG, "Failed to authorize", e) }

Java

// Ask for offline access during the first authorization request
AuthorizationRequest authorizationRequest = AuthorizationRequest.builder()
    .setRequestedScopes(requestedScopes)
    .requestOfflineAccess(serverClientId)
    .build();

Identity.getAuthorizationClient(getContext())
    .authorize(authorizationRequest)
    .addOnSuccessListener(authorizationResult -> {
        startAuthorizationIntent.launch(
            new IntentSenderRequest.Builder(
                authorizationResult.getPendingIntent().getIntentSender()
            ).build()
        );
    })
    .addOnFailureListener(e -> Log.e(TAG, "Failed to authorize"));

Im folgenden Snippet wird davon ausgegangen, dass die Autorisierung von einem Fragment aus gestartet wird.

Kotlin

private lateinit var startAuthorizationIntent: ActivityResultLauncher<IntentSenderRequest>

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?,
): View? {
    // ...
    startAuthorizationIntent =
        registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { activityResult ->
            try {
                val authorizationResult = Identity.getAuthorizationClient(requireContext())
                    .getAuthorizationResultFromIntent(activityResult.data)
                // short-lived access token
                accessToken = authorizationResult.accessToken
                // store the authorization code used for getting a refresh token safely to your app's backend server
                val authCode: String = authorizationResult.serverAuthCode
                storeAuthCodeSafely(authCode)
            } catch (e: ApiException) {
                // log exception
            }
        }
}

Java

private ActivityResultLauncher<IntentSenderRequest> startAuthorizationIntent;

@Override
public View onCreateView(
    @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // ...
    startAuthorizationIntent =
        registerForActivityResult(
            new ActivityResultContracts.StartIntentSenderForResult(),
            activityResult -> {
                try {
                    AuthorizationResult authorizationResult =
                        Identity.getAuthorizationClient(requireActivity())
                            .getAuthorizationResultFromIntent(activityResult.getData());
                    // short-lived access token
                    accessToken = authorizationResult.getAccessToken();
                    // store the authorization code used for getting a refresh token safely to your app's backend server
                    String authCode = authorizationResult.getServerAuthCode()
                    storeAuthCodeSafely(authCode);
                } catch (ApiException e) {
                    // log exception
                }
            });
}