Autentikasi menetapkan identitas seseorang, dan biasanya disebut sebagai pendaftaran atau login pengguna. Otorisasi adalah proses pemberian atau penolakan akses ke data atau resource. Misalnya, aplikasi Anda meminta izin pengguna Anda untuk mengakses Google Drive pengguna.
Panggilan autentikasi dan otorisasi harus berupa dua alur terpisah dan berbeda berdasarkan kebutuhan aplikasi.
Jika aplikasi Anda memiliki fitur yang dapat menggunakan data Google API, tetapi tidak diperlukan sebagai bagian dari fitur inti aplikasi, Anda harus mendesain aplikasi agar dapat menangani kasus dengan baik saat data API tidak dapat diakses. Misalnya, Anda dapat menyembunyikan daftar file yang baru-baru ini disimpan saat pengguna belum memberikan akses Drive.
Anda hanya boleh meminta akses ke cakupan yang diperlukan untuk mengakses Google API saat pengguna melakukan tindakan yang memerlukan akses ke API tertentu. Misalnya, Anda harus meminta izin untuk mengakses Drive pengguna setiap kali pengguna mengetuk tombol "Simpan ke Drive".
Dengan memisahkan otorisasi dari autentikasi, Anda dapat menghindari pengguna baru yang merasa kewalahan, atau pengguna yang bingung mengapa mereka diminta izin tertentu.
Untuk autentikasi, sebaiknya gunakan Credential Manager API. Untuk mengizinkan tindakan yang memerlukan akses ke data pengguna yang disimpan oleh Google, sebaiknya gunakan AuthorizationClient.
Menyiapkan project
- Buka project Anda di , atau buat project jika Anda belum memilikinya.
- Di ,
pastikan semua informasi sudah lengkap dan akurat.
- Pastikan aplikasi Anda memiliki Nama Aplikasi, Logo Aplikasi, dan Halaman Beranda Aplikasi yang ditetapkan dengan benar. Nilai ini akan ditampilkan kepada pengguna di layar izin Login dengan Google saat mendaftar dan di layar Aplikasi & layanan pihak ketiga.
- Pastikan Anda telah menentukan URL kebijakan privasi dan persyaratan layanan aplikasi Anda.
- Di , buat ID klien Android untuk aplikasi Anda jika Anda belum memilikinya. Anda harus menentukan nama paket dan tanda tangan SHA-1 aplikasi Anda.
- Di , buat ID klien "Aplikasi web" baru jika Anda belum melakukannya. Anda dapat mengabaikan kolom "Authorized JavaScript Origins" dan "Authorized redirect URIs" untuk saat ini. ID klien ini akan digunakan untuk mengidentifikasi server backend Anda saat berkomunikasi dengan layanan autentikasi Google.
Mendeklarasikan dependensi
Dalam file build.gradle modul Anda, deklarasikan dependensi menggunakan versi terbaru library Google Identity Services.
dependencies {
// ... other dependencies
implementation "com.google.android.gms:play-services-auth:21.4.0"
}
Meminta izin yang diperlukan oleh tindakan pengguna
Setiap kali pengguna melakukan tindakan yang memerlukan cakupan tambahan, panggil
AuthorizationClient.authorize()
. Misalnya, jika pengguna melakukan tindakan
yang memerlukan akses ke penyimpanan aplikasi Drive-nya, lakukan hal berikut:
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));
Saat menentukan ActivityResultLauncher
, tangani respons seperti yang ditunjukkan dalam cuplikan
berikut, dengan asumsi bahwa hal ini dilakukan dalam fragmen. Kode ini memeriksa
bahwa izin yang diperlukan telah berhasil diberikan, lalu melakukan
tindakan pengguna.
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
}
});
}
Jika Anda mengakses Google API di sisi server, panggil metode
getServerAuthCode()
dari AuthorizationResult
untuk mendapatkan kode
otorisasi yang Anda kirim ke backend untuk ditukar dengan token akses dan
token refresh. Untuk mempelajari lebih lanjut, lihat artikel
Mempertahankan akses berkelanjutan ke data pengguna.
Mencabut izin ke data atau resource pengguna
Untuk mencabut akses yang diberikan sebelumnya, panggil
AuthorizationClient.revokeAccess()
. Misalnya, jika pengguna menghapus
akunnya dari aplikasi Anda, dan aplikasi Anda sebelumnya diberi akses ke
DriveScopes.DRIVE_FILE
, gunakan kode berikut untuk mencabut akses:
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));
Menghapus cache token
Token akses OAuth di-cache secara lokal setelah diterima dari server, sehingga mempercepat akses dan mengurangi panggilan jaringan. Token ini otomatis dihapus dari
cache saat masa berlakunya berakhir, tetapi juga dapat menjadi tidak valid karena alasan lain.
Jika Anda menerima IllegalStateException
saat menggunakan token, hapus cache lokal untuk memastikan bahwa permintaan otorisasi berikutnya untuk token akses akan dikirim ke server OAuth. Cuplikan berikut menghapus invalidAccessToken
dari
cache lokal:
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));
Mendapatkan informasi pengguna selama otorisasi
Respons otorisasi tidak berisi informasi apa pun tentang akun pengguna yang digunakan; respons hanya berisi token untuk cakupan yang diminta. Misalnya, respons untuk mendapatkan token akses guna mengakses Google Drive pengguna tidak mengungkapkan identitas akun yang dipilih oleh pengguna meskipun dapat digunakan untuk mengakses file di drive pengguna. Untuk mendapatkan informasi seperti nama atau email pengguna, Anda memiliki opsi berikut:
Login pengguna dengan Akun Google mereka menggunakan Credential Manager API sebelum meminta otorisasi. Respons autentikasi dari Pengelola Kredensial mencakup informasi pengguna seperti alamat email dan juga menetapkan akun default aplikasi ke akun yang dipilih; jika diperlukan, Anda dapat melacak akun ini di aplikasi Anda. Permintaan otorisasi berikutnya menggunakan akun sebagai default dan melewati langkah pemilihan akun dalam alur otorisasi. Untuk menggunakan akun lain untuk otorisasi, lihat Otorisasi dari akun non-default.
Dalam permintaan otorisasi Anda, selain cakupan yang Anda inginkan (misalnya,
Drive scope
), minta cakupanuserinfo
,profile
, danopenid
. Setelah token akses ditampilkan, dapatkan info pengguna dengan membuat permintaan HTTPGET
ke endpoint info pengguna OAuth (https://www.googleapis.com/oauth2/v3/userinfo) menggunakan library HTTP pilihan Anda dan sertakan token akses yang telah Anda terima di header, yang setara dengan perintahcurl
berikut:curl -X GET \ "https://www.googleapis.com/oauth2/v1/userinfo?alt=json" \ -H "Authorization: Bearer $TOKEN"
Responsnya adalah
UserInfo
, yang dibatasi pada cakupan yang diminta, diformat dalam JSON.
Otorisasi dari akun non-default
Jika Anda menggunakan Pengelola Kredensial untuk mengautentikasi, dan menjalankan
AuthorizationClient.authorize()
, akun default aplikasi Anda akan disetel ke
akun yang dipilih oleh pengguna Anda. Artinya, setiap panggilan berikutnya untuk
otorisasi akan menggunakan akun default ini. Untuk memaksa menampilkan pemilih akun,
buat pengguna logout dari aplikasi menggunakan clearCredentialState()
API dari
Credential Manager.
Mempertahankan akses berkelanjutan ke data pengguna
Jika Anda perlu mengakses data pengguna dari aplikasi, panggil
AuthorizationClient.authorize()
satu kali; pada sesi berikutnya, dan selama
izin yang diberikan tidak dihapus oleh pengguna, panggil metode yang sama untuk
mendapatkan token akses guna mencapai tujuan Anda, tanpa interaksi pengguna. Namun, jika Anda perlu mengakses data pengguna dalam mode offline, dari server backend, Anda harus meminta jenis token lain yang disebut "token refresh".
Token akses sengaja didesain agar berumur pendek dan memiliki masa aktif selama satu jam. Jika token akses dicegat atau disusupi, jendela validitasnya yang terbatas akan meminimalkan potensi penyalahgunaan. Setelah masa berlakunya berakhir, token menjadi tidak valid, dan setiap upaya untuk menggunakannya akan ditolak oleh server resource. Karena token akses memiliki masa berlaku singkat, server menggunakan token refresh untuk mempertahankan akses berkelanjutan ke data pengguna. Token refresh adalah token dengan masa berlaku yang lama yang digunakan oleh klien untuk meminta token akses yang masa berlakunya singkat dari server otorisasi, saat token akses lama telah berakhir, tanpa interaksi pengguna.
Untuk mendapatkan token refresh, Anda harus mendapatkan kode autentikasi (atau kode otorisasi) terlebih dahulu selama langkah otorisasi di aplikasi Anda dengan meminta "akses offline", lalu menukar kode autentikasi dengan token refresh di server Anda. Penting untuk menyimpan token refresh yang memiliki masa aktif yang lama dengan aman di server Anda karena token tersebut dapat digunakan berulang kali untuk mendapatkan token akses baru. Oleh karena itu, sangat tidak disarankan untuk menyimpan token refresh di perangkat karena masalah keamanan. Sebagai gantinya, kode tersebut harus disimpan di server backend aplikasi tempat penukaran token akses dilakukan.
Setelah kode autentikasi dikirim ke server backend aplikasi Anda, Anda dapat menukarnya dengan token akses jangka pendek di server dan token refresh jangka panjang dengan mengikuti langkah-langkah dalam panduan otorisasi akun. Pertukaran ini hanya boleh terjadi di backend aplikasi Anda.
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"));
Cuplikan berikut mengasumsikan bahwa otorisasi dimulai dari fragmen.
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
}
});
}