Zugriff auf Google-Nutzerdaten autorisieren

Bei der Authentifizierung wird festgestellt, wer eine Person ist. Sie wird häufig als Nutzerregistrierung oder ‑anmeldung bezeichnet. Die Autorisierung ist der Prozess, bei dem der Zugriff auf Daten oder Ressourcen gewährt oder abgelehnt wird. Beispiel: Ihre App fordert die Zustimmung eines Nutzers an, auf 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 enthält, die Google API-Daten verwenden können, aber nicht zu den Kernfunktionen Ihrer App gehören, muss Ihre App Fälle, in denen API-Daten nicht zugänglich sind, problemlos verarbeiten können. Sie können beispielsweise eine Liste der zuletzt gespeicherten Dateien ausblenden, wenn der Nutzer keinen Zugriff auf Drive gewährt hat.

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

Wenn Sie die Autorisierung von der Authentifizierung trennen, können Sie vermeiden, dass neue Nutzer überfordert werden oder dass Nutzer verwirrt sind, 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-Homepage zugewiesen sind. Diese Werte werden Nutzern bei der Registrierung auf dem Zustimmungsbildschirm „Ü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 package Attribut des <manifest> Elements in Ihrer AndroidManifest.xml Datei definiert.
    6. Geben Sie den SHA-1-Signaturzertifikat-Fingerabdruck der App-Verteilung ein.
    7. Wenn Ihre App die App-Signatur von Google Play verwendet, kopieren Sie den SHA-1-Fingerabdruck von der Seite für die App-Signatur in der Play Console.
    8. Wenn Sie Ihren eigenen Keystore und Ihre eigenen Signaturschlüssel verwalten, verwenden Sie das keytool Dienstprogramm, das in Java enthalten ist, um Zertifikatsinformationen in einem für Menschen lesbaren Format auszugeben. Kopieren Sie den SHA-1 Wert im Certificate fingerprints Abschnitt der keytool Ausgabe. Weitere Informationen finden Sie unter Authentifizieren Ihres Clients in der Dokumentation zu den Google APIs für Android.
    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 noch keine haben. Sie können die Felder „Autorisierte JavaScript-Quellen“ und „Autorisierte Weiterleitungs-URIs“ vorerst ignorieren. Diese Client-ID wird verwendet, um Ihren Back-End-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 von App-Imitationen zu verringern.

Wenn Sie ein Google Play-Entwicklerkonto haben und Ihre App in der Google Play Console registriert ist, können Sie dieses Konto verwenden, um die Bestätigung abzuschließen. Für eine erfolgreiche Bestätigung müssen die folgenden Voraussetzungen erfüllt sein:

  • Sie müssen in der Google Play Console eine registrierte Anwendung mit demselben Paketnamen und SHA-1-Zertifikat-Fingerabdruck wie der Android-OAuth-Client haben, für den Sie die Bestätigung durchführen.
  • Sie müssen die Berechtigung Administrator für die App in der Google Play Console haben. Weitere Informationen zur Zugriffsverwaltung in der Google Play Console

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

Wenn die Bestätigung erfolgreich ist, wird eine Benachrichtigung angezeigt, um den Erfolg der Bestätigung zu bestätigen. Andernfalls wird eine Fehlermeldung angezeigt.

So beheben Sie eine fehlgeschlagene Bestätigung:

  • Achten Sie darauf, dass die App, die Sie bestätigen, in der Google Play Console registriert ist.
  • Achten Sie darauf, dass Sie die Berechtigung Administrator für die App in der Google Play Console haben.

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:21.5.1"
}

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, die Zugriff auf den Speicher seiner Drive-App erfordert, 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. Hier wird davon ausgegangen, dass dies in einem Fragment erfolgt. Der Code prüft, ob die erforderlichen Berechtigungen erfolgreich erteilt 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 getServerAuthCode() Methode aus AuthorizationResult auf, um einen Autorisierungscode zu erhalten, den Sie an Ihr Back-End senden, um ihn gegen ein Zugriffs- und Aktualisierungstoken einzutauschen. Weitere Informationen finden Sie unter Dauerhaften Zugriff auf die Daten des Nutzers 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, wodurch der Zugriff beschleunigt und die Anzahl der Netzwerkaufrufe reduziert wird. 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 beim Verwenden eines Tokens eine 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. Sie enthält nur ein Token für die angeforderten Bereiche. Beispiel: Die Antwort auf das Abrufen eines Zugriffstokens für den Zugriff auf Google Drive eines Nutzers enthält nicht die Identität des vom Nutzer ausgewählten Kontos, obwohl damit auf Dateien auf dem Drive des Nutzers zugegriffen werden kann. So erhalten Sie Informationen wie den Namen oder die E‑Mail-Adresse des Nutzers:

  • 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 außerdem 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 Standard verwendet und der Schritt zur Kontoauswahl im Autorisierungsablauf wird übersprungen. Informationen zur Verwendung eines anderen Kontos für die Autorisierung finden Sie unter Autorisierung über ein anderes als das Standardkonto.

  • Fordern Sie in Ihrer Autorisierungsanfrage zusätzlich zu den gewünschten Bereichen (z. B. dem Drive scope) die Bereiche userinfo, profile, und openid an. Nachdem ein Zugriffstoken zurückgegeben wurde, rufen Sie die Nutzerinformationen ab, indem Sie eine GET-HTTP-Anfrage an den OAuth-Nutzerinfo-Endpunkt (https://www.googleapis.com/oauth2/v3/userinfo) senden. Verwenden Sie dazu Ihre bevorzugte HTTP-Bibliothek und fügen Sie das erhaltene Zugriffstoken im Header ein. Das 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-Objekt, das auf die angeforderten Bereiche beschränkt ist und im JSON-Format vorliegt.

Autorisierung über ein anderes als das 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 bei allen nachfolgenden Autorisierungsaufrufen dieses Standardkonto verwendet wird. Wenn Sie erzwingen möchten, dass die Kontoauswahl angezeigt wird, melden Sie den Nutzer mit der clearCredentialState() API von Credential Manager von der App ab.

Dauerhaften Zugriff auf die Daten des Nutzers aufrechterhalten

Wenn Sie über Ihre App auf die Daten des Nutzers zugreifen müssen, rufen Sie AuthorizationClient.authorize() einmal auf. In nachfolgenden Sitzungen und solange die gewährten Berechtigungen nicht vom Nutzer entfernt werden, rufen Sie dieselbe Methode auf, um ein Zugriffstoken zu erhalten, ohne dass eine Nutzerinteraktion erforderlich ist. Wenn Sie hingegen im Offlinemodus von Ihrem Back-End-Server aus auf die Daten des Nutzers zugreifen müssen, müssen Sie eine andere Art von Token anfordern, das als Aktualisierungstoken bezeichnet wird.

Zugriffstokens sind bewusst kurzlebig und haben eine Lebensdauer von einer Stunde. Wenn ein Zugriffstoken abgefangen oder kompromittiert wird, minimiert die begrenzte Gültigkeitsdauer potenziellen Missbrauch. Nach Ablauf der Gültigkeitsdauer ist das Token ungültig und alle Versuche, es zu verwenden, werden vom Ressourcenserver abgelehnt. Da Zugriffstokens kurzlebig sind, verwenden Server Aktualisierungstokens, um den kontinuierlichen Zugriff auf die Daten eines Nutzers aufrechtzuerhalten. Aktualisierungstokens sind Tokens mit langer 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 einen Autorisierungscode während des Autorisierungsschritts in Ihrer App abrufen, indem Sie „Offlinezugriff“ anfordern. Anschließend tauschen Sie den Autorisierungscode auf Ihrem Server gegen ein Aktualisierungstoken ein. Es ist wichtig, langlebige Aktualisierungstokens sicher auf Ihrem Server zu speichern, da sie wiederholt verwendet werden können, um neue Zugriffstokens zu erhalten. Aus Sicherheitsgründen wird daher dringend davon abgeraten, Aktualisierungstokens auf dem Gerät zu speichern. Stattdessen sollten sie auf den Back-End-Servern der App gespeichert werden, wo der Austausch gegen ein Zugriffstoken erfolgt.

Nachdem der Autorisierungscode an den Back-End-Server Ihrer App gesendet wurde, können Sie ihn auf dem Server gegen ein kurzlebiges Zugriffstoken und ein langlebiges Aktualisierungstoken eintauschen. Folgen Sie dazu der Anleitung zur Kontoautorisierung. Dieser Austausch sollte nur im Back-End 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 über ein Fragment 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
                }
            });
}