A autenticação estabelece quem é alguém e é comumente chamada de inscrição ou login do usuário. A autorização é o processo de conceder ou negar acesso a dados ou recursos. Por exemplo, seu app pede o consentimento do usuário para acessar o Google Drive dele.
As chamadas de autenticação e autorização precisam ser dois fluxos separados e distintos com base nas necessidades do app.
Se o app tiver recursos que podem usar dados da API Google, mas não forem obrigatórios como parte dos recursos principais do app, crie o app para que ele consiga lidar com casos em que os dados da API não estão acessíveis. Por exemplo, você pode ocultar uma lista de arquivos salvos recentemente quando o usuário não concedeu acesso ao Drive.
Solicite acesso aos escopos necessários para acessar as APIs do Google somente quando o usuário realizar uma ação que exija acesso a uma API específica. Por exemplo, solicite permissão para acessar o Drive do usuário sempre que ele tocar em um botão "Salvar no Drive".
Ao separar a autorização da autenticação, você evita sobrecarregar novos usuários ou confundir os usuários sobre o motivo de estarem sendo solicitadas determinadas permissões.
Para autenticação, recomendamos usar a API Credential Manager. Para autorizar ações que precisam de acesso aos dados do usuário armazenados pelo Google, recomendamos usar o AuthorizationClient.
Crie o projeto
- Abra seu projeto no ou crie um se ainda não tiver.
- Na ,
confira se todas as informações estão completas e corretas.
- Confira se o app tem um nome, um logotipo e uma página inicial corretos. Esses valores serão apresentados aos usuários na tela de consentimento do recurso "Fazer login com o Google" ao se inscreverem e na tela de apps e serviços de terceiros.
- Verifique se você especificou os URLs da Política de Privacidade e dos Termos de Serviço do app.
- No , crie um ID de cliente do Android para seu app se você ainda não tiver um. Você precisará especificar o nome do pacote e a assinatura SHA-1 do app.
- No , crie um ID do cliente "Aplicativo da Web" se ainda não tiver feito isso. Por enquanto, ignore os campos "Origens JavaScript autorizadas" e "URIs de redirecionamento autorizados". Esse ID do cliente será usado para identificar seu servidor de back-end quando ele se comunicar com os serviços de autenticação do Google.
Declarar dependências
No arquivo build.gradle do módulo, declare dependências usando a versão mais recente da biblioteca do Google Identity Services.
dependencies {
// ... other dependencies
implementation "com.google.android.gms:play-services-auth:21.4.0"
}
Solicitar permissões necessárias para ações do usuário
Sempre que um usuário realizar uma ação que exija um escopo adicional, chame
AuthorizationClient.authorize()
. Por exemplo, se um usuário realizar uma ação
que exija acesso ao armazenamento do app Drive, faça o seguinte:
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));
Ao definir ActivityResultLauncher
, processe a resposta conforme mostrado no
snippet a seguir, em que presumimos que isso é feito em um fragmento. O código verifica
se as permissões necessárias foram concedidas e realiza
a ação do usuário.
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 você estiver acessando as APIs do Google no lado do servidor, chame o método
getServerAuthCode()
de AuthorizationResult
para receber um
código de autorização que você envia ao back-end para trocar por um token de acesso e
de atualização. Para saber mais, consulte
Manter o acesso contínuo aos dados do usuário.
Revogar permissões para dados ou recursos do usuário
Para revogar o acesso concedido anteriormente, chame
AuthorizationClient.revokeAccess()
. Por exemplo, se o usuário estiver removendo
a conta dele do seu app, e o app tiver recebido acesso a
DriveScopes.DRIVE_FILE
, use o seguinte código para revogar o acesso:
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));
Limpar o cache de tokens
Os tokens de acesso do OAuth são armazenados em cache localmente após o recebimento do servidor, acelerando
o acesso e reduzindo as chamadas de rede. Esses tokens são excluídos automaticamente do cache quando expiram, mas também podem ficar inválidos por outros motivos.
Se você receber um IllegalStateException
ao usar um token, limpe o cache
local para garantir que a próxima solicitação de autorização de um token de acesso seja enviada
ao servidor OAuth. O snippet a seguir remove o invalidAccessToken
do
cache local:
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));
Receber informações do usuário durante a autorização
A resposta de autorização não contém informações sobre a conta de usuário usada. Ela só tem um token para os escopos solicitados. Por exemplo, a resposta para obter um token de acesso ao Google Drive de um usuário não revela a identidade da conta selecionada pelo usuário, mesmo que possa ser usada para acessar arquivos no drive dele. Para acessar informações como o nome ou e-mail do usuário, você tem as seguintes opções:
Faça login do usuário com a Conta do Google usando as APIs do Gerenciador de credenciais antes de pedir autorização. A resposta de autenticação do Credential Manager inclui informações do usuário, como o endereço de e-mail, e também define a conta padrão do app como a conta selecionada. Se necessário, você pode rastrear essa conta no app. Uma solicitação de autorização subsequente usa a conta como padrão e pula a etapa de seleção de conta no fluxo de autorização. Para usar uma conta diferente na autorização, consulte Autorização de uma conta não padrão.
Na solicitação de autorização, além dos escopos desejados (por exemplo,
Drive scope
), peça os escoposuserinfo
,profile
eopenid
. Depois que um token de acesso for retornado, receba as informações do usuário fazendo uma solicitação HTTPGET
para o endpoint userinfo do OAuth (https://www.googleapis.com/oauth2/v3/userinfo) usando sua biblioteca HTTP preferida e incluindo o token de acesso que você recebeu no cabeçalho, equivalente ao seguinte comandocurl
:curl -X GET \ "https://www.googleapis.com/oauth2/v1/userinfo?alt=json" \ -H "Authorization: Bearer $TOKEN"
A resposta é o
UserInfo
, limitado aos escopos solicitados e formatado em JSON.
Autorização de uma conta não padrão
Se você usar o Credential Manager para autenticar e executar
AuthorizationClient.authorize()
, a conta padrão do app será definida como
a selecionada pelo usuário. Isso significa que todas as chamadas subsequentes para
autorização usam essa conta padrão. Para forçar a exibição do seletor de contas,
desconecte o usuário do app usando a API clearCredentialState()
do
Gerenciador de credenciais.
Manter o acesso contínuo aos dados do usuário
Se você precisar acessar os dados do usuário no seu app, chame
AuthorizationClient.authorize()
uma vez. Nas sessões subsequentes e enquanto
as permissões concedidas não forem removidas pelo usuário, chame o mesmo
método para receber um token de acesso e alcançar seus objetivos sem interação do
usuário. Por outro lado, se você precisar acessar os dados do usuário em um
modo off-line, do servidor de back-end, será necessário solicitar um tipo
diferente de token chamado "token de atualização".
Os tokens de acesso são projetados para ter curta duração e uma vida útil de uma hora. Se um token de acesso for interceptado ou comprometido, a validade limitada dele vai minimizar o potencial de uso indevido. Depois da expiração, o token fica inválido, e qualquer tentativa de usá-lo será rejeitada pelo servidor de recursos. Como os tokens de acesso são de curta duração, os servidores usam tokens de atualização para manter o acesso contínuo aos dados de um usuário. Os tokens de atualização têm uma longa duração e são usados por um cliente para solicitar um token de acesso de curta duração do servidor de autorização quando o token de acesso antigo expira, sem qualquer interação do usuário.
Para conseguir um token de atualização, primeiro você precisa obter um código de autenticação (ou código de autorização) durante a etapa de autorização no seu app pedindo "acesso off-line" e, em seguida, trocar o código de autenticação por um token de atualização no seu servidor. É fundamental armazenar tokens de atualização de longa duração com segurança no seu servidor, porque eles podem ser usados repetidamente para receber novos tokens de acesso. Portanto, não é recomendável armazenar tokens de atualização no dispositivo devido a problemas de segurança. Em vez disso, eles precisam ser armazenados nos servidores de back-end do app, onde ocorre a troca por um token de acesso.
Depois que o código de autenticação for enviado ao servidor de back-end do app, você poderá trocá-lo por um token de acesso de curta duração no servidor e um token de atualização de longa duração seguindo as etapas no guia de autorização da conta. Essa troca só deve acontecer no back-end do seu 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"));
O snippet a seguir pressupõe que a autorização seja iniciada de um fragmento.
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
}
});
}