![Diagramma della logica del token di autenticazione](https://developer.android.com/static/images/training/oauth_dance.png?authuser=6&hl=it)
Per accedere in modo sicuro a un servizio online, gli utenti devono eseguire l'autenticazione al servizio e fornire una prova della loro identità. Per un'applicazione che accede a un servizio di terze parti, il problema di sicurezza è ancora più complicato. Non solo l'utente deve essere autenticato per accedere al servizio, ma l'applicazione deve anche essere autorizzata ad agire per conto dell'utente.
Il protocollo OAuth2 è il metodo standard di settore per gestire l'autenticazione su servizi di terze parti. OAuth2 fornisce un singolo valore, chiamato token di autenticazione, che rappresenta sia l'identità dell'utente sia l'autorizzazione dell'applicazione ad agire per conto dell'utente. Questa lezione illustra la connessione a un server Google che supporta OAuth2. Anche se i servizi Google vengono utilizzati come esempio, le tecniche illustrate funzionano su qualsiasi servizio che supporti correttamente il protocollo OAuth2.
L'utilizzo di OAuth2 è consigliato per:
- Ottenere l'autorizzazione dall'utente per accedere a un servizio online utilizzando il proprio account.
- Autenticazione in un servizio online per conto dell'utente.
- Gestione degli errori di autenticazione.
Raccogliere informazioni
Per iniziare a utilizzare OAuth2, devi conoscere alcune informazioni specifiche dell'API relative al servizio a cui vuoi accedere:
- L'URL del servizio a cui vuoi accedere.
- L'ambito di autenticazione, ovvero una stringa che definisce il tipo specifico di accesso richiesto dalla tua app. Ad esempio, l'ambito di autenticazione per l'accesso in sola lettura a Google Tasks è
View your tasks
, mentre l'ambito di autenticazione per l'accesso in lettura/scrittura a Google Tasks èManage your tasks
. - Un ID client e un client secret, ovvero stringhe che identificano l'app nel servizio. Devi ottenere queste stringhe direttamente dal proprietario del servizio. Google dispone di un sistema self-service per ottenimento di ID client e secret.
Richiedere l'autorizzazione a Internet
Per le app che hanno come target Android 6.0 (livello API 23) e versioni successive, il metodo getAuthToken()
in sé non richiede autorizzazioni. Tuttavia, per eseguire operazioni sul token, devi aggiungere l'autorizzazione INTERNET
al file manifest, come mostrato nello snippet di codice riportato di seguito:
<manifest ... > <uses-permission android:name="android.permission.INTERNET" /> ... </manifest>
Richiedere un token di autenticazione
Per ottenere il token, chiama AccountManager.getAuthToken()
.
Attenzione: poiché alcune operazioni sull'account potrebbero comportare la comunicazione di rete, la maggior parte dei metodi AccountManager
è asincrona. Ciò significa che, invece di eseguire tutte le operazioni di autenticazione in una funzione, devi implementarla come una serie di callback.
Lo snippet seguente mostra come utilizzare una serie di callback per ricevere il token:
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
In questo esempio, OnTokenAcquired
è una classe che implementa
AccountManagerCallback
. AccountManager
chiama
run()
su OnTokenAcquired
con un
AccountManagerFuture
che contiene Bundle
. Se la chiamata ha esito positivo, il token si trova all'interno di Bundle
.
Ecco come recuperare il token da 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); ... } }
Se tutto va bene, Bundle
contiene un token valido nella chiave KEY_AUTHTOKEN
ed è tutto pronto.
La prima richiesta di un token di autenticazione potrebbe non andare a buon fine per diversi motivi:
- Un errore nel dispositivo o nella rete ha causato l'errore di
AccountManager
. - L'utente ha deciso di non concedere alla tua app l'accesso all'account.
- Le credenziali dell'account memorizzate non sono sufficienti per ottenere l'accesso all'account.
- Il token di autenticazione memorizzato nella cache è scaduto.
Le applicazioni possono gestire i primi due casi in modo banale, di solito mostrando semplicemente un messaggio di errore all'utente. Se la rete non è disponibile o l'utente ha deciso di non concedere l'accesso, la tua applicazione non può fare molto. Gli ultimi due casi sono un po' più complicati, perché ci si aspetta che le applicazioni ben strutturate gestiscano questi errori automaticamente.
Il terzo caso di errore, se le credenziali non sono sufficienti, viene comunicato tramite il Bundle
che ricevi in AccountManagerCallback
(OnTokenAcquired
dall'esempio precedente). Se Bundle
include un Intent
nella chiave KEY_INTENT
, l'autenticatore ti comunica che deve interagire direttamente con l'utente prima di poterti fornire un token valido.
Potrebbero esserci molti motivi per cui l'autenticatore restituisce un Intent
. Potrebbe
essere la prima volta che l'utente accede a questo account. Forse l'account dell'utente è scaduto e l'utente deve accedere di nuovo oppure le credenziali memorizzate non sono corrette. Forse l'account richiede l'autenticazione a due fattori o deve attivare la fotocamera per eseguire una scansione della retina. Non importa quale sia il motivo. Se vuoi un token valido, devi disattivare Intent
per ottenerlo.
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; } } }
Tieni presente che l'esempio utilizza startActivityForResult()
, in modo che tu possa acquisire
il risultato di Intent
implementando onActivityResult()
nella tua attività. Questo è importante: se non acquisisci il risultato della risposta dell'autenticatore Intent
, è impossibile capire se l'utente ha eseguito correttamente l'autenticazione.
Se il risultato è RESULT_OK
, l'autenticatore ha aggiornato le credenziali archiviate in modo che siano sufficienti per il livello di accesso richiesto e dovresti chiamare di nuovo AccountManager.getAuthToken()
per richiedere il nuovo token di autenticazione.
L'ultimo caso, in cui il token è scaduto, non è in realtà un errore AccountManager
. L'unico modo per scoprire se un token è scaduto è contattare il server e sarebbe
sprecato e costoso per AccountManager
accedere continuamente a internet per controllare
lo stato di tutti i suoi token. Quindi, si tratta di un errore che può essere rilevato solo quando un'applicazione come la tua tenta di utilizzare il token di autenticazione per accedere a un servizio online.
Connettersi al servizio online
L'esempio seguente mostra come connettersi a un server Google. Poiché Google utilizza il protocollo OAuth2 standard di settore per autenticare le richieste, le tecniche illustrate qui sono applicabili a tutti. Tuttavia, tieni presente che ogni server è diverso. Potresti dover apportare piccole modifiche a queste istruzioni per tenere conto della tua situazione specifica.
Le API di Google richiedono di fornire quattro valori per ogni richiesta: la chiave API, l'ID client, il client secret e la chiave di autenticazione. I primi tre provengono dal sito web
della console API di Google. L'ultimo è il valore stringa
ottenuto chiamando AccountManager.getAuthToken()
. che passi al server di Google come parte di una richiesta 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);
Se la richiesta restituisce
un codice di errore HTTP 401, il token è stato negato. Come menzionato nell'ultima sezione, il motivo più comune è la scadenza del token. La soluzione è semplice: chiama AccountManager.invalidateAuthToken()
e ripeti ancora una volta il processo di acquisizione del token.
I token scaduti sono molto frequenti e la loro correzione è molto semplice, pertanto molte
applicazioni presuppongono che il token sia scaduto prima ancora di richiederlo. Se il rinnovo di un token è un'operazione economica per il tuo server, potresti preferire chiamare AccountManager.invalidateAuthToken()
prima della prima chiamata a AccountManager.getAuthToken()
e evitare di richiedere un token di autenticazione due volte.