L'autenticazione stabilisce l'identità di una persona e viene 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 dell'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, devi progettare 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.
Devi 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, devi richiedere l'autorizzazione per 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 azioni che richiedono l'accesso ai dati utente archiviati da Google, ti consigliamo di utilizzare AuthorizationClient.
Configura il progetto
- Apri il progetto in o crea un progetto se non ne hai già uno.
- Nella sezione ,
assicurati che tutte le informazioni siano complete e accurate.
- Assicurati che alla tua app siano assegnati un nome, un logo e una home page corretti. Questi valori verranno presentati agli utenti nella schermata del 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.
- In , 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 della tua app.
- In , crea un nuovo ID client "Applicazione web" se non l'hai ancora fatto. 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.
Dichiarare le dipendenze
Nel file build.gradle del modulo, dichiara le dipendenze utilizzando l'ultima versione della libreria Google Identity Services.
dependencies {
// ... other dependencies
implementation "com.google.android.gms:play-services-auth:21.4.0"
}
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.launchIntentSenderRequest.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 supponiamo che venga eseguita in un frammento. Il codice verifica
che le autorizzazioni richieste siano state concesse correttamente, quindi 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 (ApiException e) {
// 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 lato server, chiama il metodo
getServerAuthCode()
da AuthorizationResult
per ottenere un
codice di autorizzazione che invii al backend per lo scambio con un token di accesso e
aggiornamento. Per saperne di più, vedi
Mantenere l'accesso continuo ai dati dell'utente.
Revocare le autorizzazioni per i dati o le risorse dell'utente
Per revocare l'accesso concesso in precedenza, chiama
AuthorizationClient.revokeAccess()
. Ad esempio, se l'utente sta rimuovendo
il proprio account dalla tua app e alla tua app è stato precedentemente 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 diventare non validi anche per altri motivi.
Se ricevi un errore IllegalStateException
quando utilizzi un token, svuota la cache locale per assicurarti che la successiva richiesta di autorizzazione per un token di accesso venga inviata al server OAuth. Il seguente snippet 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));
Recuperare le informazioni 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'email dell'utente, hai le seguenti opzioni:
Accedi all'utente con il suo Account Google utilizzando le API Credential Manager prima di chiedere l'autorizzazione. La risposta di autenticazione di Credential Manager 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 la sezione Autorizzazione da un account non predefinito.
Nella richiesta di autorizzazione, oltre agli ambiti che ti interessano (ad esempio
Drive scope
), richiedi gli ambitiuserinfo
,profile
eopenid
. Dopo aver restituito un token di accesso, recupera le informazioni utente effettuando una richiesta HTTPGET
all'endpoint userinfo OAuth (https://www.googleapis.com/oauth2/v3/userinfo) utilizzando la tua libreria HTTP preferita 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 è il
UserInfo
, limitato agli ambiti richiesti, formattato in JSON.
Autorizzazione da un account non predefinito
Se utilizzi Credential Manager per l'autenticazione ed esegui
AuthorizationClient.authorize()
, l'account predefinito della tua app è 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
Credential Manager.
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, invece, devi accedere ai dati dell'utente in modalità offline dal server di backend, devi richiedere un altro tipo di token 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, il suo periodo di validità limitato riduce al minimo il potenziale uso improprio. Una volta scaduto, il token diventa non valido e qualsiasi tentativo di utilizzarlo verrà rifiutato 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 prolungata 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 "accesso offline", quindi scambiare il codice di autenticazione con un token di aggiornamento sul tuo server. È fondamentale archiviare i token di aggiornamento a 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. ma devono essere archiviati nei server di backend dell'app dove avviene lo scambio con un token di accesso.
Dopo l'invio del codice di autorizzazione al server di backend dell'app, puoi scambiarlo con un token di accesso a breve durata sul server e un token di aggiornamento a lunga durata seguendo i passaggi della guida all'autorizzazione dell'account. Questo scambio deve avvenire solo nel backend della tua 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.launchIntentSenderRequest.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"));
Il seguente snippet presuppone che l'autorizzazione venga avviata da un frammento.
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
}
});
}