Ringkasan
Dengan izin terperinci, konsumen mendapatkan kontrol yang lebih mendetail atas data akun yang mereka pilih untuk dibagikan ke setiap aplikasi. Izin ini menguntungkan pengguna dan developer dengan memberikan kontrol, transparansi, dan keamanan yang lebih besar. Panduan ini akan membantu Anda memahami perubahan dan langkah yang diperlukan agar berhasil mengupdate aplikasi untuk menangani izin terperinci.
Apa yang dimaksud dengan izin terperinci?
Bayangkan Anda mengembangkan aplikasi produktivitas yang meminta cakupan email dan kalender. Pengguna Anda mungkin ingin menggunakan aplikasi Anda hanya untuk Google Kalender, bukan Gmail. Dengan izin OAuth terperinci, pengguna dapat memilih untuk hanya memberikan izin Google Kalender, tetapi tidak untuk Gmail. Dengan mengizinkan pengguna memberikan akses ke data tertentu, hal ini meminimalkan eksposur data, meningkatkan kepercayaan, dan memberdayakan pengguna dengan kontrol yang mengutamakan privasi atas kehidupan digital mereka. Penting untuk mendesain aplikasi Anda agar dapat menangani skenario tersebut.
Jika lebih dari satu cakupan non-Login diminta
Cakupan Login dan non-Login
Untuk aplikasi yang meminta cakupan Login dan non-Login, pengguna akan melihat halaman izin untuk cakupan Login (email, profile, dan openid) terlebih dahulu. Setelah pengguna mengizinkan untuk membagikan informasi identitas dasar mereka (nama, alamat email, dan foto profil), pengguna akan melihat layar izin terperinci untuk cakupan non-Login. Dalam hal ini, aplikasi harus
memeriksa cakupan apa yang diberikan oleh pengguna dan tidak dapat mengasumsikan bahwa pengguna memberikan semua
cakupan yang diminta. Dalam contoh berikut, aplikasi web meminta ketiga cakupan Login dan cakupan non-Login Google Drive. Setelah pengguna mengizinkan
cakupan Login, pengguna akan melihat layar izin terperinci
untuk izin Google Drive:
Lebih dari satu cakupan non-Login
Layar izin terperinci akan ditampilkan kepada pengguna saat aplikasi meminta lebih dari satu cakupan non-Login. Pengguna dapat memilih izin yang ingin mereka setujui untuk dibagikan ke aplikasi. Berikut adalah contoh layar izin terperinci yang meminta akses ke pesan Gmail dan data Google Kalender pengguna:
Untuk aplikasi yang hanya meminta cakupan Login (email,
profile, dan openid), layar izin terperinci tidak
berlaku. Pengguna menyetujui atau menolak seluruh permintaan login. Dengan kata lain, jika aplikasi hanya meminta cakupan Login (satu, dua, atau ketiganya), layar izin terperinci tidak berlaku.
Untuk aplikasi yang hanya meminta satu cakupan non-Login, layar izin terperinci tidak berlaku. Dengan kata lain, pengguna menyetujui atau menolak seluruh permintaan, dan tidak ada kotak centang di layar izin. Tabel berikut merangkum kapan layar izin terperinci ditampilkan.
| Jumlah cakupan Login | Jumlah cakupan Non-Login | Layar izin terperinci |
|---|---|---|
| 1-3 | 0 | Tidak berlaku |
| 1-3 | 1+ | Berlaku |
| 0 | 1 | Tidak berlaku |
| 0 | 2+ | Berlaku |
Menentukan apakah aplikasi Anda terpengaruh
Lakukan peninjauan menyeluruh terhadap semua bagian dalam aplikasi Anda yang menggunakan endpoint otorisasi Google OAuth 2.0 untuk permintaan izin. Perhatikan aplikasi yang meminta beberapa cakupan karena aplikasi tersebut mengaktifkan layar izin terperinci yang ditampilkan kepada pengguna. Dalam kasus tersebut, pastikan kode Anda dapat menangani kasus saat pengguna hanya mengizinkan beberapa cakupan.
Cara menentukan apakah aplikasi Anda menggunakan beberapa cakupan
Periksa kode aplikasi Anda atau panggilan jaringan keluar untuk menentukan apakah permintaan otorisasi Google OAuth 2.0 yang dibuat aplikasi Anda akan menyebabkan layar izin terperinci ditampilkan.
Memeriksa kode aplikasi Anda
Tinjau bagian kode aplikasi tempat Anda melakukan panggilan ke endpoint otorisasi Google OAuth untuk meminta izin dari pengguna. Jika Anda menggunakan salah satu Library Klien Google API, Anda sering kali dapat menemukan cakupan yang diminta aplikasi Anda dalam langkah-langkah inisialisasi klien. Beberapa contoh ditampilkan di bagian berikut. Anda harus melihat dokumentasi SDK yang digunakan aplikasi Anda untuk menangani Google OAuth 2.0 guna menentukan apakah aplikasi Anda terpengaruh, menggunakan panduan yang ditampilkan dalam contoh berikut sebagai referensi.
Google Identity Services
Cuplikan kode library JavaScript Google Identity Services berikut
menginisialisasi TokenClient dengan beberapa cakupan non-Login.
Layar izin terperinci akan ditampilkan saat aplikasi web
meminta otorisasi dari pengguna.
const client = google.accounts.oauth2.initTokenClient({ client_id: 'YOUR_CLIENT_ID', scope: 'https://www.googleapis.com/auth/calendar.readonly \ https://www.googleapis.com/auth/contacts.readonly', callback: (response) => { ... }, });
Python
Cuplikan kode berikut menggunakan modul google-auth-oauthlib.flow untuk
membuat permintaan otorisasi; Parameter scope menyertakan dua
cakupan non-Login. Layar izin terperinci akan
ditampilkan saat aplikasi web meminta otorisasi dari pengguna.
import google.oauth2.credentials import google_auth_oauthlib.flow # Use the client_secret.json file to identify the application requesting # authorization. The client ID (from that file) and access scopes are # required. flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( 'client_secret.json', scopes=[ 'https://www.googleapis.com/auth/calendar.readonly', 'https://www.googleapis.com/auth/contacts.readonly' ])
Node.js
Cuplikan kode berikut membuat objek google.auth.OAuth2, yang
menentukan parameter dalam permintaan otorisasi yang parameter scope-nya
mencakup dua cakupan non-Login. Layar izin terperinci
akan ditampilkan saat aplikasi web meminta otorisasi dari pengguna.
const {google} = require('googleapis'); /** * To use OAuth2 authentication, we need access to a CLIENT_ID, * CLIENT_SECRET, AND REDIRECT_URI from the client_secret.json file. To get * these credentials for your application, visit * https://console.cloud.google.com/apis/credentials. */ const oauth2Client = new google.auth.OAuth2( YOUR_CLIENT_ID, YOUR_CLIENT_SECRET, YOUR_REDIRECT_URL ); // Access scopes for read-only Calendar and Contacts. const scopes = [ 'https://www.googleapis.com/auth/calendar.readonly', 'https://www.googleapis.com/auth/contacts.readonly'] ]; // Generate a url that asks permissions const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined earlier. * Alternatively, if only one scope is needed, you can pass a scope URL * as a string */ scope: scopes, // Enable incremental authorization. Recommended as best practices. include_granted_scopes: true });
Memeriksa panggilan jaringan keluar
Metode untuk memeriksa panggilan jaringan akan bervariasi bergantung pada jenis aplikasi Anda.
- Aplikasi web - memeriksa aktivitas jaringan di Chrome
- Android - memeriksa traffic jaringan dengan Network Inspector
- Aplikasi Chrome
- Buka halaman ekstensi Chrome
- Centang kotak Developer mode di pojok kanan atas halaman ekstensi
- Pilih ekstensi yang ingin Anda pantau
- Klik link background page di bagian Inspect views di halaman ekstensi
- Dialog Developer Tools akan terbuka dan Anda dapat memantau traffic jaringan di tab Network
- iOS - Menganalisis traffic HTTP dengan Instruments
- Aplikasi desktop - gunakan alat perekam jaringan yang tersedia untuk sistem operasi yang digunakan untuk mengembangkan aplikasi
Saat memeriksa panggilan jaringan, cari permintaan yang dikirim ke endpoint otorisasi Google OAuth dan periksa parameter scope.
Nilai ini menyebabkan layar izin terperinci ditampilkan.
Parameter
scopeberisi cakupan Login dan cakupan non-Login.Contoh permintaan berikut berisi ketiga cakupan Login dan satu cakupan non-Login untuk melihat metadata file Google Drive pengguna:
https://accounts.google.com/o/oauth2/v2/auth? access_type=offline& scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile%20openid%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.metadata.readonly& include_granted_scopes=true& response_type=code& redirect_uri=YOUR_REDIRECT_URL& client_id=YOUR_CLIENT_ID
Parameter
scopeberisi lebih dari satu cakupan non-Login.Contoh permintaan berikut berisi dua cakupan non-Login untuk melihat metadata Google Drive pengguna dan mengelola file Google Drive tertentu:
https://accounts.google.com/o/oauth2/v2/auth? access_type=offline& scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.metadata.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.file& include_granted_scopes=true& response_type=code& redirect_uri=YOUR_REDIRECT_URL& client_id=YOUR_CLIENT_ID
Praktik terbaik untuk menangani izin terperinci
Jika Anda menentukan bahwa aplikasi Anda perlu diupdate untuk menangani izin terperinci, Anda harus melakukan update yang diperlukan pada kode Anda untuk menangani izin untuk beberapa cakupan dengan benar. Semua aplikasi harus mematuhi praktik terbaik berikut:
- Tinjau Layanan Google API: Kebijakan Data Pengguna dan pastikan Anda mematuhinya.
- Minta cakupan tertentu yang diperlukan untuk suatu tugas. Anda harus mematuhi kebijakan Google OAuth 2.0 bahwa Anda hanya meminta cakupan yang Anda perlukan. Sebaiknya hindari meminta beberapa cakupan saat login, kecuali jika hal ini penting untuk fungsi inti aplikasi Anda. Menggabungkan beberapa cakupan, terutama untuk pengguna baru yang belum memahami fitur aplikasi Anda, dapat menyulitkan mereka memahami perlunya izin ini. Hal ini dapat menimbulkan kekhawatiran dan mencegah pengguna berinteraksi lebih lanjut dengan aplikasi Anda.
- Berikan justifikasi kepada pengguna sebelum meminta permintaan otorisasi. Jelaskan dengan jelas alasan aplikasi Anda memerlukan izin yang diminta, apa yang akan Anda lakukan dengan data pengguna, dan manfaat yang akan diperoleh pengguna jika menyetujui permintaan tersebut. Riset kami menunjukkan bahwa penjelasan ini meningkatkan kepercayaan dan engagement pengguna.
- Gunakan otorisasi inkremental setiap kali aplikasi Anda meminta cakupan untuk menghindari pengelolaan beberapa token akses.
- Periksa cakupan yang diberikan pengguna. Saat meminta beberapa cakupan sekaligus, pengguna mungkin tidak memberikan semua cakupan yang diminta aplikasi Anda. Aplikasi Anda harus selalu memeriksa cakupan yang diberikan oleh pengguna dan menangani penolakan cakupan dengan menonaktifkan fitur yang relevan. Ikuti kebijakan Google OAuth 2.0 tentang menangani izin untuk beberapa cakupan dan hanya minta izin pengguna lagi setelah mereka dengan jelas menunjukkan niat untuk menggunakan fitur tertentu yang memerlukan cakupan.
Mengupdate aplikasi untuk menangani izin terperinci
Aplikasi Android
Anda harus membaca dokumentasi SDK yang Anda gunakan untuk berinteraksi dengan Google OAuth 2.0 dan mengupdate aplikasi Anda untuk menangani izin terperinci berdasarkan praktik terbaik.
Jika Anda menggunakan SDK auth.api.signin dari Layanan Play untuk
berinteraksi dengan Google OAuth 2.0, Anda dapat menggunakan
fungsi requestPermissions untuk meminta set
cakupan terkecil yang diperlukan, dan
fungsi hasPermissions untuk
memeriksa cakupan yang diberikan pengguna saat meminta
izin terperinci.
Aplikasi ekstensi Chrome
Anda harus menggunakan Chrome Identity API untuk menggunakan Google OAuth 2.0 berdasarkan praktik terbaik.
Contoh berikut menunjukkan cara menangani izin terperinci dengan benar.
manifest.json
Contoh file manifes mendeklarasikan dua cakupan non-Login untuk aplikasi ekstensi Chrome.
{
"name": "Example Chrome extension application",
...
"permissions": [
"identity"
],
"oauth2" : {
"client_id": "YOUR_CLIENT_ID",
"scopes":[
"https://www.googleapis.com/auth/calendar.readonly",
"https://www.googleapis.com/auth/contacts.readonly"
]
}
}Pendekatan yang Salah
Semua atau tidak sama sekali
Pengguna mengklik tombol untuk memulai proses otorisasi. Cuplikan kode
mengasumsikan bahwa pengguna akan melihat layar izin "semua atau tidak sama sekali"
untuk dua cakupan yang ditentukan dalam file manifest.json. Aplikasi ini mengabaikan pemeriksaan
cakupan yang diberikan pengguna.
oauth.js
...
document.querySelector('button').addEventListener('click', function () {
chrome.identity.getAuthToken({ interactive: true },
function (token) {
if (token === undefined) {
// User didn't authorize both scopes.
// Updating the UX and application accordingly
...
} else {
// User authorized both or one of the scopes.
// It neglects to check which scopes users granted and assumes
// users granted all scopes.
// Calling the APIs, etc.
...
}
});
});
Pendekatan yang Benar
Cakupan terkecil
Pilih kumpulan cakupan terkecil yang diperlukan
Aplikasi hanya boleh meminta kumpulan cakupan terkecil yang diperlukan. Sebaiknya aplikasi Anda meminta satu cakupan pada satu waktu saat diperlukan untuk menyelesaikan tugas.
Dalam contoh ini, diasumsikan bahwa kedua cakupan yang dideklarasikan dalam file manifest.json adalah kumpulan cakupan terkecil yang diperlukan. File oauth.js
menggunakan Chrome Identity API untuk memulai proses otorisasi dengan
Google. Anda harus memilih ikut serta untuk mengaktifkan izin
terperinci, sehingga pengguna memiliki kontrol yang lebih besar dalam memberikan
izin ke aplikasi Anda. Aplikasi Anda harus menangani respons dari pengguna dengan benar dengan memeriksa cakupan yang diizinkan pengguna.
oauth.js
...
document.querySelector('button').addEventListener('click', function () {
chrome.identity.getAuthToken(
{ interactive: true, enableGranularPermissions: true },
function (token, grantedScopes) {
if (token === undefined) {
// User didn't authorize any scope.
// Updating the UX and application accordingly
...
} else {
// User authorized the request. Now, check which scopes were
// granted.
if (grantedScopes.includes(
'https://www.googleapis.com/auth/calendar.readonly'))
{
// User authorized Calendar read permission.
// Calling the APIs, etc.
...
}
else
{
// User didn't authorize Calendar read permission.
// Update UX and application accordingly
...
}
if (grantedScopes.includes(
'https://www.googleapis.com/auth/contacts.readonly'))
{
// User authorized Contacts read permission.
// Calling the APIs, etc.
...
}
else
{
// User didn't authorize Contacts read permission.
// Update UX and application accordingly
...
}
}
});
});
Aplikasi iOS, iPadOS, dan macOS
Anda harus membaca dokumentasi SDK yang Anda gunakan untuk berinteraksi dengan Google OAuth 2.0 dan mengupdate aplikasi Anda untuk menangani izin terperinci berdasarkan praktik terbaik.
Jika Anda menggunakan library Login dengan Google untuk iOS dan macOS untuk berinteraksi dengan Google OAuth 2.0, Anda harus meninjau dokumentasi tentang penanganan izin terperinci.
Aplikasi web
Anda harus membaca dokumentasi SDK yang Anda gunakan untuk berinteraksi dengan Google OAuth 2.0 dan mengupdate aplikasi Anda untuk menangani izin terperinci berdasarkan praktik terbaik.
Akses sisi server (offline)
Mode akses sisi server (offline) mengharuskan Anda melakukan hal berikut:
- Siapkan server dan tentukan endpoint yang dapat diakses secara publik untuk menerima kode otorisasi.
- Konfigurasikan URI pengalihan endpoint publik Anda di halaman Klien di Konsol Google Cloud.
Cuplikan kode berikut menunjukkan contoh NodeJS yang meminta dua cakupan non-Login. Pengguna akan melihat layar izin terperinci.
Pendekatan yang Salah
Semua atau tidak sama sekali
Pengguna dialihkan ke URL otorisasi. Cuplikan kode mengasumsikan bahwa pengguna
akan melihat layar izin "semua atau tidak sama sekali" untuk dua cakupan
yang ditentukan dalam array scopes. Aplikasi ini mengabaikan pemeriksaan cakupan yang diberikan pengguna.
main.js
...
const oauth2Client = new google.auth.OAuth2(
YOUR_CLIENT_ID,
YOUR_CLIENT_SECRET,
YOUR_REDIRECT_URL
);
// Access scopes for two non-Sign-In scopes - Google Calendar and Contacts
const scopes = [
'https://www.googleapis.com/auth/contacts.readonly',
'https://www.googleapis.com/auth/calendar.readonly'
];
// Generate a url that asks permissions for the Google Calendar and Contacts
// scopes
const authorizationUrl = oauth2Client.generateAuthUrl({
// 'online' (default) or 'offline' (gets refresh_token)
access_type: 'offline',
// Pass in the scopes array defined earlier
scope: scopes,
// Enable incremental authorization. Recommended as best practices.
include_granted_scopes: true
});
async function main() {
const server = http.createServer(async function (req, res) {
// Example on redirecting user to the Google authorization server.
if (req.url == '/') {
res.writeHead(301, { "Location": authorizationUrl });
}
// Receive the callback from the Google authorization server.
if (req.url.startsWith('/oauth2callback')) {
// Handle the Google authorization server response
let q = url.parse(req.url, true).query;
if (q.error) {
// User didn't authorize both scopes.
// Updating the UX and application accordingly
...
} else {
// User authorized both or one of the scopes.
// It neglects to check which scopes users granted and assumes users
// granted all scopes.
// Get access and refresh tokens (if access_type is offline)
let { tokens } = await oauth2Client.getToken(q.code);
// Calling the APIs, etc.
...
}
}
res.end();
}).listen(80);
}
Pendekatan yang Benar
Cakupan terkecil
Pilih kumpulan cakupan terkecil yang diperlukan
Aplikasi hanya boleh meminta kumpulan cakupan terkecil yang diperlukan. Sebaiknya aplikasi Anda meminta satu cakupan pada satu waktu saat diperlukan untuk menyelesaikan tugas. Setiap kali aplikasi Anda meminta cakupan, aplikasi tersebut harus menggunakan otorisasi inkremental untuk menghindari keharusan mengelola beberapa token akses.
Jika aplikasi Anda harus meminta beberapa cakupan non-Login, Anda harus selalu menggunakan otorisasi inkremental saat meminta dan memeriksa cakupan yang diberikan pengguna.
Dalam contoh ini, diasumsikan bahwa kedua cakupan yang dinyatakan diperlukan agar aplikasi berfungsi dengan baik. Anda harus memilih ikut serta untuk mengaktifkan izin terperinci, sehingga pengguna memiliki kontrol yang lebih besar dalam memberikan izin ke aplikasi Anda. Aplikasi Anda harus menangani respons dari pengguna dengan benar dengan memeriksa cakupan yang telah mereka izinkan.
main.js
...
const oauth2Client = new google.auth.OAuth2(
YOUR_CLIENT_ID,
YOUR_CLIENT_SECRET,
YOUR_REDIRECT_URL
);
// Access scopes for two non-Sign-In scopes - Google Calendar and Contacts
const scopes = [
'https://www.googleapis.com/auth/contacts.readonly',
'https://www.googleapis.com/auth/calendar.readonly'
];
// Generate a url that asks permissions for the Google Calendar and Contacts
// scopes
const authorizationUrl = oauth2Client.generateAuthUrl({
// 'online' (default) or 'offline' (gets refresh_token)
access_type: 'offline',
// Pass in the scopes array defined earlier
scope: scopes,
// Enable incremental authorization. Recommended as best practices.
include_granted_scopes: true,
// Set to true to enable more granular permissions for Google OAuth 2.0
// client IDs created before 2019. No effect for newer Google OAuth 2.0
// client IDs, since more granular permissions is always enabled for them.
enable_granular_consent: true
});
async function main() {
const server = http.createServer(async function (req, res) {
// Redirect users to the Google authorization server.
if (req.url == '/') {
res.writeHead(301, { "Location": authorizationUrl });
}
// Receive the callback from the Google authorization server.
if (req.url.startsWith('/oauth2callback')) {
// Handle the Google authorization server response
let q = url.parse(req.url, true).query;
if (q.error) {
// User didn't authorize both scopes.
// Updating the UX and application accordingly
...
} else {
// Get access and refresh tokens (if access_type is offline)
let { tokens } = await oauth2Client.getToken(q.code);
oauth2Client.setCredentials(tokens);
// User authorized the request. Now, check which scopes were
// granted.
if (tokens.scope.includes(
'https://www.googleapis.com/auth/calendar.readonly'))
{
// User authorized Calendar read permission.
// Calling the APIs, etc.
...
}
else
{
// User didn't authorize Calendar read permission.
// Calling the APIs, etc.
...
}
// Check which scopes user granted the permission to application
if (tokens.scope.includes(
'https://www.googleapis.com/auth/contacts.readonly'))
{
// User authorized Contacts read permission.
// Calling the APIs, etc.
...
}
else
{
// User didn't authorize Contacts read permission.
// Update UX and application accordingly
...
}
}
}
res.end();
}).listen(80);
}
Tinjau panduan aplikasi web sisi server tentang cara mengakses Google API dari aplikasi berbasis server.
Akses khusus sisi klien
- Untuk aplikasi yang menggunakan library JavaScript Google Identity Services untuk berinteraksi dengan Google OAuth 2.0, Anda harus meninjau dokumentasi ini tentang cara menangani izin terperinci.
- Untuk aplikasi yang langsung melakukan panggilan menggunakan JavaScript ke endpoint otorisasi Google OAuth 2.0, Anda harus meninjau dokumentasi ini tentang penanganan izin terperinci.
Menguji aplikasi yang telah diupdate dalam menangani izin terperinci
- Uraikan semua kasus saat pengguna dapat merespons permintaan izin dan perilaku yang diharapkan dari aplikasi Anda. Misalnya, jika pengguna hanya mengizinkan dua dari tiga cakupan yang diminta, aplikasi Anda harus berperilaku sesuai dengan hal tersebut.
Uji aplikasi Anda dengan izin terperinci diaktifkan. Ada dua cara untuk mengaktifkan izin terperinci:
- Periksa layar izin OAuth 2.0 aplikasi Anda untuk melihat apakah izin terperinci sudah diaktifkan untuk aplikasi Anda. Anda juga dapat membuat client ID OAuth 2.0 Google Web, Android, atau iOS baru melalui Konsol Google Cloud untuk tujuan pengujian karena izin terperinci selalu diaktifkan untuk client ID tersebut.
Tetapkan parameter
enable_granular_consentketruesaat memanggil endpoint otorisasi Google OAuth. Beberapa SDK memiliki dukungan eksplisit untuk parameter ini. Untuk platform lainnya, periksa dokumentasi untuk mengetahui cara menambahkan parameter ini dan nilainya secara manual. Jika penerapan Anda tidak mendukung penambahan parameter, Anda dapat membuat client ID Google OAuth 2.0 Web, Android, atau iOS baru melalui Konsol Google Cloud hanya untuk tujuan pengujian seperti yang dinyatakan dalam poin sebelumnya.
Saat menguji aplikasi yang telah diupdate, gunakan Akun Google pribadi (@gmail.com), bukan akun Workspace. Hal ini karena aplikasi Workspace Enterprise dengan delegasi wewenang di seluruh domain atau ditandai sebagai Tepercaya tidak terpengaruh oleh perubahan pada izin terperinci saat ini. Oleh karena itu, pengujian dengan akun Workspace dari organisasi Anda mungkin tidak menampilkan layar izin terperinci yang baru sebagaimana mestinya.