L'authentification permet d'établir l'identité d'une personne. Elle est généralement appelée "inscription" ou "connexion". L'autorisation est le processus qui consiste à accorder ou à refuser l'accès à des données ou à des ressources. Par exemple, votre application demande le consentement d'un utilisateur pour accéder à son compte Google Drive.
Les appels d'authentification et d'autorisation doivent être deux flux distincts et séparés en fonction des besoins de l'application.
Si votre application comporte des fonctionnalités qui peuvent utiliser des données d'API Google, mais qui ne sont pas requises dans le cadre de ses fonctionnalités principales, concevez-la de manière à pouvoir gérer correctement les cas où les données d'API ne sont pas accessibles. Par exemple, vous pouvez masquer une liste de fichiers récemment enregistrés lorsque l'utilisateur n'a pas accordé l'accès à Drive.
Vous ne devez demander l'accès aux champs d'application dont vous avez besoin pour accéder aux API Google que lorsque l'utilisateur effectue une action qui nécessite l'accès à une API particulière. Par exemple, vous devez demander l'autorisation d'accéder au compte Drive de l'utilisateur chaque fois qu'il appuie sur un bouton Enregistrer dans Drive.
En séparant l'autorisation de l'authentification, vous pouvez éviter de submerger les nouveaux utilisateurs ou de les induire en erreur quant à la raison pour laquelle certaines autorisations leur sont demandées.
Pour l'authentification, nous vous recommandons d'utiliser l'API Gestionnaire d'identifiants. Pour autoriser les actions qui nécessitent l'accès aux données utilisateur stockées par Google, nous vous recommandons d'utiliser AuthorizationClient.
Configurer votre projet Google Cloud Console
- Ouvrez votre projet dans la Cloud Console, ou créez-en un si vous n'en avez pas encore.
- Sur la page Branding,
assurez-vous que toutes les informations sont complètes et exactes.
- Vérifiez que le nom, le logo et la page d'accueil de votre application sont corrects. Ces valeurs seront présentées aux utilisateurs sur l'écran de consentement "Se connecter avec Google" lors de l'inscription et sur l'écran "Applications et services tiers".
- Assurez-vous d'avoir spécifié les URL des règles de confidentialité et des conditions d'utilisation de votre application.
- Sur la page Clients,
créez un ID client Android pour votre application si vous n'en avez pas déjà un. Vous devrez spécifier le nom du package et la signature SHA-1 de votre application.
- Accédez à la page "Clients".
- Cliquez sur Créer un client.
- Sélectionnez le type d'application Android.
- Saisissez un nom pour le client OAuth. Ce nom s'affiche sur la page Clients de votre projet pour identifier le client.
- Saisissez le nom du package de votre application Android. Cette valeur est définie dans l'
packageattribut de l'élément<manifest>de votre fichierAndroidManifest.xml. - Saisissez l'empreinte du certificat de signature SHA-1 de la distribution de l'application.
- Si votre application utilise la signature d'application Google Play, copiez l'empreinte SHA-1 à partir de la page de signature d'application de la Play Console.
- Si vous gérez votre propre keystore et vos propres clés de signature, utilisez l'utilitaire
keytool
inclus dans Java pour imprimer les informations sur le certificat dans un format lisible. Copiez la valeur
SHA-1dans laCertificate fingerprintssection de la sortie `keytool`. Pour en savoir plus, consultez Authentifier votre client dans la documentation sur les API Google pour Android. - (Facultatif) Validez la propriété de votre application Android.
- Sur la page Clients,
créez un ID client "Application Web" si vous n'en avez pas déjà un. Vous pouvez ignorer les champs "Origines JavaScript autorisées" et "URI de redirection autorisés" pour le moment. Cet ID client sera utilisé pour identifier votre serveur backend lorsqu'il communiquera avec les services d'authentification de Google.
- Accédez à la page "Clients".
- Cliquez sur Créer un client.
- Sélectionnez le type Application Web.
Valider la propriété de l'application
Vous pouvez valider la propriété de votre application pour réduire le risque d'usurpation d'identité.
Pour effectuer le processus de validation, vous pouvez utiliser votre compte de développeur Google Play si vous en possédez un et que votre application est enregistrée dans la Google Play Console. Pour que la validation réussisse, vous devez respecter les exigences suivantes :
- Vous devez disposer d'une application enregistrée dans la Google Play Console avec le même nom de package et la même empreinte du certificat de signature SHA-1 que le client OAuth Android pour lequel vous effectuez la validation.
- Vous devez disposer de l'autorisation Administrateur pour l'application dans la Google Play Console. En savoir plus sur la gestion des accès dans la Google Play Console.
Dans la section Valider la propriété de l'application du client Android, cliquez sur le bouton Valider la propriété pour effectuer le processus de validation.
Si la validation réussit, une notification s'affiche pour confirmer la réussite du processus. Sinon, une invite d'erreur s'affiche.
Pour corriger une validation qui a échoué, procédez comme suit :
- Assurez-vous que l'application que vous validez est enregistrée dans la Google Play Console.
- Assurez-vous de disposer de l'autorisation Administrateur pour l'application dans la Google Play Console.
Déclarer des dépendances
Dans le fichier build.gradle de votre module, déclarez des dépendances à l'aide de la dernière version de la bibliothèque Google Identity Services.
dependencies {
// ... other dependencies
implementation "com.google.android.gms:play-services-auth:21.5.1"
}
Demander les autorisations requises par les actions de l'utilisateur
Chaque fois qu'un utilisateur effectue une action qui nécessite un champ d'application supplémentaire, appelez AuthorizationClient.authorize(). Par exemple, si un utilisateur effectue une action qui nécessite l'accès au stockage de son application Drive, procédez comme suit :
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));
Lorsque vous définissez ActivityResultLauncher, gérez la réponse comme indiqué dans l'extrait de code suivant, où nous supposons que cela est effectué dans un fragment. Le code vérifie que les autorisations requises ont été accordées, puis exécute l'action de l'utilisateur.
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
}
});
}
Si vous accédez aux API Google côté serveur, appelez la
getServerAuthCode() méthode à partir de AuthorizationResult pour obtenir un
code d'autorisation que vous envoyez à votre backend afin de l'échanger contre un jeton d'accès et un
jeton d'actualisation. Pour en savoir plus, consultez
Maintenir un accès continu aux données de l'utilisateur.
Révoquer les autorisations d'accès aux données ou aux ressources de l'utilisateur
Pour révoquer un accès précédemment accordé, appelez
AuthorizationClient.revokeAccess(). Par exemple, si l'utilisateur supprime son compte de votre application et que votre application a précédemment obtenu l'accès à DriveScopes.DRIVE_FILE, utilisez le code suivant pour révoquer l'accès :
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));
Effacer le cache de jetons
Les jetons d'accès OAuth sont mis en cache localement lors de leur réception du serveur, ce qui accélère l'accès et réduit les appels réseau. Ces jetons sont automatiquement supprimés du cache lorsqu'ils expirent, mais ils peuvent également devenir non valides pour d'autres raisons.
Si vous recevez une erreur IllegalStateException lorsque vous utilisez un jeton, effacez le cache local pour vous assurer que la prochaine requête d'autorisation pour un jeton d'accès est envoyée au serveur OAuth. L'extrait de code suivant supprime invalidAccessToken du cache local :
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));
Obtenir des informations sur l'utilisateur lors de l'autorisation
La réponse d'autorisation ne contient aucune information sur le compte utilisateur utilisé. Elle ne contient qu'un jeton pour les champs d'application demandés. Par exemple, la réponse permettant d'obtenir un jeton d'accès pour accéder au compte Google Drive d'un utilisateur ne révèle pas l'identité du compte sélectionné par l'utilisateur, même si elle peut être utilisée pour accéder aux fichiers de son Drive. Pour obtenir des informations telles que le nom ou l'adresse e-mail de l'utilisateur, vous disposez des options suivantes :
Connectez l'utilisateur avec son compte Google à l'aide des API Gestionnaire d'identifiants avant de demander l'autorisation. La réponse d'authentification du Gestionnaire d'identifiants inclut des informations sur l'utilisateur, telles que son adresse e-mail, et définit également le compte par défaut de l'application sur le compte sélectionné. Si nécessaire, vous pouvez suivre ce compte dans votre application. Une requête d'autorisation ultérieure utilise le compte par défaut et ignore l'étape de sélection du compte dans le flux d'autorisation. Pour utiliser un autre compte pour l'autorisation, consultez Autorisation à partir d'un compte non défini par défaut.
Dans votre requête d'autorisation, en plus des champs d'application souhaités (par exemple, le
Drive scope), demandez les champs d'applicationuserinfo,profileetopenid. Une fois qu'un jeton d'accès est renvoyé, obtenez les informations sur l'utilisateur en envoyant une requête HTTPGETau point de terminaison OAuth userinfo (https://www.googleapis.com/oauth2/v3/userinfo) à l'aide de votre bibliothèque HTTP préférée et en incluant le jeton d'accès que vous avez reçu dans l'en-tête, ce qui équivaut à la commandecurlsuivante :curl -X GET \ "https://www.googleapis.com/oauth2/v1/userinfo?alt=json" \ -H "Authorization: Bearer $TOKEN"La réponse est l'objet
UserInfo, limité aux champs d'application demandés, au format JSON.
Autorisation à partir d'un compte non défini par défaut
Si vous utilisez le Gestionnaire d'identifiants pour l'authentification et que vous exécutez
AuthorizationClient.authorize(), le compte par défaut de votre application est défini sur
celui sélectionné par votre utilisateur. Cela signifie que tous les appels d'autorisation ultérieurs utilisent ce compte par défaut. Pour forcer l'affichage du sélecteur de compte,
déconnectez l'utilisateur de l'application à l'aide de l'API clearCredentialState() du
Gestionnaire d'identifiants.
Maintenir un accès continu aux données de l'utilisateur
Si vous devez accéder aux données de l'utilisateur à partir de votre application, appelez
AuthorizationClient.authorize() une seule fois. Lors des sessions suivantes, et tant que l'utilisateur n'a pas supprimé les autorisations accordées, appelez la même méthode pour obtenir un jeton d'accès afin d'atteindre vos objectifs, sans aucune interaction de l'utilisateur. Si, en revanche, vous devez accéder aux données de l'utilisateur en mode hors connexion, à partir de votre serveur backend, vous devez demander un autre type de jeton appelé "jeton d'actualisation".
Les jetons d'accès sont intentionnellement conçus pour être de courte durée et avoir une durée de vie d'une heure. Si un jeton d'accès est intercepté ou compromis, sa fenêtre de validité limitée minimise les utilisations abusives potentielles. Une fois expiré, le jeton devient non valide, et toute tentative d'utilisation sera rejetée par le serveur de ressources. Étant donné que les jetons d'accès sont de courte durée, les serveurs utilisent des jetons d'actualisation pour maintenir un accès continu aux données d'un utilisateur. Les jetons d'actualisation sont des jetons de longue durée utilisés par un client pour demander un jeton d'accès de courte durée au serveur d'autorisation, lorsque l'ancien jeton d'accès a expiré, sans aucune interaction de l'utilisateur.
Pour obtenir un jeton d'actualisation, vous devez d'abord obtenir un code d'authentification (ou code d'autorisation) lors de l'étape d'autorisation de votre application en demandant un "accès hors connexion", puis échanger le code d'authentification contre un jeton d'actualisation sur votre serveur. Il est essentiel de stocker les jetons d'actualisation de longue durée de manière sécurisée sur votre serveur, car ils peuvent être utilisés à plusieurs reprises pour obtenir de nouveaux jetons d'accès. Par conséquent, il est fortement déconseillé de stocker des jetons d'actualisation sur l'appareil pour des raisons de sécurité. Ils doivent plutôt être stockés sur les serveurs backend de l'application où l'échange contre un jeton d'accès a lieu.
Une fois le code d'authentification envoyé au serveur backend de votre application, vous pouvez l'échanger contre un jeton d'accès de courte durée sur le serveur et un jeton d'actualisation de longue durée en suivant les étapes du guide d'autorisation de compte. Cet échange ne doit avoir lieu que dans le backend de votre application.
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"));
L'extrait de code suivant suppose que l'autorisation est lancée à partir d'un fragment.
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
}
});
}