Dokumen ini menjelaskan cara aplikasi server web menggunakan Library Klien Google API atau endpoint Google OAuth 2.0 untuk menerapkan otorisasi OAuth 2.0 guna mengakses Google API.
OAuth 2.0 memungkinkan pengguna berbagi data tertentu dengan aplikasi sekaligus menjaga kerahasiaan nama pengguna, sandi, dan informasi lainnya. Misalnya, aplikasi dapat menggunakan OAuth 2.0 untuk mendapatkan izin dari pengguna untuk menyimpan file di Google Drive mereka.
Alur OAuth 2.0 ini khusus untuk otorisasi pengguna. Alur ini dirancang untuk aplikasi yang dapat menyimpan informasi rahasia dan mempertahankan status. Aplikasi server web yang diotorisasi dengan benar dapat mengakses API saat pengguna berinteraksi dengan aplikasi atau setelah pengguna keluar dari aplikasi.
Aplikasi server web juga sering menggunakan akun layanan untuk mengizinkan permintaan API, terutama saat memanggil Cloud API untuk mengakses data berbasis project, bukan data khusus pengguna. Aplikasi server web dapat menggunakan akun layanan bersama dengan otorisasi pengguna.
Library klien
Contoh khusus bahasa pada halaman ini menggunakan Library Klien Google API untuk menerapkan otorisasi OAuth 2.0. Untuk menjalankan contoh kode, Anda terlebih dulu harus menginstal library klien untuk bahasa Anda.
Saat Anda menggunakan Library Klien Google API untuk menangani alur OAuth 2.0 aplikasi, library klien melakukan banyak tindakan yang seharusnya ditangani sendiri oleh aplikasi. Misalnya, hal ini menentukan kapan aplikasi dapat menggunakan atau memperbarui token akses yang disimpan serta kapan aplikasi harus mendapatkan kembali izin. Library klien juga menghasilkan URL pengalihan yang benar dan membantu mengimplementasikan pengendali pengalihan yang menukar kode otorisasi dengan token akses.
Library Klien Google API untuk aplikasi sisi server tersedia untuk bahasa berikut:
Prasyarat
Mengaktifkan API untuk project Anda
Setiap aplikasi yang memanggil Google API harus mengaktifkan API tersebut di Konsol API.
Untuk mengaktifkan API untuk project Anda:
- Buka Library API di Konsol API Google.
- Jika diminta, pilih project, atau buat project baru.
- Library API menampilkan daftar semua API yang tersedia, yang dikelompokkan berdasarkan kelompok produk dan popularitas. Jika API yang ingin Anda aktifkan tidak terlihat dalam daftar, gunakan penelusuran untuk menemukannya, atau klik View All di kelompok produk yang sesuai.
- Pilih API yang ingin Anda aktifkan, lalu klik tombol Aktifkan.
- Jika diminta, aktifkan penagihan.
- Jika diminta, baca dan setujui Persyaratan Layanan API.
Membuat kredensial otorisasi
Setiap aplikasi yang menggunakan OAuth 2.0 untuk mengakses Google API harus memiliki kredensial otorisasi yang mengidentifikasi aplikasi ke server OAuth 2.0 Google. Langkah-langkah berikut menjelaskan cara membuat kredensial untuk project Anda. Kemudian, aplikasi Anda dapat menggunakan kredensial tersebut untuk mengakses API yang telah Anda aktifkan untuk project tersebut.
- Buka halaman Klien.
- Klik Create Client.
- Pilih jenis aplikasi aplikasi web.
- Isi formulir, lalu klik Create. Aplikasi yang menggunakan bahasa dan framework
seperti PHP, Java, Python, Ruby, dan .NET harus menentukan URI pengalihan yang diizinkan. URI pengalihan adalah endpoint yang dapat menerima respons dari server OAuth 2.0. Endpoint ini harus mematuhi aturan validasi Google.
Untuk pengujian, Anda dapat menentukan URI yang merujuk ke komputer lokal, seperti
http://localhost:8080. Semua contoh dalam dokumen ini menggunakanhttp://localhost:8080sebagai URI pengalihan.Sebaiknya Anda mendesain endpoint autentikasi aplikasi sedemikian rupa sehingga aplikasi Anda tidak mengekspos kode otorisasi ke resource lain di halaman.
Setelah membuat kredensial, download file client_secret.json dari Konsol API. Simpan file dengan aman di lokasi yang hanya dapat diakses oleh aplikasi Anda.
Mengidentifikasi cakupan akses
Cakupan memungkinkan aplikasi Anda hanya meminta akses ke resource yang diperlukan sekaligus memungkinkan pengguna mengontrol jumlah akses yang mereka berikan ke aplikasi Anda. Oleh karena itu, mungkin ada hubungan terbalik antara jumlah cakupan yang diminta dan kemungkinan memperoleh izin pengguna.
Sebelum mulai menerapkan otorisasi OAuth 2.0, sebaiknya identifikasi cakupan yang memerlukan izin akses dari aplikasi Anda.
Sebaiknya aplikasi Anda meminta akses ke cakupan otorisasi melalui proses otorisasi inkremental, yang mana aplikasi Anda meminta akses ke data pengguna sesuai konteks. Praktik terbaik ini membantu pengguna lebih mudah memahami alasan aplikasi Anda memerlukan akses yang diminta.
Dokumen Cakupan OAuth 2.0 API berisi daftar lengkap cakupan yang mungkin Anda gunakan untuk mengakses Google API.
Persyaratan khusus bahasa
Untuk menjalankan contoh kode apa pun dalam dokumen ini, Anda memerlukan Akun Google, akses ke Internet, dan browser web. Jika Anda menggunakan salah satu library klien API, lihat juga persyaratan khusus bahasa di bagian berikut.
PHP
Untuk menjalankan contoh kode PHP dalam dokumen ini, Anda memerlukan:
- PHP 8.0 atau yang lebih baru dengan antarmuka command line (CLI) dan ekstensi JSON yang diinstal.
- Alat pengelolaan dependensi Composer.
-
Library Klien Google API untuk PHP:
composer require google/apiclient:^2.15.0
Lihat Library Klien Google API untuk PHP guna mengetahui informasi selengkapnya.
Python
Untuk menjalankan contoh kode Python dalam dokumen ini, Anda memerlukan:
- Python 3.7 atau yang lebih baru
- Alat pengelolaan paket pip.
- Rilis Library Klien Google API untuk Python 2.0:
pip install --upgrade google-api-python-client
google-auth,google-auth-oauthlib, dangoogle-auth-httplib2untuk otorisasi pengguna.pip install --upgrade google-auth google-auth-oauthlib google-auth-httplib2
- Framework aplikasi web Flask Python.
pip install --upgrade flask
- Library HTTP
requests.pip install --upgrade requests
Tinjau catatan rilis library klien Python Google API jika Anda tidak dapat mengupgrade Python dan panduan migrasi terkait.
Ruby
Untuk menjalankan contoh kode Ruby dalam dokumen ini, Anda memerlukan:
- Ruby 2.6 atau yang lebih baru
-
Library Google Auth untuk Ruby:
gem install googleauth
-
Library klien untuk Google API Drive dan Kalender:
gem install google-apis-drive_v3 google-apis-calendar_v3
-
Framework aplikasi web Sinatra Ruby.
gem install sinatra
Node.js
Untuk menjalankan contoh kode Node.js dalam dokumen ini, Anda memerlukan:
- LTS pemeliharaan, LTS aktif, atau rilis Node.js saat ini.
-
Klien Node.js Google API:
npm install googleapis crypto express express-session
HTTP/REST
Anda tidak perlu menginstal library apa pun untuk dapat langsung memanggil endpoint OAuth 2.0.
Mendapatkan token akses OAuth 2.0
Langkah-langkah berikut menunjukkan cara aplikasi Anda berinteraksi dengan server OAuth 2.0 Google untuk mendapatkan izin pengguna dalam melakukan permintaan API atas nama pengguna. Aplikasi Anda harus memiliki izin tersebut sebelum dapat menjalankan permintaan Google API yang memerlukan otorisasi pengguna.
Daftar berikut merangkum langkah-langkah ini secara singkat:
- Aplikasi Anda mengidentifikasi izin yang diperlukan.
- Aplikasi Anda mengalihkan pengguna ke Google beserta daftar izin yang diminta.
- Pengguna memutuskan apakah akan memberikan izin ke aplikasi Anda.
- Aplikasi Anda akan mengetahui keputusan pengguna.
- Jika pengguna memberikan izin yang diminta, aplikasi Anda akan mengambil token yang diperlukan untuk membuat permintaan API atas nama pengguna.
Langkah 1: Tetapkan parameter otorisasi
Langkah pertama Anda adalah membuat permintaan otorisasi. Permintaan tersebut menetapkan parameter yang mengidentifikasi aplikasi Anda dan menentukan izin yang akan diminta untuk diberikan pengguna kepada aplikasi Anda.
- Jika Anda menggunakan library klien Google untuk autentikasi dan otorisasi OAuth 2.0, Anda membuat dan mengonfigurasi objek yang menentukan parameter ini.
- Jika Anda memanggil endpoint Google OAuth 2.0 secara langsung, Anda akan membuat URL dan menetapkan parameter pada URL tersebut.
Tab berikut menentukan parameter otorisasi yang didukung untuk aplikasi server web. Contoh khusus bahasa juga menunjukkan cara menggunakan library klien atau library otorisasi untuk mengonfigurasi objek yang menetapkan parameter tersebut:
PHP
Cuplikan kode berikut membuat objek Google\Client(), yang menentukan
parameter dalam permintaan otorisasi.
Objek tersebut menggunakan informasi dari file client_secret.json untuk mengidentifikasi
aplikasi Anda. (Lihat membuat kredensial otorisasi untuk mengetahui informasi selengkapnya tentang file tersebut.) Objek ini juga mengidentifikasi cakupan yang diminta izin aksesnya oleh aplikasi Anda dan URL ke endpoint autentikasi aplikasi Anda, yang akan menangani respons dari server OAuth 2.0 Google. Terakhir, kode menetapkan parameter access_type dan
include_granted_scopes opsional.
Misalnya, kode ini meminta akses offline hanya baca ke metadata Google Drive dan acara Kalender pengguna:
use Google\Client; $client = new Client(); // Required, call the setAuthConfig function to load authorization credentials from // client_secret.json file. $client->setAuthConfig('client_secret.json'); // Required, to set the scope value, call the addScope function $client->addScope([Google\Service\Drive::DRIVE_METADATA_READONLY, Google\Service\Calendar::CALENDAR_READONLY]); // Required, call the setRedirectUri function to specify a valid redirect URI for the // provided client_id $client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php'); // Recommended, offline access will give you both an access and refresh token so that // your app can refresh the access token without user interaction. $client->setAccessType('offline'); // Recommended, call the setState function. Using a state value can increase your assurance that // an incoming connection is the result of an authentication request. $client->setState($sample_passthrough_value); // Optional, if your application knows which user is trying to authenticate, it can use this // parameter to provide a hint to the Google Authentication Server. $client->setLoginHint('hint@example.com'); // Optional, call the setPrompt function to set "consent" will prompt the user for consent $client->setPrompt('consent'); // Optional, call the setIncludeGrantedScopes function with true to enable incremental // authorization $client->setIncludeGrantedScopes(true);
Python
Cuplikan kode berikut menggunakan modul google-auth-oauthlib.flow untuk membuat
permintaan otorisasi.
Kode ini membuat objek Flow, yang mengidentifikasi aplikasi Anda menggunakan
informasi dari file client_secret.json yang Anda download setelah
membuat kredensial otorisasi. Objek tersebut juga mengidentifikasi
cakupan yang izin aksesnya diminta oleh aplikasi Anda dan URL ke endpoint autentikasi
aplikasi Anda, yang akan menangani respons dari server OAuth 2.0 Google. Terakhir, kode
menetapkan parameter access_type dan include_granted_scopes opsional.
Misalnya, kode ini meminta akses offline hanya baca ke metadata Google Drive dan acara Kalender pengguna:
import google.oauth2.credentials import google_auth_oauthlib.flow # Required, call the from_client_secrets_file method to retrieve the client ID from a # client_secret.json file. The client ID (from that file) and access scopes are required. (You can # also use the from_client_config method, which passes the client configuration as it originally # appeared in a client secrets file but doesn't access the file itself.) flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file('client_secret.json', scopes=['https://www.googleapis.com/auth/drive.metadata.readonly', 'https://www.googleapis.com/auth/calendar.readonly']) # Required, indicate where the API server will redirect the user after the user completes # the authorization flow. The redirect URI is required. The value must exactly # match one of the authorized redirect URIs for the OAuth 2.0 client, which you # configured in the API Console. If this value doesn't match an authorized URI, # you will get a 'redirect_uri_mismatch' error. flow.redirect_uri = 'https://www.example.com/oauth2callback' # Generate URL for request to Google's OAuth 2.0 server. # Use kwargs to set optional request parameters. authorization_url, state = flow.authorization_url( # Recommended, enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Optional, enable incremental authorization. Recommended as a best practice. include_granted_scopes='true', # Optional, if your application knows which user is trying to authenticate, it can use this # parameter to provide a hint to the Google Authentication Server. login_hint='hint@example.com', # Optional, set prompt to 'consent' will prompt the user for consent prompt='consent')
Ruby
Gunakan file client_secrets.json yang Anda buat untuk mengonfigurasi objek klien di aplikasi Anda. Saat mengonfigurasi objek klien, Anda menentukan cakupan yang perlu diakses aplikasi, beserta URL ke endpoint autentikasi aplikasi, yang akan menangani respons dari server OAuth 2.0.
Misalnya, kode ini meminta akses offline hanya baca ke metadata Google Drive dan acara Kalender pengguna:
require 'googleauth' require 'googleauth/web_user_authorizer' require 'googleauth/stores/redis_token_store' require 'google/apis/drive_v3' require 'google/apis/calendar_v3' # Required, call the from_file method to retrieve the client ID from a # client_secret.json file. client_id = Google::Auth::ClientId.from_file('/path/to/client_secret.json') # Required, scope value # Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar. scope = ['Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY', 'Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY'] # Required, Authorizers require a storage instance to manage long term persistence of # access and refresh tokens. token_store = Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new) # Required, indicate where the API server will redirect the user after the user completes # the authorization flow. The redirect URI is required. The value must exactly # match one of the authorized redirect URIs for the OAuth 2.0 client, which you # configured in the API Console. If this value doesn't match an authorized URI, # you will get a 'redirect_uri_mismatch' error. callback_uri = '/oauth2callback' # 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. authorizer = Google::Auth::WebUserAuthorizer.new(client_id, scope, token_store, callback_uri)
Aplikasi Anda menggunakan objek klien untuk menjalankan operasi OAuth 2.0, seperti membuat URL permintaan otorisasi dan menerapkan token akses ke permintaan HTTP.
Node.js
Cuplikan kode berikut membuat objek google.auth.OAuth2, yang menentukan
parameter dalam permintaan otorisasi.
Objek tersebut menggunakan informasi dari file client_secret.json untuk mengidentifikasi aplikasi Anda. Untuk meminta izin dari pengguna guna mengambil token akses, Anda mengalihkan mereka ke halaman izin. Untuk membuat URL halaman izin:
const {google} = require('googleapis'); const crypto = require('crypto'); const express = require('express'); const session = require('express-session'); /** * 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 two non-Sign-In scopes: Read-only Drive activity and Google Calendar. const scopes = [ 'https://www.googleapis.com/auth/drive.metadata.readonly', 'https://www.googleapis.com/auth/calendar.readonly' ]; // Generate a secure random state value. const state = crypto.randomBytes(32).toString('hex'); // Store state in the session req.session.state = state; // Generate a url that asks permissions for the Drive activity and Google Calendar scope const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined above. * Alternatively, if only one scope is needed, you can pass a scope URL as a string */ scope: scopes, // Enable incremental authorization. Recommended as a best practice. include_granted_scopes: true, // Include the state parameter to reduce the risk of CSRF attacks. state: state });
Catatan Penting - refresh_token hanya ditampilkan pada otorisasi
pertama. Lihat selengkapnya
di sini.
HTTP/REST
Endpoint OAuth 2.0 Google berada di https://accounts.google.com/o/oauth2/v2/auth. Endpoint ini hanya dapat diakses melalui HTTPS. Koneksi HTTP biasa ditolak.
Server otorisasi Google mendukung parameter string kueri berikut untuk aplikasi server web:
| Parameter | |||||||
|---|---|---|---|---|---|---|---|
client_id |
Wajib
Client ID untuk aplikasi Anda. Anda dapat menemukan nilai ini di halaman Klien Konsol Cloud. |
||||||
redirect_uri |
Wajib
Menentukan tempat server API mengalihkan pengguna setelah pengguna menyelesaikan alur otorisasi. Nilai harus sama persis dengan salah satu URI pengalihan yang diizinkan untuk klien OAuth 2.0, yang Anda konfigurasi di halaman Klien Konsol Cloud klien Anda. Jika nilai ini tidak cocok dengan URI pengalihan yang diizinkan untuk Perhatikan bahwa skema |
||||||
response_type |
Wajib
Menentukan apakah endpoint Google OAuth 2.0 menampilkan kode otorisasi. Tetapkan parameter value ke |
||||||
scope |
Wajib
Daftar cakupan yang dipisahkan spasi yang mengidentifikasi resource yang dapat diakses aplikasi Anda atas nama pengguna. Nilai ini menginformasikan layar izin yang ditampilkan Google kepada pengguna. Cakupan memungkinkan aplikasi Anda hanya meminta akses ke resource yang diperlukan, sekaligus memungkinkan pengguna mengontrol jumlah akses yang mereka berikan ke aplikasi Anda. Oleh karena itu, ada hubungan terbalik antara jumlah cakupan yang diminta dan kemungkinan mendapatkan izin pengguna. Sebaiknya aplikasi Anda meminta akses ke cakupan otorisasi dalam konteks jika memungkinkan. Dengan meminta akses ke data pengguna sesuai konteks, menggunakan otorisasi inkremental, Anda membantu pengguna memahami alasan aplikasi Anda memerlukan akses yang diminta. |
||||||
access_type |
Direkomendasikan
Menunjukkan apakah aplikasi Anda dapat memperbarui token akses saat pengguna tidak ada
di browser. Parameter value yang valid adalah Tetapkan nilai ke |
||||||
state |
Direkomendasikan
Menentukan nilai string apa pun yang digunakan aplikasi Anda untuk mempertahankan status antara permintaan otorisasi dan respons server otorisasi.
Server menampilkan nilai persis yang Anda kirim sebagai pasangan Anda dapat menggunakan parameter ini untuk beberapa tujuan, seperti mengarahkan pengguna ke
resource yang benar dalam aplikasi Anda, mengirim nonce, dan mengurangi pemalsuan permintaan
lintas situs. Karena |
||||||
include_granted_scopes |
Opsional
Memungkinkan aplikasi menggunakan otorisasi inkremental untuk meminta akses ke cakupan tambahan dalam konteks. Jika Anda menetapkan nilai parameter ini ke |
||||||
enable_granular_consent |
Opsional
Default-nya adalah Saat Google mengaktifkan izin terperinci untuk aplikasi, parameter ini tidak akan berpengaruh lagi. |
||||||
login_hint |
Opsional
Jika aplikasi Anda mengetahui pengguna mana yang mencoba melakukan autentikasi, aplikasi tersebut dapat menggunakan parameter ini untuk memberikan petunjuk ke Server Autentikasi Google. Server menggunakan petunjuk untuk menyederhanakan alur login, baik dengan mengisi otomatis kolom email di formulir login maupun dengan memilih sesi multi-login yang sesuai. Tetapkan nilai parameter ke alamat email atau ID |
||||||
prompt |
Opsional
Daftar perintah yang dipisahkan spasi dan peka huruf besar/kecil untuk ditampilkan kepada pengguna. Jika Anda tidak menentukan parameter ini, pengguna hanya akan diminta saat pertama kali project Anda meminta akses. Lihat Meminta izin ulang untuk mengetahui informasi selengkapnya. Nilai yang dimungkinkan adalah:
|
||||||
Langkah 2: Alihkan ke server OAuth 2.0 Google
Alihkan pengguna ke server OAuth 2.0 Google untuk memulai proses autentikasi dan otorisasi. Biasanya, hal ini terjadi saat aplikasi Anda pertama kali perlu mengakses data pengguna. Dalam kasus otorisasi inkremental, langkah ini juga terjadi saat aplikasi Anda pertama kali perlu mengakses resource tambahan yang tidak memiliki izin untuk diakses.
PHP
- Buat URL untuk meminta akses dari server OAuth 2.0 Google:
$auth_url = $client->createAuthUrl(); - Alihkan pengguna ke
$auth_url:header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
Python
Contoh ini menunjukkan cara mengalihkan pengguna ke URL otorisasi menggunakan framework aplikasi web Flask:
return flask.redirect(authorization_url)
Ruby
- Buat URL untuk meminta akses dari server OAuth 2.0 Google:
auth_uri = authorizer.get_authorization_url(request: request)
- Alihkan pengguna ke
auth_uri.
Node.js
-
Gunakan URL
authorizationUrlyang dihasilkan dari metodegenerateAuthUrlLangkah 1 untuk meminta akses dari server OAuth 2.0 Google. -
Alihkan pengguna ke
authorizationUrl.res.redirect(authorizationUrl);
HTTP/REST
Contoh pengalihan ke server otorisasi Google
Contoh URL ditampilkan di bawah, dengan baris baru dan spasi agar lebih mudah dibaca.
https://accounts.google.com/o/oauth2/v2/auth? scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly%20https%3A//www.googleapis.com/auth/calendar.readonly& access_type=offline& include_granted_scopes=true& response_type=code& state=state_parameter_passthrough_value& redirect_uri=https%3A//developers.google.com/oauthplayground& client_id=client_id
Setelah Anda membuat URL permintaan, alihkan pengguna ke URL tersebut.
Server OAuth 2.0 Google mengautentikasi pengguna dan mendapatkan izin dari pengguna agar aplikasi Anda dapat mengakses cakupan yang diminta. Respons dikirim kembali ke aplikasi Anda menggunakan URL pengalihan yang Anda tentukan.
Langkah 3: Google meminta izin pengguna
Pada langkah ini, pengguna memutuskan apakah akan memberikan akses yang diminta ke aplikasi Anda. Pada tahap ini, Google menampilkan jendela izin yang menunjukkan nama aplikasi Anda dan layanan Google API yang meminta izin untuk diakses dengan kredensial otorisasi pengguna dan ringkasan cakupan akses yang akan diberikan. Pengguna kemudian dapat mengizinkan untuk memberikan akses ke satu atau beberapa cakupan yang diminta oleh aplikasi Anda atau menolak permintaan tersebut.
Aplikasi Anda tidak perlu melakukan apa pun pada tahap ini karena aplikasi menunggu respons dari server OAuth 2.0 Google yang menunjukkan apakah akses diberikan. Respons tersebut dijelaskan di langkah berikut.
Error
Permintaan ke endpoint otorisasi OAuth 2.0 Google dapat menampilkan pesan error yang ditampilkan kepada pengguna, bukan alur autentikasi dan otorisasi yang diharapkan. Kode error umum dan resolusi yang disarankan adalah:
admin_policy_enforced
Akun Google tidak dapat mengizinkan satu atau beberapa cakupan yang diminta karena kebijakan administrator Google Workspace-nya. Lihat artikel bantuan Admin Google Workspace Mengontrol aplikasi pihak ketiga & internal mana yang dapat mengakses data Google Workspace untuk mengetahui informasi selengkapnya tentang cara administrator dapat membatasi akses ke semua cakupan atau cakupan sensitif dan terbatas hingga akses diberikan secara eksplisit ke client ID OAuth Anda.
disallowed_useragent
Endpoint otorisasi ditampilkan di dalam agen pengguna tersemat yang tidak diizinkan oleh Kebijakan OAuth 2.0 Google.
Developer iOS dan macOS mungkin mengalami error ini saat membuka permintaan otorisasi di
WKWebView.
Developer harus menggunakan library iOS seperti
Login dengan Google untuk iOS atau
AppAuth untuk iOS dari OpenID Foundation.
Developer web mungkin mengalami error ini saat aplikasi iOS atau macOS membuka link web umum di
agen pengguna tersemat dan pengguna membuka endpoint otorisasi OAuth 2.0 Google dari
situs Anda. Developer harus mengizinkan link umum dibuka di pengendali link default
sistem operasi, yang mencakup pengendali
Link Universal
atau aplikasi browser default. Library
SFSafariViewController
juga merupakan opsi yang didukung.
org_internal
Client ID OAuth dalam permintaan adalah bagian dari project yang membatasi akses ke Akun Google di Organisasi Google Cloud tertentu. Untuk mengetahui informasi selengkapnya tentang opsi konfigurasi ini, lihat bagian Jenis pengguna di artikel bantuan Menyiapkan layar izin OAuth.
invalid_client
Rahasia klien OAuth salah. Tinjau konfigurasi klien OAuth, termasuk client ID dan rahasia yang digunakan untuk permintaan ini.
deleted_client
Klien OAuth yang digunakan untuk membuat permintaan telah dihapus. Penghapusan dapat terjadi secara manual atau otomatis dalam kasus klien yang tidak digunakan . Klien yang dihapus dapat dipulihkan dalam waktu 30 hari setelah penghapusan. Pelajari lebih lanjut .
invalid_grant
Saat me-refresh token akses atau menggunakan otorisasi inkremental, token mungkin telah habis masa berlakunya atau tidak valid. Autentikasi pengguna lagi dan minta izin pengguna untuk mendapatkan token baru. Jika Anda terus melihat error ini, pastikan aplikasi Anda telah dikonfigurasi dengan benar dan Anda menggunakan token dan parameter yang benar dalam permintaan Anda. Jika tidak, akun pengguna mungkin telah dihapus atau dinonaktifkan.
redirect_uri_mismatch
redirect_uri yang diteruskan dalam permintaan otorisasi tidak cocok dengan URI pengalihan yang diizinkan untuk client ID OAuth. Tinjau URI pengalihan yang diotorisasi di
halaman Klien
Konsol Google Cloud.
Parameter redirect_uri dapat merujuk ke alur out-of-band (OOB) OAuth yang tidak digunakan lagi dan tidak didukung lagi. Lihat
panduan migrasi untuk mengupdate
integrasi Anda.
invalid_request
Terjadi error pada permintaan yang Anda buat. Hal ini dapat disebabkan oleh beberapa alasan:
- Permintaan tidak diformat dengan benar
- Permintaan tidak memiliki parameter yang diperlukan
- Permintaan tersebut menggunakan metode otorisasi yang tidak didukung oleh Google. Memverifikasi bahwa integrasi OAuth Anda menggunakan metode integrasi yang direkomendasikan
Langkah 4: Tangani respons server OAuth 2.0
Server OAuth 2.0 merespons permintaan akses aplikasi Anda menggunakan URL yang ditentukan dalam permintaan.
Jika pengguna menyetujui permintaan akses, respons akan berisi kode otorisasi. Jika pengguna tidak menyetujui permintaan tersebut, respons akan berisi pesan error. Kode otorisasi atau pesan error yang ditampilkan ke server web akan muncul di string kueri, seperti yang ditunjukkan dalam contoh berikut:
Respons error:
https://oauth2.example.com/auth?error=access_denied
Respons kode otorisasi:
https://oauth2.example.com/auth?code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7
Contoh respons server OAuth 2.0
Anda dapat menguji alur ini dengan mengklik contoh URL berikut, yang meminta akses hanya baca untuk melihat metadata file di Google Drive Anda dan akses hanya baca untuk melihat acara Google Kalender Anda:
https://accounts.google.com/o/oauth2/v2/auth? scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly%20https%3A//www.googleapis.com/auth/calendar.readonly& access_type=offline& include_granted_scopes=true& response_type=code& state=state_parameter_passthrough_value& redirect_uri=https%3A//developers.google.com/oauthplayground& client_id=client_id
Setelah menyelesaikan alur OAuth 2.0, browser akan mengalihkan Anda ke OAuth 2.0 Playground, alat untuk menguji alur OAuth. Anda akan melihat bahwa OAuth 2.0 Playground telah otomatis mengambil kode otorisasi.
Langkah 5: Tukarkan kode otorisasi dengan token refresh dan akses
Setelah menerima kode otorisasi, server web dapat menukarkan kode otorisasi dengan token akses.
PHP
Untuk menukar kode otorisasi dengan token akses, gunakan
metode fetchAccessTokenWithAuthCode:
$access_token = $client->fetchAccessTokenWithAuthCode($_GET['code']);Python
Di halaman callback, gunakan library google-auth untuk memverifikasi respons server
otorisasi. Kemudian, gunakan metode flow.fetch_token untuk menukar kode otorisasi dalam respons tersebut dengan token akses:
state = flask.session['state'] flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( 'client_secret.json', scopes=['https://www.googleapis.com/auth/drive.metadata.readonly'], state=state) flow.redirect_uri = flask.url_for('oauth2callback', _external=True) authorization_response = flask.request.url flow.fetch_token(authorization_response=authorization_response) # Store the credentials in browser session storage, but for security: client_id, client_secret, # and token_uri are instead stored only on the backend server. credentials = flow.credentials flask.session['credentials'] = { 'token': credentials.token, 'refresh_token': credentials.refresh_token, 'granted_scopes': credentials.granted_scopes}
Ruby
Di halaman callback, gunakan library googleauth untuk memverifikasi respons server
otorisasi. Gunakan metode authorizer.handle_auth_callback_deferred untuk menyimpan kode otorisasi dan mengalihkan kembali ke URL yang awalnya meminta otorisasi. Tindakan ini
menunda pertukaran kode dengan menyimpan hasilnya untuk sementara di sesi pengguna.
target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request) redirect target_url
Node.js
Untuk menukar kode otorisasi dengan token akses, gunakan metode getToken:
const url = require('url'); // Receive the callback from Google's OAuth 2.0 server. app.get('/oauth2callback', async (req, res) => { let q = url.parse(req.url, true).query; if (q.error) { // An error response e.g. error=access_denied console.log('Error:' + q.error); } else if (q.state !== req.session.state) { //check state value console.log('State mismatch. Possible CSRF attack'); res.end('State mismatch. Possible CSRF attack'); } else { // Get access and refresh tokens (if access_type is offline) let { tokens } = await oauth2Client.getToken(q.code); oauth2Client.setCredentials(tokens); });
HTTP/REST
Untuk menukar kode otorisasi dengan token akses, panggil endpoint https://oauth2.googleapis.com/token dan tetapkan parameter berikut:
| Kolom | |
|---|---|
client_id |
Client ID yang diperoleh dari halaman Klien Konsol Cloud. |
client_secret |
Opsional
Rahasia klien yang diperoleh dari halaman Klien Konsol Cloud. |
code |
Kode otorisasi yang ditampilkan dari permintaan awal. |
grant_type |
Seperti yang ditentukan dalam spesifikasi OAuth 2.0, nilai kolom ini harus ditetapkan ke authorization_code. |
redirect_uri |
Salah satu URI pengalihan yang tercantum untuk project Anda di
halaman Klien
Konsol Cloud untuk
client_id yang diberikan. |
Meskipun penggunaan DPoP bersifat opsional, sebaiknya gunakan untuk meningkatkan keamanan. Keamanan DPoP bergantung pada kunci pribadi yang dibatasi untuk satu perangkat; sebaiknya simpan kunci tersebut dengan cara yang tidak dapat disalin dari perangkat, misalnya dengan menggunakan TPM, Secure Enclave, atau keystore yang didukung hardware lainnya. Untuk menggunakan DPoP, aplikasi Anda harus membuat JWT bukti DPoP baru yang unik untuk setiap permintaan ke endpoint token dan menambahkannya sebagai header permintaan HTTP.
| Header | Wajib | Deskripsi |
|---|---|---|
DPoP |
Opsional | Bukti DPoP adalah JWT yang membuktikan
kepemilikan kunci pribadi. Ini adalah header, bukan parameter. Jika disediakan, token yang ditampilkan akan terikat ke kunci ini. Bukti baru yang unik harus dibuat untuk setiap permintaan dan
harus menyertakan klaim htm (metode HTTP) dan htu (URI HTTP) yang
cocok dengan permintaan. |
Cuplikan berikut menunjukkan contoh permintaan:
POST /token HTTP/1.1 Host: oauth2.googleapis.com Content-Type: application/x-www-form-urlencoded DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik\ VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR\ nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R1JE\ QSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiItQndDM0VTYzZhY2MybFRjIiwiaHRtIj\ oiUE9TVCIsImh0dSI6Imh0dHBzOi8vc2VydmVyLmV4YW1wbGUuY29tL3Rva2VuIiwia\ WF0IjoxNTYyMjYyNjE2fQ.2-GxA6T8lP4vfrg8v-FdWP0A0zdrj8igiMLvqRMUvwnQg\ 4PtFLbdLXiOSsX0x7NVY-FNyJK70nfbV37xRZT3Lg code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7& client_id=your_client_id& redirect_uri=https%3A//developers.google.com/oauthplayground& grant_type=authorization_code
Membuat bukti DPoP
Langkah-langkah berikut menunjukkan cara membuat bukti DPoP menggunakan OpenSSL dari command line:
- Buat pasangan kunci EC P-256:
openssl ecparam -name prime256v1 -genkey -noout -out dpop_private.pem openssl ec -in dpop_private.pem -pubout -out dpop_public.pem
- Buat header DPoP:
Header harus menyertakan klaim
typ,alg, danjwk(kunci publik). Nilaixdanyadalah koordinat berenkode Base64Url dari kunci publik EC Anda. Enkode Base64Url JSON ini:{ "typ":"dpop+jwt", "alg":"ES256", "jwk": { "kty":"EC", "x":"YOUR_PUBLIC_KEY_X", "y":"YOUR_PUBLIC_KEY_Y", "crv":"P-256" } } - Buat payload DPoP:
Payload harus menyertakan
jti(ID unik untuk permintaan),htm(metode HTTP, misalnya,POST),htu(URI HTTP, misalnya,https://oauth2.googleapis.com/token), daniat(diterbitkan pada waktu). Jika Anda menerima nonce dari server di headerDPoP-Noncepada respons terhadap permintaan sebelumnya, Anda harus menyertakan nilai nonce tersebut dalam klaimnonce. Klaim nonce bersifat opsional untuk pertukaran kode otorisasi dan hanya digunakan jika header DPoP-Nonce telah diterima sebelumnya. Enkode Base64Url JSON ini:{ "jti":"JTI_VALUE", "htm":"POST", "htu":"https://oauth2.googleapis.com/token", "iat":YOUR_JWT_ISSUED_TIME, "nonce":"SERVER_PROVIDED_NONCE" }Nilai
jtibergantung pada jenis pertukaran:- Untuk pertukaran kode otorisasi,
jtiharus berupa hash SHA256 berenkode Base64Url dari kode otorisasi:"jti":"BASE64URL(SHA256(AUTHORIZATION_CODE))". - Untuk pertukaran token refresh,
jtiharus berupa ID unik per permintaan:"jti":"YOUR_UNIQUE_PER_REQUEST_IDENTIFIER".
- Untuk pertukaran kode otorisasi,
- Menandatangani bukti:
Gabungkan header dan payload yang dienkode dengan titik (
.), lalu tanda tangani hasilnya dengan kunci pribadi Anda menggunakan ES256. Perhatikan bahwa JWS mengharuskan tanda tangan dalam formatR | Smentah yang digabungkan (64 byte untuk P-256). Jika menggunakan OpenSSL secara langsung, Anda harus mengonversi tanda tangan berenkode DER ASN.1 default ke format mentah ini.
Pertukaran yang berhasil ditunjukkan dengan respons 200 OK
yang berisi token. Jika bukti DPoP yang valid digunakan selama
pertukaran, token refresh yang ditampilkan Google akan terikat DPoP ke kunci Anda, tetapi token akses
tidak akan terikat DPoP. Token akses akan mempertahankan token_type dari
Bearer, bukan DPoP.
Selain itu, Google menampilkan header HTTP DPoP-Nonce dalam respons.
Klien Anda harus meng-cache nonce ini dan menyertakannya dalam klaim nonce dari bukti DPoP
dalam permintaan berikutnya (seperti saat menukar token refresh dengan token akses baru, atau saat
memanggil API yang dilindungi DPoP). Dengan menggunakan nonce yang dikeluarkan lebih awal ini, Anda dapat menghindari kegagalan round-trip tambahan (use_dpop_nonce) pada permintaan berikutnya.
Bukti DPoP harus disertakan untuk permintaan pertukaran token yang dibuat dengan token refresh yang terikat DPoP.
Pertukaran yang gagal terjadi jika header DPoP
tidak ada saat diharapkan, tidak valid, atau jika bukti menggunakan kunci yang berbeda dengan
kunci yang terikat ke token. Dalam kasus ini, server akan menampilkan error 400 Bad Request.
Jika bukti DPoP memiliki klaim htm atau htu yang tidak cocok, iat yang sudah tidak berlaku, jti yang digunakan kembali, atau tanda tangan yang tidak valid, Google akan menampilkan kode error invalid_dpop_proof.
Jika nonce DPoP diperlukan, seperti selama pertukaran token refresh, dan bukti DPoP tidak memiliki klaim nonce, atau nilai nonce tidak dapat diterima oleh server (misalnya, sudah tidak berlaku, sudah digunakan, atau salah), Google akan menampilkan kode error use_dpop_nonce beserta header HTTP DPoP-Nonce yang berisi nonce baru yang dapat Anda gunakan dalam permintaan berikutnya.
Kegagalan lainnya mungkin menampilkan invalid_grant.
Google merespons permintaan ini dengan menampilkan objek JSON yang berisi token akses berumur pendek dan token refresh.
Perhatikan bahwa token refresh hanya ditampilkan jika aplikasi Anda menetapkan parameter access_type
ke offline dalam permintaan awal ke server otorisasi
Google.
Respons berisi kolom berikut:
| Kolom | |
|---|---|
access_token |
Token yang dikirim aplikasi Anda untuk mengizinkan permintaan Google API. |
expires_in |
Sisa masa aktif token akses dalam detik. |
refresh_token |
Token yang dapat Anda gunakan untuk mendapatkan token akses baru. Token refresh valid hingga pengguna mencabut akses atau masa berlaku token refresh berakhir. Jika DPoP digunakan, token refresh terikat ke kunci pribadi yang digunakan untuk menandatangani bukti DPoP.
Sekali lagi, kolom ini hanya ada dalam respons ini jika Anda menetapkan parameter access_type
ke offline dalam permintaan awal ke server otorisasi Google.
|
refresh_token_expires_in |
Sisa masa aktif token refresh dalam detik. Nilai ini hanya ditetapkan saat pengguna memberikan akses berbasis waktu. |
scope |
Cakupan akses yang diberikan oleh access_token dinyatakan sebagai daftar string yang dipisahkan spasi dan peka huruf besar/kecil. |
token_type |
Jenis token yang ditampilkan. Nilai ini selalu Bearer, meskipun DPoP digunakan. |
Cuplikan berikut menunjukkan contoh header respons dan isi yang berhasil saat DPoP digunakan:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
DPoP-Nonce: AN3XwJjZsjnb0ZuWkRlek8QU7wY-Zhf-5IP6tO0tORz0KgtDT1Bo8FX-w4nz3r5lnepI
{
"access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in": 3920,
"token_type": "Bearer",
"scope": "https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly",
"refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}Error
Saat menukar kode otorisasi dengan token akses, Anda mungkin mengalami error berikut, bukan respons yang diharapkan. Kode error umum dan resolusi yang disarankan tercantum di bagian ini.
invalid_grant
Kode otorisasi yang diberikan tidak valid atau formatnya salah. Minta kode baru dengan memulai ulang proses OAuth untuk meminta izin pengguna lagi.
Langkah 6: Periksa cakupan yang diberikan pengguna
Saat meminta beberapa izin (cakupan), pengguna mungkin tidak memberikan akses ke semua izin tersebut kepada aplikasi Anda. Aplikasi Anda harus memverifikasi cakupan mana yang benar-benar diberikan dan menangani situasi saat beberapa izin ditolak dengan baik, biasanya dengan menonaktifkan fitur yang mengandalkan cakupan yang ditolak tersebut.
Namun, terdapat pengecualian. Aplikasi Google Workspace Enterprise dengan delegasi otoritas di seluruh domain, atau aplikasi yang ditandai sebagai Tepercaya, akan mengabaikan layar izin terperinci. Untuk aplikasi ini, pengguna tidak akan melihat layar izin terperinci. Sebagai gantinya, aplikasi Anda akan menerima semua cakupan yang diminta atau tidak sama sekali.
Untuk informasi yang lebih mendetail, lihat Cara menangani izin terperinci.
PHP
Untuk memeriksa cakupan yang telah diberikan pengguna, gunakan metode getGrantedScope():
// Space-separated string of granted scopes if it exists, otherwise null. $granted_scopes = $client->getOAuth2Service()->getGrantedScope(); // Determine which scopes user granted and build a dictionary $granted_scopes_dict = [ 'Drive' => str_contains($granted_scopes, Google\Service\Drive::DRIVE_METADATA_READONLY), 'Calendar' => str_contains($granted_scopes, Google\Service\Calendar::CALENDAR_READONLY) ];
Python
Objek credentials yang ditampilkan memiliki properti granted_scopes,
yang merupakan daftar cakupan yang telah diberikan pengguna ke aplikasi Anda.
credentials = flow.credentials flask.session['credentials'] = { 'token': credentials.token, 'refresh_token': credentials.refresh_token, 'granted_scopes': credentials.granted_scopes}
Fungsi berikut memeriksa cakupan yang telah diberikan pengguna ke aplikasi Anda.
def check_granted_scopes(credentials): features = {} if 'https://www.googleapis.com/auth/drive.metadata.readonly' in credentials['granted_scopes']: features['drive'] = True else: features['drive'] = False if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['granted_scopes']: features['calendar'] = True else: features['calendar'] = False return features
Ruby
Saat meminta beberapa cakupan sekaligus, periksa cakupan mana yang diberikan melalui
properti scope dari objek credentials.
# User authorized the request. Now, check which scopes were granted. if credentials.scope.include?(Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY) # User authorized read-only Drive activity permission. # Calling the APIs, etc else # User didn't authorize read-only Drive activity permission. # Update UX and application accordingly end # Check if user authorized Calendar read permission. if credentials.scope.include?(Google::Apis::CalendarV3::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 end
Node.js
Saat meminta beberapa cakupan sekaligus, periksa cakupan mana yang diberikan melalui
properti scope dari objek tokens.
// User authorized the request. Now, check which scopes were granted. if (tokens.scope.includes('https://www.googleapis.com/auth/drive.metadata.readonly')) { // User authorized read-only Drive activity permission. // Calling the APIs, etc. } else { // User didn't authorize read-only Drive activity permission. // Update UX and application accordingly } // Check if user authorized Calendar read permission. 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. // Update UX and application accordingly }
HTTP/REST
Untuk memeriksa apakah pengguna telah memberi aplikasi Anda akses ke cakupan tertentu,
periksa kolom scope dalam respons token akses. Cakupan akses yang diberikan oleh
access_token yang dinyatakan sebagai daftar string yang dipisahkan spasi dan peka huruf besar/kecil.
Misalnya, contoh respons token akses berikut menunjukkan bahwa pengguna telah memberikan akses ke aplikasi Anda untuk izin aktivitas Drive hanya baca dan acara Kalender:
{ "access_token": "1/fFAGRNJru1FTz70BzhT3Zg", "expires_in": 3920, "token_type": "Bearer", "scope": "https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly", "refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI" }
Memanggil Google API
PHP
Gunakan token akses untuk memanggil Google API dengan menyelesaikan langkah-langkah berikut:
- Jika Anda perlu menerapkan token akses ke objek
Google\Clientbaru — misalnya, jika Anda menyimpan token akses dalam sesi pengguna — gunakan metodesetAccessToken:$client->setAccessToken($access_token); - Bangun objek layanan untuk API yang ingin Anda panggil. Anda membuat objek layanan dengan
memberikan objek
Google\Clientyang diizinkan ke konstruktor untuk API yang ingin Anda panggil. Misalnya, untuk memanggil Drive API:$drive = new Google\Service\Drive($client); - Buat permintaan ke layanan API menggunakan
antarmuka yang disediakan oleh objek layanan.
Misalnya, untuk mencantumkan file di Google Drive pengguna yang diautentikasi:
$files = $drive->files->listFiles(array());
Python
Setelah mendapatkan token akses, aplikasi Anda dapat menggunakan token tersebut untuk mengizinkan permintaan API atas nama akun pengguna atau akun layanan tertentu. Gunakan kredensial otorisasi khusus pengguna untuk membuat objek layanan bagi API yang ingin Anda panggil, lalu gunakan objek tersebut untuk membuat permintaan API yang diotorisasi.
- Bangun objek layanan untuk API yang ingin Anda panggil. Anda membuat objek layanan dengan
memanggil metode
buildlibrarygoogleapiclient.discoverydengan nama dan versi API serta kredensial pengguna: Misalnya, untuk memanggil Drive API versi 3:from googleapiclient.discovery import build drive = build('drive', 'v2', credentials=credentials)
- Buat permintaan ke layanan API menggunakan
antarmuka yang disediakan oleh objek layanan.
Misalnya, untuk mencantumkan file di Google Drive pengguna yang diautentikasi:
files = drive.files().list().execute()
Ruby
Setelah mendapatkan token akses, aplikasi Anda dapat menggunakan token tersebut untuk membuat permintaan API atas nama akun pengguna atau akun layanan tertentu. Gunakan kredensial otorisasi khusus pengguna untuk membuat objek layanan bagi API yang ingin Anda panggil, lalu gunakan objek tersebut untuk membuat permintaan API yang diotorisasi.
- Bangun objek layanan untuk API yang ingin Anda panggil.
Misalnya, untuk memanggil Drive API versi 3:
drive = Google::Apis::DriveV3::DriveService.new
- Tetapkan kredensial pada layanan:
drive.authorization = credentials
- Buat permintaan ke layanan API menggunakan
antarmuka
yang disediakan oleh objek layanan.
Misalnya, untuk mencantumkan file di Google Drive pengguna yang diautentikasi:
files = drive.list_files
Atau, otorisasi dapat diberikan per metode dengan menyediakan parameter options ke metode:
files = drive.list_files(options: { authorization: credentials })
Node.js
Setelah mendapatkan token akses dan menetapkannya ke objek OAuth2, gunakan objek tersebut untuk memanggil Google API. Aplikasi Anda dapat menggunakan token tersebut untuk mengizinkan permintaan API atas nama akun pengguna atau akun layanan tertentu. Bangun objek layanan untuk API yang ingin Anda panggil.
Misalnya, kode berikut menggunakan Google Drive API untuk mencantumkan nama file di Drive pengguna.
const { google } = require('googleapis'); // Example of using Google Drive API to list filenames in user's Drive. const drive = google.drive('v3'); drive.files.list({ auth: oauth2Client, pageSize: 10, fields: 'nextPageToken, files(id, name)', }, (err1, res1) => { if (err1) return console.log('The API returned an error: ' + err1); const files = res1.data.files; if (files.length) { console.log('Files:'); files.map((file) => { console.log(`${file.name} (${file.id})`); }); } else { console.log('No files found.'); } });
HTTP/REST
Setelah aplikasi Anda mendapatkan token akses, Anda dapat menggunakan token tersebut untuk melakukan panggilan ke Google API atas nama akun pengguna tertentu jika cakupan akses yang diperlukan oleh API telah diberikan. Untuk melakukannya, sertakan
token akses dalam permintaan ke API dengan menyertakan parameter kueri access_token
atau nilai Bearer header HTTP Authorization. Jika memungkinkan,
header HTTP lebih disarankan, karena string kueri cenderung terlihat di log server. Dalam sebagian besar
kasus, Anda dapat menggunakan library klien untuk menyiapkan panggilan ke Google API (misalnya, saat
memanggil Drive Files API).
Anda dapat mencoba semua Google API dan melihat cakupannya di OAuth 2.0 Playground.
Contoh HTTP GET
Panggilan ke endpoint
drive.files (Drive Files API) menggunakan header HTTP Authorization: Bearer
mungkin terlihat seperti berikut. Perhatikan bahwa Anda harus menentukan token akses Anda sendiri:
GET /drive/v2/files HTTP/1.1 Host: www.googleapis.com Authorization: Bearer access_token
Berikut adalah panggilan ke API yang sama untuk pengguna yang diautentikasi menggunakan parameter string kueri access_token:
GET https://www.googleapis.com/drive/v2/files?access_token=access_token
Contoh curl
Anda dapat menguji perintah ini dengan aplikasi command line curl. Berikut adalah contoh yang menggunakan opsi header HTTP (lebih disukai):
curl -H "Authorization: Bearer access_token" https://www.googleapis.com/drive/v2/files
Atau, opsi parameter string kueri:
curl https://www.googleapis.com/drive/v2/files?access_token=access_token
Contoh lengkap
Contoh berikut mencetak daftar file berformat JSON di Google Drive pengguna setelah pengguna melakukan autentikasi dan memberikan izin kepada aplikasi untuk mengakses metadata Drive pengguna.
PHP
Untuk menjalankan contoh ini:
- Di Konsol API, tambahkan URL mesin lokal ke daftar URL pengalihan. Misalnya, tambahkan
http://localhost:8080. - Buat direktori baru dan ubah ke direktori tersebut. Contoh:
mkdir ~/php-oauth2-example cd ~/php-oauth2-example
- Instal Library Klien Google API untuk PHP menggunakan Composer:
composer require google/apiclient:^2.15.0
- Buat file
index.phpdanoauth2callback.phpdengan konten berikut. - Jalankan contoh dengan server web pengujian bawaan PHP:
php -S localhost:8080 ~/php-oauth2-example
index.php
<?php require_once __DIR__.'/vendor/autoload.php'; session_start(); $client = new Google\Client(); $client->setAuthConfig('client_secret.json'); // User granted permission as an access token is in the session. if (isset($_SESSION['access_token']) && $_SESSION['access_token']) { $client->setAccessToken($_SESSION['access_token']); // Check if user granted Drive permission if ($_SESSION['granted_scopes_dict']['Drive']) { echo "Drive feature is enabled."; echo "</br>"; $drive = new Drive($client); $files = array(); $response = $drive->files->listFiles(array()); foreach ($response->files as $file) { echo "File: " . $file->name . " (" . $file->id . ")"; echo "</br>"; } } else { echo "Drive feature is NOT enabled."; echo "</br>"; } // Check if user granted Calendar permission if ($_SESSION['granted_scopes_dict']['Calendar']) { echo "Calendar feature is enabled."; echo "</br>"; } else { echo "Calendar feature is NOT enabled."; echo "</br>"; } } else { // Redirect users to outh2call.php which redirects users to Google OAuth 2.0 $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php'; header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL)); } ?>
oauth2callback.php
<?php require_once __DIR__.'/vendor/autoload.php'; session_start(); $client = new Google\Client(); // Required, call the setAuthConfig function to load authorization credentials from // client_secret.json file. $client->setAuthConfigFile('client_secret.json'); $client->setRedirectUri('http://' . $_SERVER['HTTP_HOST']. $_SERVER['PHP_SELF']); // Required, to set the scope value, call the addScope function. $client->addScope([Google\Service\Drive::DRIVE_METADATA_READONLY, Google\Service\Calendar::CALENDAR_READONLY]); // Enable incremental authorization. Recommended as a best practice. $client->setIncludeGrantedScopes(true); // Recommended, offline access will give you both an access and refresh token so that // your app can refresh the access token without user interaction. $client->setAccessType("offline"); // Generate a URL for authorization as it doesn't contain code and error if (!isset($_GET['code']) && !isset($_GET['error'])) { // Generate and set state value $state = bin2hex(random_bytes(16)); $client->setState($state); $_SESSION['state'] = $state; // Generate a url that asks permissions. $auth_url = $client->createAuthUrl(); header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL)); } // User authorized the request and authorization code is returned to exchange access and // refresh tokens. if (isset($_GET['code'])) { // Check the state value if (!isset($_GET['state']) || $_GET['state'] !== $_SESSION['state']) { die('State mismatch. Possible CSRF attack.'); } // Get access and refresh tokens (if access_type is offline) $token = $client->fetchAccessTokenWithAuthCode($_GET['code']); /** Save access and refresh token to the session variables. * ACTION ITEM: In a production app, you likely want to save the * refresh token in a secure persistent storage instead. */ $_SESSION['access_token'] = $token; $_SESSION['refresh_token'] = $client->getRefreshToken(); // Space-separated string of granted scopes if it exists, otherwise null. $granted_scopes = $client->getOAuth2Service()->getGrantedScope(); // Determine which scopes user granted and build a dictionary $granted_scopes_dict = [ 'Drive' => str_contains($granted_scopes, Google\Service\Drive::DRIVE_METADATA_READONLY), 'Calendar' => str_contains($granted_scopes, Google\Service\Calendar::CALENDAR_READONLY) ]; $_SESSION['granted_scopes_dict'] = $granted_scopes_dict; $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/'; header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL)); } // An error response e.g. error=access_denied if (isset($_GET['error'])) { echo "Error: ". $_GET['error']; } ?>
Python
Contoh ini menggunakan framework Flask. Alat ini
menjalankan aplikasi web di http://localhost:8080 yang memungkinkan Anda menguji alur
OAuth 2.0. Jika Anda membuka URL tersebut, Anda akan melihat lima link:
- Call Drive API: Link ini mengarah ke halaman yang mencoba menjalankan contoh permintaan API jika pengguna memberikan izin. Jika perlu, library ini akan memulai alur otorisasi. Jika berhasil, halaman akan menampilkan respons API.
- Halaman tiruan untuk memanggil Calendar API: Link ini mengarah ke halaman tiruan yang mencoba menjalankan contoh permintaan API Calendar jika pengguna memberikan izin. Jika perlu, library ini akan memulai alur otorisasi. Jika berhasil, halaman akan menampilkan respons API.
- Menguji alur autentikasi secara langsung: Link ini mengarah ke halaman yang mencoba mengirim pengguna melalui alur otorisasi. Aplikasi meminta izin untuk mengirimkan permintaan API yang diizinkan atas nama pengguna.
- Cabut kredensial saat ini: Link ini mengarah ke halaman yang mencabut izin yang telah diberikan pengguna ke aplikasi.
- Clear Flask session credentials: Link ini menghapus kredensial otorisasi yang disimpan dalam sesi Flask. Tindakan ini memungkinkan Anda melihat apa yang akan terjadi jika pengguna yang telah memberikan izin ke aplikasi Anda mencoba menjalankan permintaan API dalam sesi baru. Tindakan ini juga memungkinkan Anda melihat respons API yang akan diperoleh aplikasi jika pengguna telah mencabut izin yang diberikan ke aplikasi Anda, dan aplikasi Anda masih mencoba mengizinkan permintaan dengan token akses yang dicabut.
# -*- coding: utf-8 -*- import os import flask import json import requests import google.oauth2.credentials import google_auth_oauthlib.flow import googleapiclient.discovery # This variable specifies the name of a file that contains the OAuth 2.0 # information for this application, including its client_id and client_secret. CLIENT_SECRETS_FILE = "client_secret.json" # The OAuth 2.0 access scope allows for access to the # authenticated user's account and requires requests to use an SSL connection. SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly', 'https://www.googleapis.com/auth/calendar.readonly'] API_SERVICE_NAME = 'drive' API_VERSION = 'v2' app = flask.Flask(__name__) # Note: A secret key is included in the sample so that it works. # If you use this code in your application, replace this with a truly secret # key. See https://flask.palletsprojects.com/quickstart/#sessions. app.secret_key = 'REPLACE ME - this value is here as a placeholder.' @app.route('/') def index(): return print_index_table() @app.route('/drive') def drive_api_request(): if 'credentials' not in flask.session: return flask.redirect('authorize') features = flask.session['features'] if features['drive']: # Load client secrets from the server-side file. with open(CLIENT_SECRETS_FILE, 'r') as f: client_config = json.load(f)['web'] # Load user-specific credentials from browser session storage. session_credentials = flask.session['credentials'] # Reconstruct the credentials object. credentials = google.oauth2.credentials.Credentials( refresh_token=session_credentials.get('refresh_token'), scopes=session_credentials.get('granted_scopes'), token=session_credentials.get('token'), client_id=client_config.get('client_id'), client_secret=client_config.get('client_secret'), token_uri=client_config.get('token_uri')) drive = googleapiclient.discovery.build( API_SERVICE_NAME, API_VERSION, credentials=credentials) files = drive.files().list().execute() # Save credentials back to session in case access token was refreshed. flask.session['credentials'] = credentials_to_dict(credentials) return flask.jsonify(**files) else: # User didn't authorize read-only Drive activity permission. return '<p>Drive feature is not enabled.</p>' @app.route('/calendar') def calendar_api_request(): if 'credentials' not in flask.session: return flask.redirect('authorize') features = flask.session['features'] if features['calendar']: # User authorized Calendar read permission. # Calling the APIs, etc. return ('<p>User granted the Google Calendar read permission. '+ 'This sample code does not include code to call Calendar</p>') else: # User didn't authorize Calendar read permission. # Update UX and application accordingly return '<p>Calendar feature is not enabled.</p>' @app.route('/authorize') def authorize(): # Create flow instance to manage the OAuth 2.0 Authorization Grant Flow steps. flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( CLIENT_SECRETS_FILE, scopes=SCOPES) # The URI created here must exactly match one of the authorized redirect URIs # for the OAuth 2.0 client, which you configured in the API Console. If this # value doesn't match an authorized URI, you will get a 'redirect_uri_mismatch' # error. flow.redirect_uri = flask.url_for('oauth2callback', _external=True) authorization_url, state = flow.authorization_url( # Enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Enable incremental authorization. Recommended as a best practice. include_granted_scopes='true') # Store the state so the callback can verify the auth server response. flask.session['state'] = state return flask.redirect(authorization_url) @app.route('/oauth2callback') def oauth2callback(): # Specify the state when creating the flow in the callback so that it can # verified in the authorization server response. state = flask.session['state'] flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( CLIENT_SECRETS_FILE, scopes=SCOPES, state=state) flow.redirect_uri = flask.url_for('oauth2callback', _external=True) # Use the authorization server's response to fetch the OAuth 2.0 tokens. authorization_response = flask.request.url flow.fetch_token(authorization_response=authorization_response) # Store credentials in the session. # ACTION ITEM: In a production app, you likely want to save these # credentials in a persistent database instead. credentials = flow.credentials credentials = credentials_to_dict(credentials) flask.session['credentials'] = credentials # Check which scopes user granted features = check_granted_scopes(credentials) flask.session['features'] = features return flask.redirect('/') @app.route('/revoke') def revoke(): if 'credentials' not in flask.session: return ('You need to <a href="/authorize">authorize</a> before ' + 'testing the code to revoke credentials.') # Load client secrets from the server-side file. with open(CLIENT_SECRETS_FILE, 'r') as f: client_config = json.load(f)['web'] # Load user-specific credentials from the session. session_credentials = flask.session['credentials'] # Reconstruct the credentials object. credentials = google.oauth2.credentials.Credentials( refresh_token=session_credentials.get('refresh_token'), scopes=session_credentials.get('granted_scopes'), token=session_credentials.get('token'), client_id=client_config.get('client_id'), client_secret=client_config.get('client_secret'), token_uri=client_config.get('token_uri')) revoke = requests.post('https://oauth2.googleapis.com/revoke', params={'token': credentials.token}, headers = {'content-type': 'application/x-www-form-urlencoded'}) status_code = getattr(revoke, 'status_code') if status_code == 200: # Clear the user's session credentials after successful revocation if 'credentials' in flask.session: del flask.session['credentials'] del flask.session['features'] return('Credentials successfully revoked.' + print_index_table()) else: return('An error occurred.' + print_index_table()) @app.route('/clear') def clear_credentials(): if 'credentials' in flask.session: del flask.session['credentials'] return ('Credentials have been cleared.<br><br>' + print_index_table()) def credentials_to_dict(credentials): return {'token': credentials.token, 'refresh_token': credentials.refresh_token, 'granted_scopes': credentials.granted_scopes} def check_granted_scopes(credentials): features = {} if 'https://www.googleapis.com/auth/drive.metadata.readonly' in credentials['granted_scopes']: features['drive'] = True else: features['drive'] = False if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['granted_scopes']: features['calendar'] = True else: features['calendar'] = False return features def print_index_table(): return ('<table>' + '<tr><td><a href="/authorize">Test the auth flow directly</a></td>' + '<td>Go directly to the authorization flow. If there are stored ' + ' credentials, you still might not be prompted to reauthorize ' + ' the application.</td></tr>' + '<tr><td><a href="/drive">Call Drive API directly</a></td>' + '<td> Use stored credentials to call the API, you still might not be prompted to reauthorize ' + ' the application.</td></tr>' + '<tr><td><a href="/calendar">Call Calendar API directly</a></td>' + '<td> Use stored credentials to call the API, you still might not be prompted to reauthorize ' + ' the application.</td></tr>' + '<tr><td><a href="/revoke">Revoke current credentials</a></td>' + '<td>Revoke the access token associated with the current user ' + ' session. After revoking credentials, if you go to the test ' + ' page, you should see an <code>invalid_grant</code> error.' + '</td></tr>' + '<tr><td><a href="/clear">Clear Flask session credentials</a></td>' + '<td>Clear the access token currently stored in the user session. ' + ' After clearing the token, if you <a href="/authorize">authorize</a> ' + ' again, you should go back to the auth flow.' + '</td></tr></table>') if __name__ == '__main__': # When running locally, disable OAuthlib's HTTPs verification. # ACTION ITEM for developers: # When running in production *do not* leave this option enabled. os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' # This disables the requested scopes and granted scopes check. # If users only grant partial request, the warning would not be thrown. os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = '1' # Specify a hostname and port that are set as a valid redirect URI # for your API project in the Google API Console. app.run('localhost', 8080, debug=True)
Ruby
Contoh ini menggunakan framework Sinatra.
require 'googleauth' require 'googleauth/web_user_authorizer' require 'googleauth/stores/redis_token_store' require 'google/apis/drive_v3' require 'google/apis/calendar_v3' require 'sinatra' configure do enable :sessions # Required, call the from_file method to retrieve the client ID from a # client_secret.json file. set :client_id, Google::Auth::ClientId.from_file('/path/to/client_secret.json') # Required, scope value # Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar. scope = ['Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY', 'Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY'] # Required, Authorizers require a storage instance to manage long term persistence of # access and refresh tokens. set :token_store, Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new) # Required, indicate where the API server will redirect the user after the user completes # the authorization flow. The redirect URI is required. The value must exactly # match one of the authorized redirect URIs for the OAuth 2.0 client, which you # configured in the API Console. If this value doesn't match an authorized URI, # you will get a 'redirect_uri_mismatch' error. set :callback_uri, '/oauth2callback' # 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. set :authorizer, Google::Auth::WebUserAuthorizer.new(settings.client_id, settings.scope, settings.token_store, callback_uri: settings.callback_uri) end get '/' do # NOTE: Assumes the user is already authenticated to the app user_id = request.session['user_id'] # Fetch stored credentials for the user from the given request session. # nil if none present credentials = settings.authorizer.get_credentials(user_id, request) if credentials.nil? # Generate a url that asks the user to authorize requested scope(s). # Then, redirect user to the url. redirect settings.authorizer.get_authorization_url(request: request) end # User authorized the request. Now, check which scopes were granted. if credentials.scope.include?(Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY) # User authorized read-only Drive activity permission. # Example of using Google Drive API to list filenames in user's Drive. drive = Google::Apis::DriveV3::DriveService.new files = drive.list_files(options: { authorization: credentials }) "<pre>#{JSON.pretty_generate(files.to_h)}</pre>" else # User didn't authorize read-only Drive activity permission. # Update UX and application accordingly end # Check if user authorized Calendar read permission. if credentials.scope.include?(Google::Apis::CalendarV3::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 end end # Receive the callback from Google's OAuth 2.0 server. get '/oauth2callback' do # Handle the result of the oauth callback. Defers the exchange of the code by # temporarily stashing the results in the user's session. target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request) redirect target_url end
Node.js
Untuk menjalankan contoh ini:
-
Di Konsol API, tambahkan URL
mesin lokal ke daftar URL pengalihan. Misalnya, tambahkan
http://localhost. - Pastikan Anda telah menginstal LTS pemeliharaan, LTS aktif, atau rilis Node.js saat ini.
-
Buat direktori baru dan ubah ke direktori tersebut. Contoh:
mkdir ~/nodejs-oauth2-example cd ~/nodejs-oauth2-example
-
Instal Library Klien Google API untuk Node.js menggunakan npm:
npm install googleapis
-
Buat file
main.jsdengan konten berikut. -
Jalankan contoh:
node .\main.js
main.js
const http = require('http'); const https = require('https'); const url = require('url'); const { google } = require('googleapis'); const crypto = require('crypto'); const express = require('express'); const session = require('express-session'); /** * To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI. * 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 two non-Sign-In scopes: Read-only Drive activity and Google Calendar. const scopes = [ 'https://www.googleapis.com/auth/drive.metadata.readonly', 'https://www.googleapis.com/auth/calendar.readonly' ]; /* Global variable that stores user credential in this code example. * ACTION ITEM for developers: * Store user's refresh token in your data store if * incorporating this code into your real app. * For more information on handling refresh tokens, * see https://github.com/googleapis/google-api-nodejs-client#handling-refresh-tokens */ let userCredential = null; async function main() { const app = express(); app.use(session({ secret: 'your_secure_secret_key', // Replace with a strong secret resave: false, saveUninitialized: false, })); // Example on redirecting user to Google's OAuth 2.0 server. app.get('/', async (req, res) => { // Generate a secure random state value. const state = crypto.randomBytes(32).toString('hex'); // Store state in the session req.session.state = state; // Generate a url that asks permissions for the Drive activity and Google Calendar scope const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined above. * Alternatively, if only one scope is needed, you can pass a scope URL as a string */ scope: scopes, // Enable incremental authorization. Recommended as a best practice. include_granted_scopes: true, // Include the state parameter to reduce the risk of CSRF attacks. state: state }); res.redirect(authorizationUrl); }); // Receive the callback from Google's OAuth 2.0 server. app.get('/oauth2callback', async (req, res) => { // Handle the OAuth 2.0 server response let q = url.parse(req.url, true).query; if (q.error) { // An error response e.g. error=access_denied console.log('Error:' + q.error); } else if (q.state !== req.session.state) { //check state value console.log('State mismatch. Possible CSRF attack'); res.end('State mismatch. Possible CSRF attack'); } else { // Get access and refresh tokens (if access_type is offline) let { tokens } = await oauth2Client.getToken(q.code); oauth2Client.setCredentials(tokens); /** Save credential to the global variable in case access token was refreshed. * ACTION ITEM: In a production app, you likely want to save the refresh token * in a secure persistent database instead. */ userCredential = tokens; // User authorized the request. Now, check which scopes were granted. if (tokens.scope.includes('https://www.googleapis.com/auth/drive.metadata.readonly')) { // User authorized read-only Drive activity permission. // Example of using Google Drive API to list filenames in user's Drive. const drive = google.drive('v3'); drive.files.list({ auth: oauth2Client, pageSize: 10, fields: 'nextPageToken, files(id, name)', }, (err1, res1) => { if (err1) return console.log('The API returned an error: ' + err1); const files = res1.data.files; if (files.length) { console.log('Files:'); files.map((file) => { console.log(`${file.name} (${file.id})`); }); } else { console.log('No files found.'); } }); } else { // User didn't authorize read-only Drive activity permission. // Update UX and application accordingly } // Check if user authorized Calendar read permission. 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. // Update UX and application accordingly } } }); // Example on revoking a token app.get('/revoke', async (req, res) => { // Build the string for the POST request let postData = "token=" + userCredential.access_token; // Options for POST request to Google's OAuth 2.0 server to revoke a token let postOptions = { host: 'oauth2.googleapis.com', port: '443', path: '/revoke', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(postData) } }; // Set up the request const postReq = https.request(postOptions, function (res) { res.setEncoding('utf8'); res.on('data', d => { console.log('Response: ' + d); }); }); postReq.on('error', error => { console.log(error) }); // Post the request with data postReq.write(postData); postReq.end(); }); const server = http.createServer(app); server.listen(8080); } main().catch(console.error);
HTTP/REST
Contoh Python ini menggunakan framework Flask dan library Requests untuk mendemonstrasikan alur web OAuth 2.0. Sebaiknya gunakan Library Klien Google API untuk Python untuk alur ini. (Contoh di tab Python menggunakan library klien.)
import json import flask import requests app = flask.Flask(__name__) # To get these credentials (CLIENT_ID CLIENT_SECRET) and for your application, visit # https://console.cloud.google.com/apis/credentials. CLIENT_ID = '123456789.apps.googleusercontent.com' CLIENT_SECRET = 'abc123' # Read from a file or environmental variable in a real app # Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar. SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly' # Indicate where the API server will redirect the user after the user completes # the authorization flow. The redirect URI is required. The value must exactly # match one of the authorized redirect URIs for the OAuth 2.0 client, which you # configured in the API Console. If this value doesn't match an authorized URI, # you will get a 'redirect_uri_mismatch' error. REDIRECT_URI = 'http://example.com/oauth2callback' @app.route('/') def index(): if 'credentials' not in flask.session: return flask.redirect(flask.url_for('oauth2callback')) credentials = json.loads(flask.session['credentials']) if credentials['expires_in'] <= 0: return flask.redirect(flask.url_for('oauth2callback')) else: # User authorized the request. Now, check which scopes were granted. if 'https://www.googleapis.com/auth/drive.metadata.readonly' in credentials['scope']: # User authorized read-only Drive activity permission. # Example of using Google Drive API to list filenames in user's Drive. headers = {'Authorization': 'Bearer {}'.format(credentials['access_token'])} req_uri = 'https://www.googleapis.com/drive/v2/files' r = requests.get(req_uri, headers=headers).text else: # User didn't authorize read-only Drive activity permission. # Update UX and application accordingly r = 'User did not authorize Drive permission.' # Check if user authorized Calendar read permission. if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['scope']: # User authorized Calendar read permission. # Calling the APIs, etc. r += 'User authorized Calendar permission.' else: # User didn't authorize Calendar read permission. # Update UX and application accordingly r += 'User did not authorize Calendar permission.' return r @app.route('/oauth2callback') def oauth2callback(): if 'code' not in flask.request.args: state = str(uuid.uuid4()) flask.session['state'] = state # Generate a url that asks permissions for the Drive activity # and Google Calendar scope. Then, redirect user to the url. auth_uri = ('https://accounts.google.com/o/oauth2/v2/auth?response_type=code' '&client_id={}&redirect_uri={}&scope={}&state={}').format(CLIENT_ID, REDIRECT_URI, SCOPE, state) return flask.redirect(auth_uri) else: if 'state' not in flask.request.args or flask.request.args['state'] != flask.session['state']: return 'State mismatch. Possible CSRF attack.', 400 auth_code = flask.request.args.get('code') data = {'code': auth_code, 'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET, 'redirect_uri': REDIRECT_URI, 'grant_type': 'authorization_code'} # Exchange authorization code for access and refresh tokens (if access_type is offline) r = requests.post('https://oauth2.googleapis.com/token', data=data) flask.session['credentials'] = r.text return flask.redirect(flask.url_for('index')) if __name__ == '__main__': import uuid app.secret_key = str(uuid.uuid4()) app.debug = False app.run()
Aturan validasi URI pengalihan
Google menerapkan aturan validasi berikut ke URI pengalihan untuk membantu developer menjaga keamanan aplikasi mereka. URI pengalihan Anda harus mematuhi aturan ini. Lihat RFC 3986 bagian 3 untuk definisi domain, host, jalur, kueri, skema, dan userinfo, yang digunakan dalam aturan ini.
| Aturan validasi | |
|---|---|
| Skema |
URI pengalihan harus menggunakan skema HTTPS, bukan HTTP biasa. URI localhost (termasuk URI alamat IP localhost) dikecualikan dari aturan ini. |
| Host |
Host tidak boleh berupa alamat IP mentah. Alamat IP localhost dikecualikan dari aturan ini. |
| Domain |
“googleusercontent.com”.goo.gl) kecuali jika
aplikasi memiliki domain tersebut. Selain itu, jika aplikasi yang memiliki domain penyingkat memilih untuk mengalihkan ke domain tersebut, URI pengalihan tersebut harus berisi “/google-callback/” di jalurnya atau diakhiri dengan “/google-callback”. |
| Userinfo |
URI pengalihan tidak boleh berisi subkomponen userinfo. |
| Path |
URI pengalihan tidak boleh berisi path traversal (juga disebut directory backtracking),
yang diwakili oleh |
| Kueri |
URI pengalihan tidak boleh berisi pengalihan terbuka. |
| Fragment |
URI pengalihan tidak boleh berisi komponen fragmen. |
| Karakter |
URI pengalihan tidak boleh berisi karakter tertentu, termasuk:
|
Otorisasi inkremental
Dalam protokol OAuth 2.0, aplikasi Anda meminta otorisasi untuk mengakses resource, yang diidentifikasi berdasarkan cakupan. Praktik terbaik pengalaman pengguna adalah meminta otorisasi untuk resource pada saat Anda membutuhkannya. Untuk mengaktifkan praktik tersebut, server otorisasi Google mendukung otorisasi inkremental. Fitur ini memungkinkan Anda meminta cakupan sesuai kebutuhan dan, jika pengguna memberikan izin untuk cakupan baru, menampilkan kode otorisasi yang dapat ditukarkan dengan token yang berisi semua cakupan yang telah diberikan pengguna ke project.
Misalnya, aplikasi yang memungkinkan orang mencoba trek musik dan membuat mix mungkin memerlukan sangat sedikit resource pada waktu login, mungkin hanya nama orang yang login. Namun, menyimpan mix yang sudah selesai akan memerlukan akses ke Google Drive mereka. Sebagian besar orang akan menganggapnya wajar jika mereka hanya diminta akses ke Google Drive mereka pada saat aplikasi benar-benar membutuhkannya.
Dalam hal ini, pada saat login, aplikasi mungkin meminta cakupan openid dan
profile untuk melakukan login dasar, lalu meminta
cakupan https://www.googleapis.com/auth/drive.file pada saat permintaan pertama untuk menyimpan
campuran.
Untuk menerapkan otorisasi inkremental, Anda harus menyelesaikan alur normal untuk meminta token akses, tetapi pastikan permintaan otorisasi menyertakan cakupan yang telah diberikan sebelumnya. Dengan pendekatan ini, aplikasi Anda tidak perlu mengelola beberapa token akses.
Aturan berikut berlaku untuk token akses yang diperoleh dari otorisasi inkremental:
- Token ini dapat digunakan untuk mengakses resource yang sesuai dengan cakupan mana pun yang digabungkan ke dalam otorisasi gabungan yang baru.
- Saat Anda menggunakan token refresh untuk otorisasi gabungan guna mendapatkan token akses, token akses tersebut mewakili otorisasi gabungan dan dapat digunakan untuk nilai
scopeapa pun yang disertakan dalam respons. - Otorisasi gabungan mencakup semua cakupan yang diberikan pengguna ke project API meskipun izin tersebut diminta dari klien yang berbeda. Misalnya, jika pengguna memberikan akses ke satu cakupan menggunakan klien desktop aplikasi, lalu memberikan cakupan lain ke aplikasi yang sama melalui klien seluler, otorisasi gabungan akan menyertakan kedua cakupan tersebut.
- Jika Anda mencabut token yang mewakili otorisasi gabungan, akses ke semua cakupan otorisasi tersebut atas nama pengguna terkait akan dicabut secara bersamaan.
Contoh kode khusus bahasa di Langkah 1: Tetapkan pengalihan otorisasi ke server OAuth 2.0 Google semuanya menggunakan otorisasi inkremental. Contoh kode berikut juga menunjukkan kode yang perlu Anda tambahkan untuk menggunakan otorisasi inkremental.
PHP
$client->setIncludeGrantedScopes(true);Python
Di Python, tetapkan argumen kata kunci include_granted_scopes ke true untuk
memastikan bahwa permintaan otorisasi menyertakan cakupan yang telah diberikan sebelumnya. Sangat mungkin bahwa
include_granted_scopes tidak akan menjadi satu-satunya argumen kata kunci yang Anda tetapkan, seperti
yang ditunjukkan dalam contoh di bawah.
authorization_url, state = flow.authorization_url( # Enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Enable incremental authorization. Recommended as a best practice. include_granted_scopes='true')
Ruby
auth_client.update!( :additional_parameters => {"include_granted_scopes" => "true"} )
Node.js
const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined above. * Alternatively, if only one scope is needed, you can pass a scope URL as a string */ scope: scopes, // Enable incremental authorization. Recommended as a best practice. include_granted_scopes: true });
HTTP/REST
GET https://accounts.google.com/o/oauth2/v2/auth? client_id=your_client_id& response_type=code& state=state_parameter_passthrough_value& scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly%20https%3A//www.googleapis.com/auth/calendar.readonly& redirect_uri=https%3A//developers.google.com/oauthplayground& prompt=consent& include_granted_scopes=true
Memperbarui token akses (akses offline)
Token akses akan habis masa berlakunya secara berkala dan menjadi kredensial yang tidak valid untuk permintaan API terkait. Anda dapat memperbarui token akses tanpa meminta izin pengguna (termasuk saat pengguna tidak ada) jika Anda meminta akses offline ke cakupan yang terkait dengan token.
- Jika Anda menggunakan Library Klien Google API, objek klien akan memperbarui token akses sesuai kebutuhan selama Anda mengonfigurasi objek tersebut untuk akses offline.
- Jika Anda tidak menggunakan library klien, Anda harus menetapkan parameter kueri HTTP
access_typekeofflinesaat mengalihkan pengguna ke server OAuth 2.0 Google. Dalam hal ini, server otorisasi Google akan menampilkan token refresh saat Anda menukarkan kode otorisasi dengan token akses. Kemudian, jika masa berlaku token akses berakhir (atau kapan saja), Anda dapat menggunakan token refresh untuk mendapatkan token akses baru.
Meminta akses offline adalah persyaratan untuk aplikasi apa pun yang perlu mengakses Google API saat pengguna tidak ada. Misalnya, aplikasi yang menjalankan layanan pencadangan atau
mengeksekusi tindakan pada waktu yang telah ditentukan harus dapat memperbarui token aksesnya saat
pengguna tidak ada. Gaya akses default disebut online.
Aplikasi web sisi server, aplikasi terinstal, dan perangkat semuanya mendapatkan token refresh selama proses otorisasi. Token refresh biasanya tidak digunakan dalam aplikasi web sisi klien (JavaScript).
PHP
Jika aplikasi Anda memerlukan akses offline ke Google API, tetapkan jenis akses klien API ke
offline:
$client->setAccessType("offline");Setelah pengguna memberikan akses offline ke cakupan yang diminta, Anda dapat terus menggunakan klien API untuk mengakses Google API atas nama pengguna saat pengguna offline. Objek klien akan memperbarui token akses sesuai kebutuhan.
Python
Di Python, tetapkan argumen kata kunci access_type ke offline untuk memastikan
bahwa Anda dapat memperbarui token akses tanpa harus meminta izin pengguna
lagi. Sangat mungkin bahwa access_type tidak akan menjadi satu-satunya argumen kata kunci
yang Anda tetapkan, seperti yang ditunjukkan dalam contoh di bawah.
authorization_url, state = flow.authorization_url( # Enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Enable incremental authorization. Recommended as a best practice. include_granted_scopes='true')
Setelah pengguna memberikan akses offline ke cakupan yang diminta, Anda dapat terus menggunakan klien API untuk mengakses Google API atas nama pengguna saat pengguna offline. Objek klien akan memperbarui token akses sesuai kebutuhan.
Ruby
Jika aplikasi Anda memerlukan akses offline ke Google API, tetapkan jenis akses klien API ke
offline:
auth_client.update!( :additional_parameters => {"access_type" => "offline"} )
Setelah pengguna memberikan akses offline ke cakupan yang diminta, Anda dapat terus menggunakan klien API untuk mengakses Google API atas nama pengguna saat pengguna offline. Objek klien akan memperbarui token akses sesuai kebutuhan.
Node.js
Jika aplikasi Anda memerlukan akses offline ke Google API, tetapkan jenis akses klien API ke
offline:
const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined above. * Alternatively, if only one scope is needed, you can pass a scope URL as a string */ scope: scopes, // Enable incremental authorization. Recommended as a best practice. include_granted_scopes: true });
Setelah pengguna memberikan akses offline ke cakupan yang diminta, Anda dapat terus menggunakan klien API untuk mengakses Google API atas nama pengguna saat pengguna offline. Objek klien akan memperbarui token akses sesuai kebutuhan.
Token akses akan berakhir. Library ini akan otomatis menggunakan token refresh untuk mendapatkan token akses baru jika masa berlakunya akan segera berakhir. Cara mudah untuk memastikan Anda selalu menyimpan token terbaru adalah dengan menggunakan peristiwa token:
oauth2Client.on('tokens', (tokens) => { if (tokens.refresh_token) { // store the refresh_token in your secure persistent database console.log(tokens.refresh_token); } console.log(tokens.access_token); });
Peristiwa token ini hanya terjadi pada otorisasi pertama, dan Anda harus menetapkan
access_type ke offline saat memanggil metode generateAuthUrl
untuk menerima token refresh. Jika Anda telah memberikan izin yang diperlukan kepada aplikasi
tanpa menetapkan batasan yang sesuai untuk menerima token refresh, Anda harus
mengizinkan ulang aplikasi untuk menerima token refresh baru.
Untuk menetapkan refresh_token di lain waktu, Anda dapat menggunakan metode setCredentials:
oauth2Client.setCredentials({ refresh_token: `STORED_REFRESH_TOKEN` });
Setelah klien memiliki token refresh, token akses akan diperoleh dan diperbarui secara otomatis dalam panggilan berikutnya ke API.
HTTP/REST
Untuk memperbarui token akses, aplikasi Anda mengirimkan permintaan POST HTTPS
ke server otorisasi Google (https://oauth2.googleapis.com/token) yang
menyertakan parameter berikut dalam isi permintaan:
| Nama | Nilai |
|---|---|
client_id |
Client ID yang diperoleh dari Konsol API. |
client_secret |
Opsional
Rahasia klien yang diperoleh dari Konsol API. |
grant_type |
Seperti yang
ditentukan dalam
spesifikasi OAuth 2.0,
nilai kolom ini harus ditetapkan ke refresh_token. |
refresh_token |
Token refresh yang ditampilkan dari pertukaran kode otorisasi. |
Meskipun penggunaan DPoP bersifat opsional, sebaiknya gunakan untuk meningkatkan keamanan. Untuk menggunakan DPoP dengan token refresh, aplikasi Anda harus membuat JWT bukti DPoP baru yang unik untuk setiap permintaan ke endpoint token. Bukti ini harus ditandatangani dengan kunci pribadi yang sama dengan yang digunakan selama pertukaran kode otorisasi awal. Aplikasi Anda menambahkan bukti sebagai header permintaan HTTP.
| Header | Wajib | Deskripsi |
|---|---|---|
DPoP |
Opsional | Bukti DPoP adalah JWT yang membuktikan
kepemilikan kunci pribadi. Ini adalah header, bukan parameter. Jika disediakan, token yang ditampilkan akan terikat ke kunci ini. Bukti baru yang unik harus dibuat untuk setiap permintaan dan
harus menyertakan klaim htm (metode HTTP) dan htu (URI HTTP) yang
cocok dengan permintaan. |
Cuplikan berikut menunjukkan contoh permintaan:
POST /token HTTP/1.1 Host: oauth2.googleapis.com Content-Type: application/x-www-form-urlencoded DPoP: DPOP_PROOF_JWT client_id=your_client_id& refresh_token=refresh_token& grant_type=refresh_token
Untuk menggunakan DPoP dengan token refresh, Anda harus membuat JWT bukti DPoP baru yang unik untuk permintaan tersebut. Lihat Membuat bukti DPoP untuk mengetahui petunjuk langkah demi langkah dalam membuat pasangan kunci dan membuat JWT.
Pertukaran yang berhasil ditunjukkan dengan respons 200 OK
yang berisi token akses baru. Jika DPoP digunakan, token_type adalah Bearer. Respons yang berhasil mengonfirmasi
bahwa bukti DPoP untuk token refresh telah diterima. Google juga dapat menampilkan header HTTP
DPoP-Nonce baru dalam respons; jika ditampilkan, klien Anda harus meng-cache nonce ini
dan menyertakannya dalam klaim nonce bukti DPoP dalam permintaan berikutnya.
Pertukaran yang gagal terjadi jika header DPoP tidak ada, tidak valid, atau menggunakan kunci yang salah. Untuk mengetahui detail tentang kode error DPoP tertentu dan penanganan nonce, lihat Pertukaran gagal.
Cuplikan berikut menunjukkan contoh header respons dan isi yang berhasil saat DPoP digunakan:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
DPoP-Nonce: AN3XwJjZsjnb0ZuWkRlek8QU7wY-Zhf-5IP6tO0tORz0KgtDT1Bo8FX-w4nz3r5lnepI
{
"access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in": 3920,
"scope": "https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly",
"token_type": "Bearer"
}Perhatikan bahwa ada batasan jumlah token refresh yang akan dikeluarkan; satu batas per kombinasi klien/pengguna, dan batas lainnya per pengguna di semua klien. Anda harus menyimpan token refresh dalam penyimpanan jangka panjang dan terus menggunakannya selama masih valid. Jika aplikasi Anda meminta terlalu banyak token refresh, aplikasi tersebut mungkin akan mencapai batas ini, sehingga token refresh yang lebih lama akan berhenti berfungsi.
Pencabutan token
Dalam beberapa kasus, pengguna mungkin ingin mencabut akses yang diberikan ke aplikasi. Pengguna dapat mencabut akses dengan membuka Setelan Akun. Lihat bagian Menghapus akses situs atau aplikasi di Situs & aplikasi pihak ketiga yang memiliki akses ke akun Anda dalam dokumen dukungan untuk mengetahui informasi selengkapnya.
Aplikasi juga dapat mencabut akses yang diberikan kepadanya secara terprogram. Pencabutan terprogram penting dalam situasi saat pengguna berhenti berlangganan, menghapus aplikasi, atau resource API yang diperlukan oleh aplikasi telah berubah secara signifikan. Dengan kata lain, bagian dari proses penghapusan dapat mencakup permintaan API untuk memastikan izin yang sebelumnya diberikan ke aplikasi dihapus.
PHP
Untuk mencabut token secara terprogram, panggil revokeToken():
$client->revokeToken();Python
Untuk mencabut token secara terprogram, buat permintaan ke
https://oauth2.googleapis.com/revoke yang menyertakan token sebagai parameter dan menetapkan
header Content-Type:
requests.post('https://oauth2.googleapis.com/revoke', params={'token': credentials.token}, headers = {'content-type': 'application/x-www-form-urlencoded'})
Ruby
Untuk mencabut token secara terprogram, buat permintaan HTTP ke endpoint oauth2.revoke:
uri = URI('https://oauth2.googleapis.com/revoke') response = Net::HTTP.post_form(uri, 'token' => auth_client.access_token)
Token dapat berupa token akses atau token refresh. Jika token tersebut adalah token akses dan memiliki token refresh yang sesuai, token refresh juga akan dicabut.
Jika pencabutan berhasil diproses, kode status responsnya adalah
200. Untuk kondisi error, kode status 400 ditampilkan bersama dengan
kode error.
Node.js
Untuk mencabut token secara terprogram, buat permintaan POST HTTPS ke endpoint /revoke:
const https = require('https'); // Build the string for the POST request let postData = "token=" + userCredential.access_token; // Options for POST request to Google's OAuth 2.0 server to revoke a token let postOptions = { host: 'oauth2.googleapis.com', port: '443', path: '/revoke', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(postData) } }; // Set up the request const postReq = https.request(postOptions, function (res) { res.setEncoding('utf8'); res.on('data', d => { console.log('Response: ' + d); }); }); postReq.on('error', error => { console.log(error) }); // Post the request with data postReq.write(postData); postReq.end();
Parameter token dapat berupa token akses atau token refresh. Jika token tersebut adalah token akses dan memiliki token refresh yang sesuai, token refresh juga akan dicabut.
Jika pencabutan berhasil diproses, kode status responsnya adalah
200. Untuk kondisi error, kode status 400 ditampilkan bersama dengan
kode error.
HTTP/REST
Untuk mencabut token secara terprogram, aplikasi Anda membuat permintaan ke
https://oauth2.googleapis.com/revoke dan menyertakan token sebagai parameter:
curl -d -X -POST --header "Content-type:application/x-www-form-urlencoded" \
https://oauth2.googleapis.com/revoke?token={token}Token dapat berupa token akses atau token refresh. Jika token tersebut adalah token akses dan memiliki token refresh yang sesuai, token refresh juga akan dicabut.
Jika pencabutan berhasil diproses, kode status HTTP responsnya adalah
200. Untuk kondisi error, kode status HTTP 400 ditampilkan bersama
dengan kode error.
Akses berbasis waktu
Akses berbasis waktu memungkinkan pengguna memberi aplikasi Anda akses ke data mereka selama durasi terbatas untuk menyelesaikan tindakan. Akses berbasis waktu tersedia di produk Google tertentu selama alur izin, yang memberi pengguna opsi untuk memberikan akses selama jangka waktu terbatas. Contohnya adalah Data Portability API yang memungkinkan transfer data satu kali.
Saat pengguna memberikan akses berbasis waktu ke aplikasi Anda, masa berlaku token refresh akan berakhir setelah
durasi yang ditentukan. Perhatikan bahwa token refresh dapat dibatalkan lebih awal dalam keadaan tertentu; lihat kasus ini untuk mengetahui detailnya. Kolom refresh_token_expires_in yang ditampilkan dalam respons pertukaran kode otorisasi mewakili waktu yang tersisa hingga token refresh habis masa berlakunya dalam kasus tersebut.
Menerapkan Perlindungan Lintas Akun
Langkah tambahan yang harus Anda lakukan untuk melindungi akun pengguna adalah menerapkan Perlindungan Lintas Akun dengan memanfaatkan Layanan Perlindungan Lintas Akun Google. Layanan ini memungkinkan Anda berlangganan notifikasi peristiwa keamanan yang memberikan informasi kepada aplikasi Anda tentang perubahan besar pada akun pengguna. Kemudian, Anda dapat menggunakan informasi tersebut untuk mengambil tindakan, bergantung pada cara Anda memutuskan untuk merespons peristiwa.
Beberapa contoh jenis peristiwa yang dikirim ke aplikasi Anda oleh Layanan Perlindungan Lintas Akun Google adalah:
-
https://schemas.openid.net/secevent/risc/event-type/sessions-revoked -
https://schemas.openid.net/secevent/oauth/event-type/token-revoked -
https://schemas.openid.net/secevent/risc/event-type/account-disabled
Lihat halaman Melindungi akun pengguna dengan Perlindungan Lintas Akun untuk mengetahui informasi selengkapnya tentang cara menerapkan Perlindungan Lintas Akun dan daftar lengkap peristiwa yang tersedia.