Sử dụng OAuth 2.0 cho ứng dụng từ máy chủ đến máy chủ

Hệ thống Google OAuth 2.0 hỗ trợ các hoạt động tương tác từ máy chủ đến máy chủ, chẳng hạn như giữa ứng dụng web và dịch vụ của Google. Trong trường hợp này, bạn cần có tài khoản dịch vụ. Đây là tài khoản thuộc về ứng dụng của bạn thay vì của một người dùng cuối cá nhân. Ứng dụng của bạn gọi các API của Google thay mặt cho tài khoản dịch vụ, nên người dùng sẽ không trực tiếp tham gia. Tình huống này đôi khi được gọi là " OAuth cấp quyền truy cập" hoặc "2LO". (Thuật ngữ liên quan " OAuth ba bên" đề cập đến các tình huống trong đó ứng dụng của bạn thay mặt người dùng cuối gọi API Google, và trong một số trường hợp cần có sự đồng ý của người dùng.)

Thông thường, một ứng dụng sẽ dùng tài khoản dịch vụ khi ứng dụng đó dùng các API của Google để làm việc với dữ liệu của chính nó thay vì dữ liệu của người dùng. Ví dụ: một ứng dụng dùng Google Cloud Datastore để lưu trữ cố định dữ liệu sẽ sử dụng tài khoản dịch vụ để xác thực các lệnh gọi đến Google Cloud Datastore API.

Quản trị viên miền Google Workspace cũng có thể cấp cho tài khoản dịch vụ quyền trên toàn miền để thay mặt người dùng trong miền truy cập vào dữ liệu người dùng.

Tài liệu này mô tả cách một ứng dụng có thể hoàn thành quy trình OAuth 2.0 từ máy chủ đến máy chủ bằng cách sử dụng thư viện ứng dụng API của Google (nên dùng) hoặc HTTP.

Tổng quan

Để hỗ trợ các hoạt động tương tác từ máy chủ đến máy chủ, trước tiên, hãy tạo một tài khoản dịch vụ cho dự án của bạn trong API Console. Nếu bạn muốn truy cập vào dữ liệu người dùng của người dùng trong tài khoản Google Workspace, hãy uỷ quyền quyền truy cập trên toàn miền vào tài khoản dịch vụ.

Sau đó, ứng dụng chuẩn bị thực hiện lệnh gọi API được cho phép bằng cách sử dụng thông tin xác thực của tài khoản dịch vụ để yêu cầu mã truy cập từ máy chủ xác thực OAuth 2.0.

Cuối cùng, ứng dụng của bạn có thể sử dụng mã truy cập để gọi các API của Google.

Tạo một tài khoản dịch vụ

Thông tin xác thực của tài khoản dịch vụ bao gồm một địa chỉ email được tạo và dùng riêng biệt, có ít nhất một cặp khoá công khai/riêng tư. Nếu bạn bật tính năng uỷ quyền trên toàn miền, thì mã ứng dụng khách cũng sẽ có trong thông tin xác thực của tài khoản dịch vụ.

Nếu ứng dụng của bạn chạy trên Google App Engine, thì tài khoản dịch vụ sẽ tự động được thiết lập khi bạn tạo dự án.

Nếu ứng dụng của bạn chạy trên Google Compute Engine, thì tài khoản dịch vụ cũng sẽ tự động được thiết lập khi bạn tạo dự án, nhưng bạn phải chỉ định phạm vi mà ứng dụng của bạn cần truy cập khi bạn tạo một phiên bản Google Compute Engine. Để biết thêm thông tin, hãy xem phần Chuẩn bị một thực thể để sử dụng tài khoản dịch vụ.

Nếu ứng dụng của bạn không chạy trên Google App Engine hoặc Google Compute Engine, bạn phải có được những thông tin đăng nhập này trong Google API Console. Để tạo thông tin đăng nhập cho tài khoản dịch vụ hoặc xem thông tin xác thực công khai mà bạn đã tạo, hãy làm như sau:

Đầu tiên, tạo một tài khoản dịch vụ:

  1. Mở Service accounts page.
  2. If prompted, select a project, or create a new one.
  3. Nhấp vào Tạo tài khoản dịch vụ .
  4. Trong Chi tiết tài khoản dịch vụ , nhập tên, ID và mô tả cho tài khoản dịch vụ, sau đó nhấp vào Tạo và tiếp tục .
  5. Tùy chọn: Trong phần Cấp quyền truy cập tài khoản dịch vụ này cho dự án , hãy chọn vai trò IAM để cấp cho tài khoản dịch vụ.
  6. Nhấp vào Tiếp tục .
  7. Tùy chọn: Trong Cấp cho người dùng quyền truy cập vào tài khoản dịch vụ này , thêm người dùng hoặc nhóm được phép sử dụng và quản lý tài khoản dịch vụ.
  8. Nhấp vào Xong .

Tiếp theo, tạo khóa tài khoản dịch vụ:

  1. Nhấp vào địa chỉ email cho tài khoản dịch vụ mà bạn đã tạo.
  2. Nhấp vào tab Phím .
  3. Trong danh sách thả xuống Thêm khóa , chọn Tạo khóa mới .
  4. Nhấp vào Tạo .

Cặp khóa công khai/riêng tư mới của bạn được tạo và tải xuống máy của bạn; nó đóng vai trò là bản sao duy nhất của khóa riêng. Bạn có trách nhiệm lưu trữ nó một cách an toàn. Nếu bạn làm mất cặp khóa này, bạn sẽ phải tạo một cặp khóa mới.

Bạn có thể quay lại API Console bất cứ lúc nào để xem địa chỉ email, vân tay số của khoá công khai và các thông tin khác hoặc để tạo thêm các cặp khoá công khai/riêng tư. Để biết thêm thông tin chi tiết về thông tin đăng nhập tài khoản dịch vụ trong API Console, hãy xem mục Tài khoản dịch vụ trong tệp trợ giúp API Console.

Ghi lại địa chỉ email của tài khoản dịch vụ và lưu trữ tệp khoá riêng tư của tài khoản dịch vụ ở vị trí mà ứng dụng của bạn có thể truy cập được. Ứng dụng của bạn cần các mã này để thực hiện các lệnh gọi API được uỷ quyền.

Uỷ quyền trên toàn miền cho tài khoản dịch vụ

Khi sử dụng tài khoản Google Workspace, quản trị viên Workspace của tổ chức có thể cho phép một ứng dụng truy cập vào dữ liệu người dùng trên Workspace thay mặt cho người dùng trong miền Google Workspace. Ví dụ: một ứng dụng dùng API Lịch Google để thêm sự kiện vào lịch của tất cả người dùng trong miền Google Workspace sẽ thay mặt người dùng sử dụng tài khoản dịch vụ để truy cập vào API Lịch Google. Việc uỷ quyền cho tài khoản dịch vụ thay mặt người dùng trong một miền truy cập vào dữ liệu đôi khi được gọi là "uỷ quyền trên toàn miền" cho một tài khoản dịch vụ.

Để uỷ quyền trên toàn miền cho một tài khoản dịch vụ, quản trị viên cấp cao của miền Google Workspace phải hoàn tất các bước sau:

  1. Trong Bảng điều khiển dành cho quản trị viên của miền Google Workspace, hãy chuyển đến Trình đơn chính > Bảo mật > Quyền truy cập và kiểm soát dữ liệu > Chế độ kiểm soát API.
  2. Trong ngăn Ủy quyền trên toàn miền, hãy chọn Quản lý uỷ quyền trên toàn miền.
  3. Nhấp vào Thêm mới.
  4. Trong trường Mã khách hàng, hãy nhập Mã khách hàng của tài khoản dịch vụ. Bạn có thể tìm thấy mã ứng dụng khách của tài khoản dịch vụ trong Service accounts page.
  5. Trong trường Phạm vi OAuth (phân tách bằng dấu phẩy), hãy nhập danh sách các phạm vi mà ứng dụng của bạn muốn được cấp quyền truy cập. Ví dụ: nếu ứng dụng của bạn cần toàn quyền truy cập vào API Google Drive và API Lịch Google trên toàn miền, hãy nhập: https://www.googleapis.com/auth/drive, https://www.googleapis.com/auth/calendar.
  6. Nhấp vào Uỷ quyền.

Ứng dụng của bạn hiện có quyền thực hiện lệnh gọi API với tư cách người dùng trong miền Workspace của bạn (để "mạo danh" người dùng). Khi chuẩn bị thực hiện các lệnh gọi API được uỷ quyền này, bạn sẽ chỉ định rõ ràng rằng người dùng sẽ mạo danh.

Chuẩn bị thực hiện lệnh gọi API được uỷ quyền

Java

Sau khi bạn có được địa chỉ email ứng dụng và khoá riêng tư từ API Console, hãy dùng Thư viện ứng dụng API của Google cho Java để tạo đối tượng GoogleCredential từ thông tin xác thực của tài khoản dịch vụ và các phạm vi mà ứng dụng của bạn cần truy cập. Ví dụ:

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.services.sqladmin.SQLAdminScopes;

// ...

GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"))
    .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN));

Nếu đang phát triển một ứng dụng trên Google Cloud Platform, thì bạn có thể sử dụng thông tin đăng nhập mặc định của ứng dụng để đơn giản hoá quy trình xác thực.

Uỷ quyền thẩm quyền trên toàn miền

Nếu bạn đã uỷ quyền quyền truy cập vào tài khoản dịch vụ trên toàn miền và muốn mạo danh một tài khoản người dùng, hãy chỉ định địa chỉ email của tài khoản người dùng bằng phương thức createDelegated của đối tượng GoogleCredential. Ví dụ:

GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"))
    .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN))
    .createDelegated("workspace-user@example.com");

Mã ở trên sử dụng đối tượng GoogleCredential để gọi phương thức createDelegated(). Đối số cho phương thức createDelegated() phải là một người dùng thuộc tài khoản Workspace của bạn. Mã mà bạn thực hiện yêu cầu sẽ sử dụng thông tin xác thực này để gọi các API của Google bằng tài khoản dịch vụ của bạn.

Python

Sau khi bạn có được địa chỉ email ứng dụng và khoá riêng tư từ API Console, hãy sử dụng Thư viện ứng dụng API của Google cho Python để hoàn thành các bước sau:

  1. Tạo đối tượng Credentials từ thông tin xác thực của tài khoản dịch vụ và các phạm vi mà ứng dụng của bạn cần quyền truy cập. Ví dụ:
    from google.oauth2 import service_account
    
    SCOPES = ['https://www.googleapis.com/auth/sqlservice.admin']
    SERVICE_ACCOUNT_FILE = '/path/to/service.json'
    
    credentials = service_account.Credentials.from_service_account_file(
            SERVICE_ACCOUNT_FILE, scopes=SCOPES)

    Nếu đang phát triển một ứng dụng trên Google Cloud Platform, thì bạn có thể sử dụng thông tin đăng nhập mặc định của ứng dụng để đơn giản hoá quy trình xác thực.

  2. Uỷ quyền thẩm quyền trên toàn miền

    Nếu bạn đã uỷ quyền quyền truy cập vào tài khoản dịch vụ trên toàn miền và muốn mạo danh một tài khoản người dùng, hãy sử dụng phương thức with_subject của một đối tượng ServiceAccountCredentials hiện có. Ví dụ:

    delegated_credentials = credentials.with_subject('user@example.org')

Sử dụng đối tượng Thông tin xác thực để gọi các API của Google trong ứng dụng của bạn.

HTTP/REST

Sau khi bạn nhận được mã ứng dụng khách và khoá riêng tư từ API Console, ứng dụng của bạn cần hoàn tất các bước sau:

  1. Tạo một Mã thông báo web JSON (JWT, phát âm, "jot") bao gồm một tiêu đề, một nhóm thông báo xác nhận quyền sở hữu và một chữ ký.
  2. Yêu cầu mã truy cập từ Máy chủ uỷ quyền OAuth 2.0 của Google.
  3. Xử lý phản hồi JSON mà Máy chủ uỷ quyền trả về.

Các phần sau mô tả cách hoàn thành các bước này.

Nếu phản hồi chứa mã truy cập, thì bạn có thể dùng mã truy cập này để gọi một API của Google. (Nếu phản hồi không bao gồm mã truy cập, thì JWT và yêu cầu mã thông báo của bạn có thể không được định dạng đúng cách, hoặc tài khoản dịch vụ có thể không có quyền truy cập vào các phạm vi được yêu cầu.)

Khi mã thông báo truy cập hết hạn, ứng dụng của bạn sẽ tạo một JWT khác, ký mã này và yêu cầu mã truy cập khác.

Ứng dụng máy chủ của bạn dùng JWT để yêu cầu mã thông báo từ Máy chủ ủy quyền của Google, sau đó sử dụng mã thông báo này để gọi điểm cuối API của Google. Không liên quan đến người dùng cuối nào.

Phần còn lại của phần này mô tả chi tiết về việc tạo JWT, ký JWT, tạo yêu cầu mã truy cập và xử lý phản hồi.

Tạo JWT

JWT bao gồm 3 phần: tiêu đề, nhóm thông báo xác nhận quyền sở hữu và chữ ký. Tiêu đề và nhóm thông báo xác nhận quyền sở hữu là các đối tượng JSON. Các đối tượng JSON này được chuyển đổi tuần tự thành các byte UTF-8, sau đó được mã hoá bằng phương thức mã hoá Base64url. Phương thức mã hoá này đảm bảo khả năng thích ứng trước các thay đổi mã hoá do các thao tác mã hoá lặp lại. Tiêu đề, nhóm thông báo xác nhận quyền sở hữu và chữ ký được nối với nhau bằng ký tự dấu chấm (.).

JWT bao gồm như sau:

{Base64url encoded header}.{Base64url encoded claim set}.{Base64url encoded signature}

Chuỗi cơ sở cho chữ ký như sau:

{Base64url encoded header}.{Base64url encoded claim set}
Tạo tiêu đề JWT

Tiêu đề bao gồm ba trường cho biết thuật toán ký, định dạng của câu nhận định và [mã khoá của khoá tài khoản dịch vụ](https://cloud.google.com/iam/docs/reference/rest/v1/projects.serviceAccounts.keys) dùng để ký JWT. Thuật toán và định dạng là bắt buộc và mỗi trường chỉ có một giá trị. Khi các thuật toán và định dạng bổ sung được triển khai, tiêu đề này sẽ thay đổi tương ứng. Không bắt buộc phải có mã khoá. Nếu bạn chỉ định mã khoá không chính xác, GCP sẽ thử tất cả khoá liên kết với tài khoản dịch vụ để xác minh mã thông báo và từ chối mã thông báo nếu không tìm thấy khoá hợp lệ. Google giữ quyền từ chối những mã thông báo có mã khoá không chính xác trong tương lai.

Tài khoản dịch vụ phụ thuộc vào thuật toán RSA SHA-256 và định dạng mã thông báo JWT. Do đó, cách trình bày tiêu đề theo định dạng JSON như sau:

{"alg":"RS256","typ":"JWT", "kid":"370ab79b4513eb9bad7c9bd16a95cb76b5b2a56a"}

Sau đây là cách mô tả Base64url của việc này:

          eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsICJraWQiOiIzNzBhYjc5YjQ1MTNlYjliYWQ3YzliZDE2YTk1Y2I3NmI1YjJhNTZhIn0=
Tạo nhóm thông báo xác nhận quyền sở hữu JWT

Tập hợp xác nhận quyền sở hữu JWT chứa thông tin về JWT, bao gồm các quyền đang được yêu cầu (phạm vi), mục tiêu của mã thông báo, nhà phát hành, thời điểm phát hành mã thông báo và toàn thời gian tồn tại của mã thông báo. Hầu hết các trường đều là trường bắt buộc. Giống như tiêu đề JWT, tập hợp xác nhận quyền sở hữu JWT là một đối tượng JSON và được dùng để tính toán chữ ký.

Thông báo xác nhận quyền sở hữu bắt buộc

Các thông báo xác nhận quyền sở hữu bắt buộc trong nhóm thông báo xác nhận quyền sở hữu JWT được trình bày dưới đây. Các thông báo này có thể xuất hiện theo thứ tự bất kỳ trong nhóm thông báo xác nhận quyền sở hữu.

Tên Nội dung mô tả
iss Địa chỉ email của tài khoản dịch vụ.
scope Danh sách được phân tách bằng dấu cách gồm các quyền mà ứng dụng yêu cầu.
aud Phần mô tả mục tiêu dự kiến của câu nhận định. Khi tạo yêu cầu mã truy cập, giá trị này luôn là https://oauth2.googleapis.com/token.
exp Thời gian hết hạn của thông tin xác nhận, được chỉ định là giây kể từ 00:00:00 (giờ UTC), ngày 1 tháng 1 năm 1970. Giá trị này có tối đa 1 giờ sau thời gian phát hành.
iat Thời gian đưa ra khẳng định, được chỉ định là giây kể từ 00:00:00 (giờ UTC), ngày 1 tháng 1 năm 1970.

Dưới đây là minh hoạ JSON của các trường bắt buộc trong một nhóm thông báo xác nhận quyền sở hữu JWT:

{
  "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
  "scope": "https://www.googleapis.com/auth/devstorage.read_only",
  "aud": "https://oauth2.googleapis.com/token",
  "exp": 1328554385,
  "iat": 1328550785
}
Thông báo xác nhận quyền sở hữu bổ sung

Trong một số trường hợp doanh nghiệp, ứng dụng có thể sử dụng tính năng uỷ quyền trên toàn miền để hành động thay mặt cho một người dùng cụ thể trong một tổ chức. Quyền thực hiện loại mạo danh này phải được cấp trước khi ứng dụng có thể nhập vai người dùng. Quyền này thường do quản trị viên cấp cao xử lý. Để biết thêm thông tin, hãy xem phần Kiểm soát quyền truy cập vào API bằng tính năng uỷ quyền trên toàn miền.

Để lấy mã truy cập cấp cho một ứng dụng được uỷ quyền truy cập vào một tài nguyên, hãy đưa địa chỉ email của người dùng vào tập xác nhận quyền sở hữu JWT làm giá trị của trường sub.

Tên Nội dung mô tả
sub Địa chỉ email của người dùng mà ứng dụng đang yêu cầu quyền truy cập được uỷ quyền.

Nếu một ứng dụng không có quyền mạo danh người dùng, thì phản hồi cho yêu cầu mã truy cập có chứa trường sub sẽ là một lỗi.

Dưới đây là ví dụ về một tập hợp xác nhận quyền sở hữu JWT có trường sub:

{
  "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
  "sub": "some.user@example.com",
  "scope": "https://www.googleapis.com/auth/prediction",
  "aud": "https://oauth2.googleapis.com/token",
  "exp": 1328554385,
  "iat": 1328550785
}
Mã hoá nhóm xác nhận quyền sở hữu JWT

Giống như tiêu đề JWT, nhóm thông báo xác nhận quyền sở hữu JWT phải được chuyển đổi tuần tự thành UTF-8 và Base64url-safe được mã hoá. Dưới đây là ví dụ về nội dung trình bày JSON cho một tập hợp Thông báo xác nhận quyền sở hữu JWT:

{
  "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
  "scope": "https://www.googleapis.com/auth/prediction",
  "aud": "https://oauth2.googleapis.com/token",
  "exp": 1328554385,
  "iat": 1328550785
}
Tính toán chữ ký

Chữ ký web JSON (JWS) là thông số kỹ thuật hướng dẫn cơ chế tạo chữ ký cho JWT. Đầu vào cho chữ ký là mảng byte của nội dung sau:

{Base64url encoded header}.{Base64url encoded claim set}

Phải sử dụng thuật toán ký trong tiêu đề JWT khi tính toán chữ ký. Thuật toán ký duy nhất được Máy chủ uỷ quyền OAuth 2.0 của Google hỗ trợ là RSA sử dụng thuật toán băm SHA-256. Giá trị này được biểu thị dưới dạng RS256 trong trường alg trong tiêu đề JWT.

Ký ký hiệu UTF-8 của dữ liệu đầu vào bằng SHA256withRSA (còn gọi là RSASSA-PKCS1-V1_5-SIGN bằng hàm băm SHA-256) bằng khoá riêng tư lấy từ Google API Console. Dữ liệu đầu ra sẽ là một mảng byte.

Chữ ký khi đó phải được mã hoá Base64url. Tiêu đề, nhóm thông báo xác nhận quyền sở hữu và chữ ký được nối với nhau bằng ký tự dấu chấm (.). Kết quả là JWT. Nội dung phải như sau (đã thêm dấu ngắt dòng để cho rõ ràng):

{Base64url encoded header}.
{Base64url encoded claim set}.
{Base64url encoded signature}

Dưới đây là ví dụ về JWT trước khi mã hoá Base64url:

{"alg":"RS256","typ":"JWT"}.
{
"iss":"761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
"scope":"https://www.googleapis.com/auth/prediction",
"aud":"https://oauth2.googleapis.com/token",
"exp":1328554385,
"iat":1328550785
}.
[signature bytes]

Dưới đây là ví dụ về một JWT đã được ký và sẵn sàng để truyền:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL29hdXRoMi92NC90b2tlbiIsImV4cCI6MTMyODU1NDM4NSwiaWF0IjoxMzI4NTUwNzg1fQ.UFUt59SUM2_AW4cRU8Y0BYVQsNTo4n7AFsNrqOpYiICDu37vVt-tw38UKzjmUKtcRsLLjrR3gFW3dNDMx_pL9DVjgVHDdYirtrCekUHOYoa1CMR66nxep5q5cBQ4y4u2kIgSvChCTc9pmLLNoIem-ruCecAJYgI9Ks7pTnW1gkOKs0x3YpiLpzplVHAkkHztaXiJdtpBcY1OXyo6jTQCa3Lk2Q3va1dPkh_d--GU2M5flgd8xNBPYw4vxyt0mP59XZlHMpztZt0soSgObf7G3GXArreF_6tpbFsS3z2t5zkEiHuWJXpzcYr5zWTRPDEHsejeBSG8EgpLDce2380ROQ

Tạo yêu cầu mã truy cập

Sau khi tạo JWT đã ký, ứng dụng có thể dùng JWT này để yêu cầu mã truy cập. Yêu cầu mã truy cập này là yêu cầu HTTPS POST và phần nội dung được mã hoá URL. URL sẽ hiển thị dưới đây:

https://oauth2.googleapis.com/token

Các tham số sau đây là bắt buộc trong yêu cầu POST HTTPS:

Tên Nội dung mô tả
grant_type Sử dụng chuỗi sau, được mã hoá URL nếu cần: urn:ietf:params:oauth:grant-type:jwt-bearer
assertion JWT, bao gồm cả chữ ký.

Dưới đây là tệp kết xuất thô của yêu cầu HTTPS POST được dùng trong yêu cầu mã truy cập:

POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU3MzM4MSwiaWF0IjoxMzI4NTY5NzgxfQ.ixOUGehweEVX_UKXv5BbbwVEdcz6AYS-6uQV6fGorGKrHf3LIJnyREw9evE-gs2bmMaQI5_UbabvI4k-mQE4kBqtmSpTzxYBL1TCd7Kv5nTZoUC1CmwmWCFqT9RE6D7XSgPUh_jF1qskLa2w0rxMSjwruNKbysgRNctZPln7cqQ

Dưới đây là yêu cầu tương tự, sử dụng curl:

curl -d 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU3MzM4MSwiaWF0IjoxMzI4NTY5NzgxfQ.RZVpzWygMLuL-n3GwjW1_yhQhrqDacyvaXkuf8HcJl8EtXYjGjMaW5oiM5cgAaIorrqgYlp4DPF_GuncFqg9uDZrx7pMmCZ_yHfxhSCXru3gbXrZvAIicNQZMFxrEEn4REVuq7DjkTMyCMGCY1dpMa8aWfTQFt3Eh7smLchaZsU
' https://oauth2.googleapis.com/token

Xử lý phản hồi

Nếu yêu cầu JWT và mã truy cập được định dạng đúng cách và tài khoản dịch vụ có quyền thực hiện thao tác này, thì phản hồi JSON từ Máy chủ uỷ quyền sẽ bao gồm một mã truy cập. Sau đây là ví dụ về phản hồi:

{
  "access_token": "1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M",
  "scope": "https://www.googleapis.com/auth/prediction"
  "token_type": "Bearer",
  "expires_in": 3600
}

Bạn có thể sử dụng lại mã truy cập trong khoảng thời gian do giá trị expires_in chỉ định.

Gọi API của Google

Java

Sử dụng đối tượng GoogleCredential để gọi API của Google bằng cách hoàn tất các bước sau:

  1. Tạo một đối tượng dịch vụ cho API mà bạn muốn gọi bằng đối tượng GoogleCredential. Ví dụ:
    SQLAdmin sqladmin =
        new SQLAdmin.Builder(httpTransport, JSON_FACTORY, credential).build();
  2. Gửi yêu cầu đến dịch vụ API bằng cách sử dụng giao diện do đối tượng dịch vụ cung cấp. Ví dụ: để liệt kê các thực thể của cơ sở dữ liệu Cloud SQL trong dự án thú vị-example-123:
    SQLAdmin.Instances.List instances =
        sqladmin.instances().list("exciting-example-123").execute();

Python

Sử dụng đối tượng Credentials được uỷ quyền để gọi các API của Google bằng cách hoàn tất các bước sau:

  1. Tạo một đối tượng dịch vụ cho API mà bạn muốn gọi. Bạn sẽ tạo một đối tượng dịch vụ bằng cách gọi hàm build kèm theo tên và phiên bản của API cũng như đối tượng Credentials được uỷ quyền. Ví dụ: để gọi phiên bản 1beta3 của API Quản trị Cloud SQL:
    import googleapiclient.discovery
    
    sqladmin = googleapiclient.discovery.build('sqladmin', 'v1beta3', credentials=credentials)
  2. Gửi yêu cầu đến dịch vụ API bằng cách sử dụng giao diện do đối tượng dịch vụ cung cấp. Ví dụ: để liệt kê các thực thể của cơ sở dữ liệu Cloud SQL trong dự án thú vị-example-123:
    response = sqladmin.instances().list(project='exciting-example-123').execute()

HTTP/REST

Sau khi ứng dụng của bạn nhận được mã truy cập, bạn có thể dùng mã này để thay mặt một tài khoản dịch vụ hoặc tài khoản người dùng cụ thể thực hiện lệnh gọi đến một API Google nếu(các) phạm vi truy cập mà API yêu cầu đã được cấp. Để thực hiện việc này, hãy đưa mã truy cập vào một yêu cầu đối với API bằng cách cung cấp tham số truy vấn access_token hoặc giá trị tiêu đề HTTP Authorization Bearer. Khi có thể, bạn nên sử dụng tiêu đề HTTP vì chuỗi truy vấn có xu hướng xuất hiện trong nhật ký máy chủ. Trong hầu hết các trường hợp, bạn có thể sử dụng thư viện ứng dụng để thiết lập lệnh gọi tới các API của Google (ví dụ: khi gọi API Tệp trên Drive).

Bạn có thể dùng thử tất cả các API của Google và xem các phạm vi của các API đó tại Playground 2.0.

Ví dụ về HTTP GET

Lệnh gọi đến điểm cuối drive.files (API Tệp trên Drive) sử dụng tiêu đề HTTP Authorization: Bearer có thể có dạng như sau. Lưu ý rằng bạn cần chỉ định mã truy cập của riêng mình:

GET /drive/v2/files HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer access_token

Dưới đây là lệnh gọi tới cùng một API cho người dùng đã xác thực bằng tham số chuỗi truy vấn access_token:

GET https://www.googleapis.com/drive/v2/files?access_token=access_token

Ví dụ về curl

Bạn có thể kiểm thử các lệnh này bằng ứng dụng dòng lệnh curl. Dưới đây là ví dụ về cách sử dụng tuỳ chọn tiêu đề HTTP (ưu tiên):

curl -H "Authorization: Bearer access_token" https://www.googleapis.com/drive/v2/files

Hoặc một cách khác là tuỳ chọn tham số chuỗi truy vấn:

curl https://www.googleapis.com/drive/v2/files?access_token=access_token

Khi mã truy cập hết hạn

Mã truy cập do Máy chủ uỷ quyền OAuth 2.0 của Google cấp sẽ hết hạn sau khoảng thời gian do giá trị expires_in cung cấp. Khi một mã truy cập hết hạn, ứng dụng sẽ tạo một JWT khác, ký tên đó rồi yêu cầu một mã truy cập khác.

Mã lỗi JWT

error trường error_description trường Ý nghĩa Cách giải quyết
unauthorized_client Unauthorized client or scope in request. Nếu bạn đang cố sử dụng tính năng uỷ quyền trên toàn miền, thì tài khoản dịch vụ chưa được uỷ quyền trong Bảng điều khiển dành cho quản trị viên của miền của người dùng.

Đảm bảo rằng tài khoản dịch vụ được uỷ quyền trong trang Ủy quyền trên toàn miền của Bảng điều khiển dành cho quản trị viên cho người dùng trong trường (trường) xác nhận quyền sở hữu sub.

Tuy thường mất vài phút, nhưng quá trình uỷ quyền có thể mất tới 24 giờ mới có hiệu lực cho tất cả người dùng trong Tài khoản Google của bạn.

unauthorized_client Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested. Một tài khoản dịch vụ được uỷ quyền bằng địa chỉ email của khách hàng thay vì mã ứng dụng khách (dạng số) trong Bảng điều khiển dành cho quản trị viên. Trên trang Uỷ quyền trên toàn miền trong Bảng điều khiển dành cho quản trị viên, hãy xoá ứng dụng rồi thêm lại ứng dụng đó với mã dạng số.
access_denied (giá trị bất kỳ) Nếu bạn đang sử dụng tính năng uỷ quyền trên toàn miền, thì một hoặc nhiều phạm vi yêu cầu sẽ không được uỷ quyền trong Bảng điều khiển dành cho quản trị viên.

Đảm bảo rằng tài khoản dịch vụ đã được uỷ quyền trên trang Ủy quyền trên toàn miền của Bảng điều khiển dành cho quản trị viên cho người dùng trong thông báo xác nhận quyền sở hữu sub (trường) và tài khoản đó có tất cả các phạm vi mà bạn đang yêu cầu trong thông báo xác nhận quyền sở hữu scope của JWT.

Tuy thường mất vài phút, nhưng quá trình uỷ quyền có thể mất tới 24 giờ mới có hiệu lực cho tất cả người dùng trong Tài khoản Google của bạn.

admin_policy_enforced (giá trị bất kỳ) Tài khoản Google này không thể uỷ quyền cho một hoặc nhiều phạm vi theo yêu cầu do chính sách của quản trị viên Google Workspace của tài khoản đó.

Xem bài viết trợ giúp dành cho Quản trị viên Google Workspace Kiểm soát những ứng dụng nội bộ và ứng dụng bên thứ ba nào truy cập vào dữ liệu Google Workspace để biết thêm thông tin về cách quản trị viên có thể hạn chế quyền truy cập vào tất cả các phạm vi hoặc các phạm vi nhạy cảm và bị hạn chế cho đến khi được cấp rõ ràng quyền truy cập cho mã ứng dụng khách OAuth của mình.

invalid_client (giá trị bất kỳ)

Ứng dụng OAuth hoặc mã thông báo JWT không hợp lệ hoặc được định cấu hình không chính xác.

Hãy tham khảo nội dung mô tả lỗi để biết thông tin chi tiết.

Đảm bảo mã thông báo JWT hợp lệ và chứa thông báo xác nhận quyền sở hữu chính xác.

Hãy kiểm tra để đảm bảo rằng tài khoản ứng dụng và tài khoản dịch vụ OAuth được định cấu hình chính xác và bạn đang sử dụng đúng địa chỉ email.

Kiểm tra để đảm bảo rằng mã thông báo JWT là chính xác và mã này được cấp cho mã ứng dụng khách trong yêu cầu.

invalid_grant Not a valid email. Người dùng không tồn tại. Kiểm tra để đảm bảo địa chỉ email trong (trường) thông báo xác nhận quyền sở hữu sub là chính xác.
invalid_grant

Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your 'iat' and 'exp' values and use a clock with skew to account for clock differences between systems.

Thông thường, giá trị này có nghĩa là giờ địa phương của hệ thống không chính xác. Điều này cũng có thể xảy ra nếu giá trị exp cách giá trị iat cách đây hơn 65 phút, hoặc giá trị exp thấp hơn giá trị iat.

Đảm bảo đồng hồ trên hệ thống nơi JWT được tạo là chính xác. Nếu cần, hãy đồng bộ hoá thời gian của bạn với Google NTP.

invalid_grant Invalid JWT Signature.

Xác nhận JWT được ký bằng một khoá riêng tư không liên kết với tài khoản dịch vụ được xác định trong email ứng dụng khách hoặc khoá dùng để sử dụng đã bị xoá, vô hiệu hoá hoặc đã hết hạn.

Ngoài ra, câu nhận định JWT có thể được mã hoá không chính xác – phải được mã hoá Base64, không có các dòng mới hoặc dấu bằng khoảng đệm.

Giải mã tập hợp xác nhận quyền sở hữu JWT và xác minh khoá đã ký câu nhận định được liên kết với tài khoản dịch vụ.

Hãy thử dùng thư viện OAuth do Google cung cấp để đảm bảo JWT được tạo chính xác.

invalid_scope Invalid OAuth scope or ID token audience provided. Chưa yêu cầu phạm vi nào (danh sách phạm vi trống) hoặc một trong các phạm vi được yêu cầu không tồn tại (nghĩa là không hợp lệ).

Đảm bảo rằng thông báo xác nhận quyền sở hữu scope (trường) của JWT được điền sẵn, đồng thời so sánh các phạm vi trong JWT với các phạm vi được ghi nhận trong tài liệu cho những API mà bạn muốn sử dụng, để đảm bảo không có lỗi hoặc lỗi chính tả.

Xin lưu ý rằng danh sách phạm vi trong thông báo xác nhận quyền sở hữu scope cần được phân tách bằng dấu cách, chứ không phải dấu phẩy.

disabled_client The OAuth client was disabled. Khoá dùng để ký câu nhận định JWT đã bị tắt.

Truy cập vào Google API Consolevà trong phần IAM & Admin > Service Accounts (IAM và Quản trị viên > Tài khoản dịch vụ), hãy bật tài khoản dịch vụ có chứa "Key ID" (Mã khoá) dùng để ký câu nhận định.

org_internal This client is restricted to users within its organization. Mã ứng dụng khách OAuth trong yêu cầu là một phần của dự án giới hạn quyền truy cập vào Tài khoản Google trong một tổ chức Google Cloud cụ thể.

Hãy sử dụng tài khoản dịch vụ của tổ chức để xác thực. Xác nhận cấu hình loại người dùng cho ứng dụng OAuth.

Phụ lục: Uỷ quyền tài khoản dịch vụ không có OAuth

Với một số API của Google, bạn có thể thực hiện lệnh gọi API được uỷ quyền bằng cách sử dụng JWT đã ký trực tiếp dưới dạng mã thông báo truy cập, thay vì mã truy cập OAuth 2.0. Khi có thể, bạn có thể tránh phải gửi yêu cầu mạng đến máy chủ uỷ quyền của Google trước khi thực hiện lệnh gọi API.

Nếu API bạn muốn gọi đã phát hành định nghĩa dịch vụ trong kho lưu trữ API Google trên GitHub, thì bạn có thể thực hiện các lệnh gọi API được uỷ quyền bằng cách sử dụng JWT thay vì mã truy cập. Cách làm như sau:

  1. Tạo tài khoản dịch vụ như mô tả ở trên. Hãy nhớ giữ lại tệp JSON mà bạn nhận được khi tạo tài khoản.
  2. Bạn có thể sử dụng thư viện JWT tiêu chuẩn bất kỳ, chẳng hạn như thư viện JWT có tại jwt.io, hãy tạo một JWT có tiêu đề và tải trọng như ví dụ sau:
    {
      "alg": "RS256",
      "typ": "JWT",
      "kid": "abcdef1234567890"
    }
    .
    {
      "iss": "123456-compute@developer.gserviceaccount.com",
      "sub": "123456-compute@developer.gserviceaccount.com",
      "aud": "https://firestore.googleapis.com/",
      "iat": 1511900000,
      "exp": 1511903600
    }
    • Đối với trường kid trong tiêu đề, hãy chỉ định mã khoá riêng tư của tài khoản dịch vụ. Bạn có thể tìm thấy giá trị này trong trường private_key_id của tệp JSON tài khoản dịch vụ.
    • Đối với các trường isssub, hãy chỉ định địa chỉ email của tài khoản dịch vụ. Bạn có thể tìm thấy giá trị này trong trường client_email của tệp JSON tài khoản dịch vụ.
    • Đối với trường aud, hãy chỉ định điểm cuối API. Ví dụ: https://SERVICE.googleapis.com/.
    • Đối với trường iat, hãy chỉ định thời gian Unix hiện tại, còn đối với trường exp, hãy chỉ định thời gian chính xác là 3600 giây sau đó, khi JWT sẽ hết hạn.

Ký JWT bằng RSA-256 bằng khoá riêng tư có trong tệp JSON của tài khoản dịch vụ của bạn.

Ví dụ:

Java

Sử dụng google-api-java-clientjava-jwt:

GoogleCredential credential =
        GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"));
PrivateKey privateKey = credential.getServiceAccountPrivateKey();
String privateKeyId = credential.getServiceAccountPrivateKeyId();

long now = System.currentTimeMillis();

try {
    Algorithm algorithm = Algorithm.RSA256(null, privateKey);
    String signedJwt = JWT.create()
        .withKeyId(privateKeyId)
        .withIssuer("123456-compute@developer.gserviceaccount.com")
        .withSubject("123456-compute@developer.gserviceaccount.com")
        .withAudience("https://firestore.googleapis.com/")
        .withIssuedAt(new Date(now))
        .withExpiresAt(new Date(now + 3600 * 1000L))
        .sign(algorithm);
} catch ...

Python

Sử dụng PyJWT:

iat = time.time()
exp = iat + 3600
payload = {'iss': '123456-compute@developer.gserviceaccount.com',
           'sub': '123456-compute@developer.gserviceaccount.com',
           'aud': 'https://firestore.googleapis.com/',
           'iat': iat,
           'exp': exp}
additional_headers = {'kid': PRIVATE_KEY_ID_FROM_JSON}
signed_jwt = jwt.encode(payload, PRIVATE_KEY_FROM_JSON, headers=additional_headers,
                       algorithm='RS256')
  1. Gọi API, sử dụng JWT đã ký làm mã thông báo truy cập:
    GET /v1/projects/abc/databases/123/indexes HTTP/1.1
    Authorization: Bearer SIGNED_JWT
    Host: firestore.googleapis.com