
Per accedere in modo sicuro a un servizio online, gli utenti devono autenticarsi 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 è la soluzione 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. Sebbene i servizi Google vengano utilizzati come esempio, le tecniche dimostrate funzioneranno su qualsiasi servizio che supporta correttamente il protocollo OAuth2.
L'utilizzo di OAuth2 è consigliato per:
- Ottenere l'autorizzazione dell'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 stai tentando di 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 di 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 la tua app nel servizio. Devi ottenere queste stringhe direttamente dal proprietario del servizio. Google dispone di un sistema self-service per ottenere 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()
stesso non richiede autorizzazioni. Tuttavia, per eseguire operazioni sul token, devi aggiungere l'autorizzazione INTERNET
al file manifest, come mostrato nel seguente snippet di codice:
<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 dell'account potrebbero comportare la comunicazione di rete, la maggior parte dei metodi AccountManager
è asincrona. Ciò significa che, invece di svolgere tutte le operazioni di autenticazione in una sola 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
contenente un Bundle
. Se la chiamata ha esito positivo, il token si trova all'interno di Bundle
.
Ecco come ottenere 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 tua 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 il mancato funzionamento di
AccountManager
. - L'utente ha deciso di non concedere alla tua app l'accesso all'account.
- Le credenziali dell'account memorizzato 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, l'applicazione non può fare molto. Gli ultimi due casi sono un po' più complicati, perché si prevede che le applicazioni ben funzionate gestiscano questi errori automaticamente.
Il terzo caso di errore, se le credenziali non sono sufficienti, viene comunicato tramite il Bundle
che hai ricevuto nel tuo 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 che possa fornirti un token valido.
I motivi per cui l'autenticatore potrebbe restituire un Intent
potrebbero essere diversi. Potrebbe essere la prima volta che l'utente accede a questo account. Forse l'account dell'utente è scaduto e l'utente deve eseguire di nuovo l'accesso oppure le credenziali archiviate 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 di che cosa si tratta. Se vuoi un token valido, devi attivare 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()
, per consentirti di acquisire il risultato di Intent
implementando onActivityResult()
nella tua attività. Questo è importante: se non acquisisci il risultato dalla 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. Devi 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
dispendioso e costoso per AccountManager
accedere continuamente a internet per controllare
lo stato di tutti i suoi token. Si tratta di un errore che può essere rilevato soltanto 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 di Google. Poiché Google utilizza il protocollo OAuth2 standard di settore per autenticare le richieste, le tecniche illustrate qui sono applicabili in modo ampio. 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, significa che il token è stato negato. Come accennato nell'ultima sezione, il motivo più comune è che il token è scaduto. La soluzione è semplice: chiama AccountManager.invalidateAuthToken()
e ripeti ancora una volta la procedura di acquisizione dei token.
Poiché i token scaduti sono un evento molto comune e correggerli è così semplice, molte applicazioni presumono che il token sia scaduto prima ancora di richiederlo. Se il rinnovo di un token per il tuo server è un'operazione economica, potresti preferire chiamare AccountManager.invalidateAuthToken()
prima della prima chiamata al numero AccountManager.getAuthToken()
e evitare di richiedere un token di autenticazione due volte.