Pour accéder de manière sécurisée à un service en ligne, les utilisateurs doivent s'authentifier auprès du service et fournir une preuve de leur identité. Pour une application qui accède à un service tiers, le problème de sécurité est encore plus complexe. L'utilisateur doit non seulement être authentifié pour accéder au service, mais l'application doit également être autorisée à agir en son nom.
Le protocole OAuth2 est la méthode standard dans l'industrie pour gérer l'authentification auprès de services tiers. OAuth2 fournit une valeur unique, appelée jeton d'authentification, qui représente à la fois l'identité de l'utilisateur et l'autorisation d'une application à agir en son nom. Cette leçon explique comment se connecter à un serveur Google compatible avec OAuth2. Bien que les services Google soient utilisés à titre d'exemple, les techniques présentées fonctionneront pour tout service compatible avec le protocole OAuth2.
Le protocole OAuth2 est utile pour:
- Obtenir l'autorisation de l'utilisateur d'accéder à un service en ligne à l'aide de son compte
- S'authentifier auprès d'un service en ligne pour le compte de l'utilisateur
- Gérer les erreurs d'authentification
Recueillir des informations
Pour commencer à utiliser OAuth2, vous devez connaître certaines informations spécifiques aux API concernant le service auquel vous essayez d'accéder:
- URL du service auquel vous souhaitez accéder.
- Le champ d'application de l'authentification, qui est une chaîne qui définit le type d'accès spécifique demandé par votre application. Par exemple, le champ d'application de l'authentification pour l'accès en lecture seule à Google Tasks est
View your tasks
, tandis que le champ d'application de l'authentification pour l'accès en lecture/écriture à Google Tasks estManage your tasks
. - Un ID client et un code secret du client, qui sont des chaînes qui identifient votre application auprès du service. Vous devez obtenir ces chaînes directement auprès du propriétaire du service. Google dispose d'un système en libre-service pour obtenir des ID client et des secrets.
Demander l'autorisation d'accéder à Internet
Pour les applications ciblant Android 6.0 (niveau d'API 23) ou version ultérieure, la méthode getAuthToken()
ne nécessite aucune autorisation. Toutefois, pour effectuer des opérations sur le jeton, vous devez ajouter l'autorisation INTERNET
à votre fichier manifeste, comme indiqué dans l'extrait de code suivant:
<manifest ... > <uses-permission android:name="android.permission.INTERNET" /> ... </manifest>
Demander un jeton d'authentification
Pour obtenir le jeton, appelez AccountManager.getAuthToken()
.
Attention : Étant donné que certaines opérations de compte peuvent impliquer une communication réseau, la plupart des méthodes AccountManager
sont asynchrones. Cela signifie qu'au lieu d'effectuer tout le travail d'authentification dans une seule fonction, vous devez l'implémenter sous la forme d'une série de rappels.
L'extrait de code suivant montre comment utiliser une série de rappels pour obtenir le jeton:
Kotlin
val am: AccountManager = AccountManager.get(this) val options = Bundle() am.getAuthToken( myAccount_, // Account retrieved using getAccountsByType() "Manage your tasks", // Auth scope options, // Authenticator-specific options this, // Your activity OnTokenAcquired(), // Callback called when a token is successfully acquired Handler(OnError()) // Callback called if an error occurs )
Java
AccountManager am = AccountManager.get(this); Bundle options = new Bundle(); am.getAuthToken( myAccount_, // Account retrieved using getAccountsByType() "Manage your tasks", // Auth scope options, // Authenticator-specific options this, // Your activity new OnTokenAcquired(), // Callback called when a token is successfully acquired new Handler(new OnError())); // Callback called if an error occurs
Dans cet exemple, OnTokenAcquired
est une classe qui implémente AccountManagerCallback
. AccountManager
appelle run()
sur OnTokenAcquired
avec un AccountManagerFuture
contenant un Bundle
. Si l'appel a réussi, le jeton se trouve dans Bundle
.
Voici comment obtenir le jeton à partir de Bundle
:
Kotlin
private class OnTokenAcquired : AccountManagerCallback<Bundle> { override fun run(result: AccountManagerFuture<Bundle>) { // Get the result of the operation from the AccountManagerFuture. val bundle: Bundle = result.getResult() // The token is a named value in the bundle. The name of the value // is stored in the constant AccountManager.KEY_AUTHTOKEN. val token: String = bundle.getString(AccountManager.KEY_AUTHTOKEN) } }
Java
private class OnTokenAcquired implements AccountManagerCallback<Bundle> { @Override public void run(AccountManagerFuture<Bundle> result) { // Get the result of the operation from the AccountManagerFuture. Bundle bundle = result.getResult(); // The token is a named value in the bundle. The name of the value // is stored in the constant AccountManager.KEY_AUTHTOKEN. String token = bundle.getString(AccountManager.KEY_AUTHTOKEN); ... } }
Si tout se passe bien, Bundle
contient un jeton valide dans la clé KEY_AUTHTOKEN
et vous avez terminé.
Votre première demande de jeton d'authentification peut échouer pour plusieurs raisons:
- Une erreur sur l'appareil ou le réseau a entraîné l'échec de
AccountManager
. - L'utilisateur a décidé de ne pas autoriser votre application à accéder au compte.
- Les identifiants enregistrés ne suffisent pas pour accéder au compte.
- Le jeton d'authentification mis en cache a expiré.
Les applications peuvent gérer facilement les deux premiers cas, généralement simplement en affichant un message d'erreur à l'utilisateur. Si le réseau est en panne ou si l'utilisateur a décidé de ne pas accorder l'accès, votre application ne peut pas faire grand-chose pour y remédier. Les deux derniers cas sont un peu plus compliqués, car les applications bien conçues sont censées gérer automatiquement ces défaillances.
Le troisième cas d'échec, lié à des identifiants insuffisants, est communiqué via le Bundle
que vous recevez dans votre AccountManagerCallback
(OnTokenAcquired
de l'exemple précédent). Si Bundle
inclut un Intent
dans la clé KEY_INTENT
, l'authentificateur vous indique qu'il doit interagir directement avec l'utilisateur avant de pouvoir vous fournir un jeton valide.
L'authentificateur peut renvoyer une Intent
pour de nombreuses raisons. Il se peut que l'utilisateur se connecte à ce compte pour la première fois. Le compte de l'utilisateur a peut-être expiré et il doit se reconnecter, ou les identifiants stockés sont incorrects. Peut-être que le compte nécessite une authentification à deux facteurs ou qu'il doit activer l'appareil photo pour effectuer un scan rétinien. Peu importe la raison. Si vous voulez un jeton valide, vous devez déclencher Intent
pour l'obtenir.
Kotlin
private inner class OnTokenAcquired : AccountManagerCallback<Bundle> { override fun run(result: AccountManagerFuture<Bundle>) { val launch: Intent? = result.getResult().get(AccountManager.KEY_INTENT) as? Intent if (launch != null) { startActivityForResult(launch, 0) } } }
Java
private class OnTokenAcquired implements AccountManagerCallback<Bundle> { @Override public void run(AccountManagerFuture<Bundle> result) { ... Intent launch = (Intent) result.getResult().get(AccountManager.KEY_INTENT); if (launch != null) { startActivityForResult(launch, 0); return; } } }
Notez que l'exemple utilise startActivityForResult()
. Vous pouvez donc capturer le résultat de l'Intent
en implémentant onActivityResult()
dans votre propre activité. C'est important: si vous ne capturez pas le résultat de la réponse Intent
de l'authentificateur, il est impossible de savoir si l'utilisateur s'est correctement authentifié.
Si le résultat est RESULT_OK
, cela signifie que l'authentificateur a mis à jour les identifiants stockés afin qu'ils soient suffisants pour le niveau d'accès que vous avez demandé. Vous devez donc appeler à nouveau AccountManager.getAuthToken()
pour demander le nouveau jeton d'authentification.
Le dernier cas, où le jeton a expiré, n'est pas réellement un échec AccountManager
. Le seul moyen de savoir si un jeton a expiré est de contacter le serveur. De plus, il serait coûteux et coûteux pour AccountManager
de se connecter en permanence pour vérifier l'état de tous ses jetons. Il s'agit donc d'un échec qui ne peut être détecté que lorsqu'une application comme la vôtre tente d'utiliser le jeton d'authentification pour accéder à un service en ligne.
Se connecter au service en ligne
L'exemple ci-dessous montre comment se connecter à un serveur Google. Étant donné que Google utilise le protocole OAuth2 standard pour authentifier les requêtes, les techniques décrites ici sont applicables de manière générale. Gardez à l'esprit, cependant, que chaque serveur est différent. Vous devrez peut-être apporter de légers ajustements à ces instructions pour tenir compte de votre situation spécifique.
Les API Google exigent que vous fournissiez quatre valeurs par requête: la clé API, l'ID client, le code secret du client et la clé d'authentification. Les trois premiers proviennent
du site Web de la console Google APIs. La dernière est la valeur de chaîne que vous avez obtenue en appelant AccountManager.getAuthToken()
. Vous les transmettez au serveur Google dans le cadre d'une requête HTTP.
Kotlin
val url = URL("https://www.googleapis.com/tasks/v1/users/@me/lists?key=$your_api_key") val conn = url.openConnection() as HttpURLConnection conn.apply { addRequestProperty("client_id", your client id) addRequestProperty("client_secret", your client secret) setRequestProperty("Authorization", "OAuth $token") }
Java
URL url = new URL("https://www.googleapis.com/tasks/v1/users/@me/lists?key=" + your_api_key); URLConnection conn = (HttpURLConnection) url.openConnection(); conn.addRequestProperty("client_id", your client id); conn.addRequestProperty("client_secret", your client secret); conn.setRequestProperty("Authorization", "OAuth " + token);
Si la requête renvoie un code d'erreur HTTP 401, votre jeton a été refusé. Comme indiqué dans la section précédente, la raison la plus courante est que le jeton a expiré. La solution est simple: appelez AccountManager.invalidateAuthToken()
et répétez le processus d'acquisition du jeton une fois de plus.
Les jetons expirés étant très courants et leur résolution est si simple, de nombreuses applications supposent simplement que le jeton a expiré avant même de le demander. Si le renouvellement d'un jeton est une opération peu coûteuse pour votre serveur, vous pouvez appeler AccountManager.invalidateAuthToken()
avant le premier appel à AccountManager.getAuthToken()
et éviter d'avoir à demander un jeton d'authentification deux fois.