L'autenticazione stabilisce l'identità di una persona ed è comunemente definita registrazione o accesso utente. L'autorizzazione è il processo di concessione o rifiuto dell'accesso a dati o risorse. Ad esempio, la tua app richiede il consenso di un utente per accedere al suo Google Drive.
Le chiamate di autenticazione e autorizzazione devono essere due flussi separati e distinti in base alle esigenze dell'app.
Se la tua app ha funzionalità che possono utilizzare i dati delle API di Google, ma non sono richieste come parte delle funzionalità principali dell'app, progetta l'app in modo che possa gestire correttamente i casi in cui i dati delle API non sono accessibili. Ad esempio, potresti nascondere un elenco di file salvati di recente quando l'utente non ha concesso l'accesso a Drive.
Dovresti richiedere l'accesso agli ambiti necessari per accedere alle API di Google solo quando l'utente esegue un'azione che richiede l'accesso a una determinata API. Ad esempio, dovresti richiedere l'autorizzazione ad accedere a Drive dell'utente ogni volta che l'utente tocca un pulsante Salva su Drive.
Separando l'autorizzazione dall'autenticazione, puoi evitare di sopraffare i nuovi utenti o di confonderli sul motivo per cui vengono richieste determinate autorizzazioni.
Per l'autenticazione, ti consigliamo di utilizzare l'API Credential Manager. Per autorizzare le azioni che richiedono l'accesso ai dati utente archiviati da Google, ti consigliamo di utilizzare AuthorizationClient.
Configurare il progetto della console Google Cloud
- Apri il progetto nella console Cloud, o creane uno se non ne hai già uno.
- Nella pagina Branding,
assicurati che tutte le informazioni siano complete e accurate.
- Assicurati che all'app siano assegnati un nome, un logo e una home page corretti. Questi valori verranno presentati agli utenti nella schermata per il consenso di Accedi con Google durante la registrazione e nella schermata App e servizi di terze parti.
- Assicurati di aver specificato gli URL delle norme sulla privacy e dei Termini di servizio della tua app.
- Nella pagina Client,
crea un ID client Android per la tua app se non ne hai già uno. Dovrai specificare il nome del pacchetto e la firma SHA-1 dell'app.
- Vai alla pagina Client.
- Fai clic su Crea client.
- Seleziona il tipo di applicazione Android.
- Inserisci un nome per il client OAuth. Questo nome viene visualizzato nella pagina Client del progetto per identificare il client.
- Inserisci il nome del pacchetto della tua app per Android. Questo valore è definito nell'
packageattributo dell'elemento<manifest>nel fileAndroidManifest.xml. - Inserisci l'impronta digitale del certificato di firma SHA-1 della distribuzione dell'app.
- Se la tua app utilizza la firma dell'app di Google Play, copia l'impronta digitale SHA-1 dalla pagina di firma dell'app di Play Console.
- Se gestisci il tuo keystore e le tue chiavi di firma, utilizza l'utilità keytool
inclusa in Java per stampare le informazioni del certificato in un formato
leggibile. Copia il valore
SHA-1nellaCertificate fingerprintssezione dell'output di keytool. Per saperne di più, consulta Autenticare il client nella documentazione delle API di Google per Android. - (Facoltativo) Verifica la proprietà dell'applicazione Android.
- Nella pagina Client,
crea un nuovo ID client "Applicazione web" se non ne hai già uno. Per il momento puoi ignorare i campi "Origini JavaScript autorizzate" e "URI di reindirizzamento autorizzati". Questo ID client verrà utilizzato per identificare il server di backend quando comunica con i servizi di autenticazione di Google.
- Vai alla pagina Client.
- Fai clic su Crea client.
- Seleziona il tipo di applicazione web.
Verificare la proprietà dell'app
Puoi verificare la proprietà della tua applicazione per ridurre il rischio di furto d'identità dell'app.
Per completare la procedura di verifica, puoi utilizzare il tuo account sviluppatore Google Play, se ne hai uno e la tua app è registrata su Google Play Console. Per una verifica riuscita, devono essere soddisfatti i seguenti requisiti:
- Devi avere un'applicazione registrata in Google Play Console con lo stesso nome del pacchetto e la stessa impronta digitale del certificato di firma SHA-1 del client OAuth Android per cui stai completando la verifica.
- Devi avere l'autorizzazione di amministratore per l'app in Google Play Console. Scopri di più sulla gestione dell'accesso in Google Play Console.
Nella sezione Verifica la proprietà dell'app del client Android, fai clic sul pulsante Verifica proprietà per completare la procedura di verifica.
Se la verifica va a buon fine, viene visualizzata una notifica per confermare il successo della procedura di verifica. In caso contrario, viene visualizzato un prompt di errore.
Per risolvere un problema di verifica, prova a:
- Assicurati che l'app che stai verificando sia un'app registrata in Google Play Console.
- Assicurati di avere l'autorizzazione di amministratore per l'app in Google Play Console.
Dichiarare le dipendenze
Nel file build.gradle del modulo, dichiara le dipendenze utilizzando l'ultima versione della libreria dei servizi di identità Google.
dependencies {
// ... other dependencies
implementation "com.google.android.gms:play-services-auth:21.5.1"
}
Richiedere le autorizzazioni richieste dalle azioni dell'utente
Ogni volta che un utente esegue un'azione che richiede un ambito aggiuntivo, chiama AuthorizationClient.authorize(). Ad esempio, se un utente esegue un'azione che richiede l'accesso allo spazio di archiviazione dell'app Drive, procedi nel seguente modo:
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));
Quando definisci ActivityResultLauncher, gestisci la risposta come mostrato nello snippet seguente, in cui si presuppone che venga eseguita in un fragment. Il codice verifica che le autorizzazioni richieste siano state concesse correttamente e poi esegue l'azione dell'utente.
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
}
});
}
Se accedi alle API di Google sul lato server, chiama il
getServerAuthCode() metodo da AuthorizationResult per ottenere un
codice di autorizzazione che invii al backend per scambiarlo con un token di accesso e un
token di aggiornamento. Per saperne di più, consulta
Mantenere l'accesso continuo ai dati dell'utente.
Revocare le autorizzazioni per i dati utente o le risorse
Per revocare l'accesso concesso in precedenza, chiama
AuthorizationClient.revokeAccess(). Ad esempio, se l'utente sta rimuovendo il suo account dalla tua app e alla tua app è stato concesso l'accesso a DriveScopes.DRIVE_FILE, utilizza il seguente codice per revocare l'accesso:
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));
Svuotare la cache dei token
I token di accesso OAuth vengono memorizzati nella cache locale al momento della ricezione dal server, velocizzando l'accesso e riducendo le chiamate di rete. Questi token vengono eliminati automaticamente dalla cache alla scadenza, ma possono anche diventare non validi per altri motivi.
Se ricevi un IllegalStateException quando utilizzi un token, svuota la cache locale per assicurarti che la prossima richiesta di autorizzazione per un token di accesso venga inviata al server OAuth. Lo snippet seguente rimuove invalidAccessToken dalla cache locale:
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));
Ottenere informazioni sull'utente durante l'autorizzazione
La risposta di autorizzazione non contiene informazioni sull'account utente utilizzato; la risposta contiene solo un token per gli ambiti richiesti. Ad esempio, la risposta per ottenere un token di accesso per accedere a Google Drive di un utente non rivela l'identità dell'account selezionato dall'utente, anche se può essere utilizzata per accedere ai file sul Drive dell'utente. Per ottenere informazioni come il nome o l'indirizzo email dell'utente, hai le seguenti opzioni:
Fai accedere l'utente con il suo Account Google utilizzando le API Credential Manager prima di chiedere l'autorizzazione. La risposta di autenticazione di Gestore delle credenziali include informazioni sull'utente, come l'indirizzo email, e imposta anche l'account predefinito dell'app sull'account selezionato; se necessario, puoi monitorare questo account nella tua app. Una successiva richiesta di autorizzazione utilizza l'account come predefinito e salta il passaggio di selezione dell'account nel flusso di autorizzazione. Per utilizzare un account diverso per l'autorizzazione, consulta Autorizzazione da un account non predefinito.
Nella richiesta di autorizzazione, oltre agli ambiti che ti interessano (ad esempio, l'
Drive scope), chiedi gli ambitiuserinfo,profileeopenid. Dopo aver ricevuto un token di accesso, recupera le informazioni sull'utente effettuando una richiesta HTTPGETall'endpoint userinfo OAuth (https://www.googleapis.com/oauth2/v3/userinfo) utilizzando la libreria HTTP che preferisci e includendo il token di accesso che hai ricevuto nell'intestazione, equivalente al seguente comandocurl:curl -X GET \ "https://www.googleapis.com/oauth2/v1/userinfo?alt=json" \ -H "Authorization: Bearer $TOKEN"La risposta è
UserInfo, limitata agli ambiti richiesti, in formato JSON.
Autorizzazione da un account non predefinito
Se utilizzi Gestore delle credenziali per l'autenticazione ed esegui
AuthorizationClient.authorize(), l'account predefinito della tua app viene impostato su
quello selezionato dall'utente. Ciò significa che tutte le chiamate successive per l'autorizzazione utilizzano questo account predefinito. Per forzare la visualizzazione del selettore di account, disconnetti l'utente dall'app utilizzando l'API clearCredentialState() di Gestore delle credenziali.
Mantenere l'accesso continuo ai dati dell'utente
Se devi accedere ai dati dell'utente dalla tua app, chiama
AuthorizationClient.authorize() una sola volta; nelle sessioni successive e finché le autorizzazioni concesse non vengono rimosse dall'utente, chiama lo stesso
metodo per ottenere un token di accesso per raggiungere i tuoi obiettivi, senza alcuna interazione dell'utente. Se, d'altra parte, devi accedere ai dati dell'utente in modalità offline, dal server di backend, devi richiedere un tipo di token diverso chiamato "token di aggiornamento".
I token di accesso sono progettati intenzionalmente per avere una durata breve, ovvero di un'ora. Se un token di accesso viene intercettato o compromesso, la sua finestra di validità limitata riduce al minimo il potenziale uso improprio. Dopo la scadenza, il token diventa non valido e tutti i tentativi di utilizzarlo verranno rifiutati dal server delle risorse. Poiché i token di accesso hanno una durata breve, i server utilizzano i token di aggiornamento per mantenere l'accesso continuo ai dati di un utente. I token di aggiornamento sono token con una durata elevata che vengono utilizzati da un client per richiedere un token di accesso di breve durata dal server di autorizzazione, quando il vecchio token di accesso è scaduto, senza alcuna interazione dell'utente.
Per ottenere un token di aggiornamento, devi prima ottenere un codice di autenticazione (o codice di autorizzazione) durante il passaggio di autorizzazione nella tua app chiedendo l'"accesso offline", quindi scambiare il codice di autenticazione con un token di aggiornamento sul tuo server. È fondamentale archiviare i token di aggiornamento di lunga durata in modo sicuro sul server, perché possono essere utilizzati ripetutamente per ottenere nuovi token di accesso. Pertanto, è fortemente sconsigliato archiviare i token di aggiornamento sul dispositivo per motivi di sicurezza. Devono invece essere archiviati nei server di backend dell'app in cui avviene lo scambio con un token di accesso.
Dopo che il codice di autorizzazione è stato inviato al server di backend dell'app, puoi scambiarlo con un token di accesso di breve durata sul server e un token di aggiornamento di lunga durata seguendo i passaggi descritti nella guida all'autorizzazione dell'account. Questo scambio deve avvenire solo nel backend dell'app.
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"));
Lo snippet seguente presuppone che l'autorizzazione venga avviata da 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
}
});
}