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 giữa máy chủ với máy chủ, chẳng hạn như giữa một ứng dụng web và một dịch vụ của Google. Đối với trường hợp này, bạn cần có một tài khoản dịch vụ. Đây là tài khoản thuộc về ứng dụng của bạn chứ không phải thuộc về người dùng cuối cá nhân. Ứng dụng của bạn gọi API của Google thay cho tài khoản dịch vụ, vì vậy, người dùng không trực tiếp tham gia. Trường hợp này đôi khi được gọi là "OAuth hai chân" hoặc "2LO". (Thuật ngữ liên quan "OAuth ba chân" đề cập đến các trường hợp mà ứng dụng của bạn gọi API Google thay mặt cho người dùng cuối và trong đó đôi khi cần có sự đồng ý của người dùng.)

Thông thường, một ứng dụng sẽ sử dụng tài khoản dịch vụ khi ứng dụng đó dùng API của Google để xử lý dữ liệu của chính ứng dụng thay vì dữ liệu của người dùng. Ví dụ: một ứng dụng sử dụng Google Cloud Datastore để duy trì dữ liệu sẽ sử dụng một 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 để truy cập vào dữ liệu người dùng thay mặt cho người dùng trong miền.

Tài liệu này mô tả cách một ứng dụng có thể hoàn tất 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 Google API (nên dùng) hoặc HTTP.

Tổng quan

Để hỗ trợ các hoạt động tương tác giữa các 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 cho người dùng trong tài khoản Google Workspace của mình, hãy uỷ quyền truy cập trên toàn miền cho tài khoản dịch vụ.

Sau đó, ứng dụng của bạn chuẩn bị thực hiện các lệnh gọi API được uỷ quyền 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ủ uỷ quyền 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 là duy nhất và í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 là một phần của thông tin đăng nhập 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ẽ được thiết lập tự động 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ẽ được thiết lập tự động khi bạn tạo dự án, nhưng bạn phải chỉ định các phạm vi mà ứng dụng của bạn cần có quyề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 bài viết Chuẩn bị một phiên bản để 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, thì bạn phải lấy những thông tin đăng nhập này trong Google API Console. Để tạo thông tin xác thực 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 biểu tượng API Console bất cứ lúc nào để xem địa chỉ email, dấu vân tay 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 về thông tin đăng nhập tài khoản dịch vụ trong API Console, hãy xem 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ụ ở một vị trí mà ứng dụng của bạn có thể truy cập. Ứng dụng của bạn cần các khoá 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ể uỷ quyền cho một ứng dụng truy cập vào dữ liệu người dùng Workspace thay mặt cho người dùng trong miền Google Workspace. Ví dụ: một ứng dụng sử dụng Google Calendar API để thêm sự kiện vào lịch của tất cả người dùng trong miền Google Workspace sẽ sử dụng tài khoản dịch vụ để truy cập Google Calendar API thay mặt cho người dùng. Việc uỷ quyền cho một tài khoản dịch vụ truy cập vào dữ liệu thay mặt cho người dùng trong một miền đô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 kiểm soát truy cập và dữ liệu > Quyền kiểm soát API.
  2. Trong ngăn Uỷ quyền trên toàn miền, hãy chọn Quản lý việc uỷ quyền trên toàn miền.
  3. Nhấp vào Thêm mới.
  4. Trong trường Mã ứng dụng khách, hãy nhập Mã ứng dụng khách 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 sẽ được cấp quyền truy cập. Ví dụ: nếu ứng dụng của bạn cần có quyền truy cập đầy đủ trên toàn miền vào Google Drive API và Google Calendar API, hãy nhập: https://www.googleapis.com/auth/drive, https://www.googleapis.com/auth/calendar.
  6. Nhấp vào Uỷ quyền.

Giờ đây, ứng dụng của bạn có quyền thực hiện các lệnh gọi API dưới danh nghĩa 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 người dùng cần mạo danh.

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

Java

Sau khi bạn lấy địa chỉ email và khoá riêng tư của ứng dụng khách từ API Console, hãy dùng Thư viện xác thực của Google cho Java để tạo một đối tượng GoogleCredentials từ thông tin đăng nhập 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.auth.oauth2.GoogleCredentials;
import com.google.api.services.sqladmin.SQLAdminScopes;

// ...

GoogleCredentials credentials = GoogleCredentials.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, bạn có thể sử dụng thông tin xác thực mặc định của ứng dụng thay thế. Thông tin này có thể đơn giản hoá quy trình.

Uỷ quyền trên toàn miền

Nếu bạn đã uỷ quyền truy cập trên toàn miền cho tài khoản dịch vụ 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 GoogleCredentials. Ví dụ:

GoogleCredentials credentials = GoogleCredentials.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 GoogleCredentials để 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ã của 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 lấy địa chỉ email và khoá riêng tư của ứng dụng từ API Console, hãy dùng Thư viện ứng dụng API của Google cho Python để hoàn tất các bước sau:

  1. Tạo một đố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 có 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, bạn có thể sử dụng thông tin xác thực mặc định của ứng dụng thay vì thông tin xác thực mặc định của người dùng để đơn giản hoá quy trình.

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

    Nếu bạn đã uỷ quyền truy cập trên toàn miền cho tài khoản dịch vụ 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 đăng nhập để 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 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 là "jot") bao gồm tiêu đề, một tập hợp các xác nhận quyền sở hữu và 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 đây mô tả cách hoàn tất những bước này.

Nếu phản hồi có mã truy cập, bạn có thể dùng mã truy cập đó để gọi một API của Google. (Nếu phản hồi không bao gồm mã thông báo truy cập, thì JWT và yêu cầu mã thông báo của bạn có thể không được tạo đú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ã truy cập hết hạn, ứng dụng của bạn sẽ tạo một JWT khác, ký tên và yêu cầu một mã truy cập khác.

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

Phần còn lại của phần này mô tả cụ thể 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 đề, tập hợp các khai báo và chữ ký. Tiêu đề và tập hợp 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 giúp chống lại các thay đổi về phương thức mã hoá do các thao tác mã hoá lặp lại. Tiêu đề, bộ 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 được tạo 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 đề này bao gồm 3 trường cho biết thuật toán ký, định dạng của câu khẳng định và [khoá nhận dạng khoá tài khoản dịch vụ](https://cloud.google.com/iam/docs/reference/rest/v1/projects.serviceAccounts.keys) được dùng để ký JWT. Bạn phải cung cấp thuật toán và định dạng, đồng thời mỗi trường chỉ có một giá trị. Khi các thuật toán và định dạng khác được giới thiệu, tiêu đề này sẽ thay đổi cho phù hợp. Mã khoá là không bắt buộc và nếu bạn chỉ định một Mã khoá không chính xác, thì GCP sẽ thử tất cả các khoá được 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 có quyền từ chối các 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ụ dựa vào thuật toán RSA SHA-256 và định dạng mã thông báo JWT. Do đó, biểu diễn JSON của tiêu đề sẽ như sau:

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

Sau đây là biểu thị Base64url của khoá này:

          eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsICJraWQiOiIzNzBhYjc5YjQ1MTNlYjliYWQ3YzliZDE2YTk1Y2I3NmI1YjJhNTZhIn0=
Tạo bộ xác nhận quyền sở hữu JWT

Tập hợp các xác nhận quyền sở hữu JWT chứa thông tin về JWT, bao gồm cả các quyền được yêu cầu (phạm vi), mục tiêu của mã thông báo, tổ chức phát hành, thời gian phát hành mã thông báo và 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 trong quá trình 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 khai báo bắt buộc trong tập hợp khai báo JWT được trình bày dưới đây. Chúng có thể xuất hiện theo thứ tự bất kỳ trong tập hợp yêu cầu bản quyền.

Tên Mô tả
iss Địa chỉ email của tài khoản dịch vụ.
scope Danh sách các quyền mà ứng dụng yêu cầu, được phân tách bằng dấu cách.
aud Một giá trị mô tả mục tiêu dự kiến của câu khẳng định. Khi đưa ra 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 câu khẳng định, được chỉ định là số giây kể từ 00:00:00 giờ UTC ngày 1 tháng 1 năm 1970. Giá trị này có thời hạn tối đa là 1 giờ sau thời gian phát hành.
iat Thời gian phát hành câu khẳng định, được chỉ định là số giây kể từ 00:00:00 giờ UTC, ngày 1 tháng 1 năm 1970.

Sau đây là biểu thị JSON của các trường bắt buộc trong một tập hợp các khai báo 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 khác

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 để thay mặt cho một người dùng cụ thể trong tổ chức. Bạn phải được cấp quyền thực hiện loại hoạt động mạo danh này trước khi ứng dụng có thể mạo danh người dùng và thường do quản trị viên cấp cao xử lý. Để biết thêm thông tin, hãy xem bài viết Kiểm soát quyền truy cập 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 ứng dụng quyền truy cập được uỷ quyền vào một tài nguyên, hãy thêm địa chỉ email của người dùng vào tập hợp xác nhận quyền sở hữu JWT làm giá trị của trường sub.

Tên 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 nhập vai người dùng, thì phản hồi cho yêu cầu mã truy cập bao gồm trường sub sẽ là lỗi.

Dưới đây là ví dụ về một tập hợp các xác nhận quyền sở hữu JWT có chứa 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á bộ xác nhận quyền sở hữu JWT

Giống như tiêu đề JWT, tập hợp các khai báo JWT phải được chuyển đổi tuần tự thành UTF-8 và được mã hoá an toàn bằng Base64url. Dưới đây là ví dụ về một biểu thị JSON của JWT Claim set:

{
  "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à quy cách 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}

Bạn 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 mà Máy chủ uỷ quyền Google OAuth 2.0 hỗ trợ là RSA bằng thuật toán băm SHA-256. Điều này được thể hiện dưới dạng RS256 trong trường alg trong tiêu đề JWT.

Ký biểu thị UTF-8 của dữ liệu đầu vào bằng SHA256withRSA (còn gọi là RSASSA-PKCS1-V1_5-SIGN với hàm băm SHA-256) bằng khoá riêng tư nhận được từ Google API Console. Kết quả đầu ra sẽ là một mảng byte.

Sau đó, bạn phải mã hoá chữ ký bằng Base64url. Tiêu đề, bộ 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 này phải là nội dung sau (đã thêm dấu ngắt dòng để rõ ràng hơn):

{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

Đưa ra yêu cầu về mã truy cập

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

https://oauth2.googleapis.com/token

Bạn phải có các thông số sau trong yêu cầu HTTPS POST:

Tên Mô tả
grant_type Sử dụng chuỗi sau, 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à một bản 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 JWT và yêu cầu mã truy cập được tạo đúng cách và tài khoản dịch vụ có quyền thực hiện thao tác, thì phản hồi JSON từ Máy chủ uỷ quyền sẽ bao gồm mã truy cập. Sau đây là một 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ể dùng lại mã truy cập trong khoảng thời gian do giá trị expires_in chỉ định.

Gọi các API của Google

Java

Sử dụng đối tượng GoogleCredentials để 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ằng đối tượng GoogleCredentials. Ví dụ:
    SQLAdmin sqladmin =
        new SQLAdmin.Builder(httpTransport, JSON_FACTORY, credentials).build();
  2. Đưa ra yêu cầu cho 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 phiên bản cơ sở dữ liệu Cloud SQL trong dự án exciting-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 tạo một đối tượng dịch vụ bằng cách gọi hàm build bằng 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. Đưa ra yêu cầu cho 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 phiên bản cơ sở dữ liệu Cloud SQL trong dự án exciting-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 để thực hiện các lệnh gọi đến một API của Google thay mặt cho một tài khoản dịch vụ hoặc tài khoản người dùng nhất định nếu bạn đã cấp(các) phạm vi truy cập mà API yêu cầu. Để thực hiện việc này, hãy thêm mã truy cập vào yêu cầu gửi đến API bằng cách thêm tham số truy vấn access_token hoặc giá trị tiêu đề HTTP Authorization Bearer. Khi có thể, bạn nên dùng tiêu đề HTTP vì chuỗi truy vấn thườ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 một thư viện ứng dụng để thiết lập các lệnh gọi đến 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 phạm vi của chúng tại OAuth 2.0 Playground.

Ví dụ về HTTP GET

Một lệnh gọi đến điểm cuối drive.files (Drive Files API) bằng cách sử dụng tiêu đề HTTP Authorization: Bearer có thể trông như sau. Xin 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

Sau đây là một lệnh gọi đến cùng một API cho người dùng đã xác thực bằng cách sử dụ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. Sau đây là ví dụ sử dụng lựa chọn tiêu đề HTTP (nên dùng):

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

Hoặc, bạn có thể 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 phát hành sẽ hết hạn sau khoảng thời gian do giá trị expires_in cung cấp. Khi mã truy cập hết hạn, ứng dụng sẽ tạo một JWT khác, ký tên và 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ố gắng sử dụng tính năng uỷ quyền trên toàn miền, thì tài khoản dịch vụ sẽ không được uỷ quyền trong Bảng điều khiển dành cho quản trị viên của miền người dùng.

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

Mặc dù thường chỉ mất vài phút, nhưng có thể mất đến 24 giờ để uỷ quyền có hiệu lực đối với 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 ứng dụng chứ không phải mã ứng dụng (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 khách rồi thêm lại ứng dụng đó bằng mã nhận dạng 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 được 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 Uỷ quyền trên toàn miền của Bảng điều khiển dành cho quản trị viên đối với người dùng trong yêu cầu sub (trường) và tài khoản đó bao gồm tất cả các phạm vi mà bạn đang yêu cầu trong yêu cầu scope của JWT.

Mặc dù thường chỉ mất vài phút, nhưng có thể mất đến 24 giờ để uỷ quyền có hiệu lực đối với 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 không thể uỷ quyền một hoặc nhiều phạm vi được yêu cầu do chính sách của quản trị viên Google Workspace.

Hãy xem bài viết trợ giúp dành cho Quản trị viên Google Workspace Kiểm soát việc những ứng dụng nội bộ và ứng dụng của 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 phạm vi nhạy cảm và bị hạn chế cho đến khi quyền truy cập được cấp rõ ràng cho mã ứng dụng OAuth của bạn.

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 các khai báo chính xác.

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

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

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

Ứng dụng OAuth đang được dùng để đưa ra yêu cầu đã bị xoá. Bạn có thể xoá theo cách thủ công hoặc tự động trong trường hợp các ứng dụng không được dùng . Bạn có thể khôi phục các khách hàng đã xoá trong vòng 30 ngày kể từ ngày xoá. Tìm hiểu thêm.

Sử dụng mã ứng dụng khách vẫn đang hoạt động.

invalid_grant Not a valid email. Người dùng không tồn tại. Kiểm tra để đảm bảo rằng địa chỉ email trong yêu cầu (trường) 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, điều này có nghĩa là thời gian hệ thống cục bộ không chính xác. Điều này cũng có thể xảy ra nếu giá trị exp lớn hơn 65 phút so với giá trị iat hoặc giá trị exp thấp hơn giá trị iat.

Đảm bảo rằng đồ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 với Google NTP.

invalid_grant Invalid JWT Signature.

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

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

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

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

invalid_scope Invalid OAuth scope or ID token audience provided. Không có phạm vi nào được yêu cầu (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 (tức là không hợp lệ).

Đảm bảo rằng bạn đã điền thông tin vào khai báo scope (trường) của JWT và so sánh các phạm vi mà JWT chứa với các phạm vi được ghi lại 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 các phạm vi trong khai báo 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 lệnh JWT bị vô hiệu hoá.

Chuyển đến Google API Console, rồi trong phần 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 "Mã khoá" được dùng để ký xác nhận.

org_internal This client is restricted to users within its organization. Mã ứng dụng OAuth trong yêu cầu thuộc một 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ể.

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 của bạn.

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

Với một số API của Google, 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 trực tiếp JWT đã ký làm mã thông báo người mang thay vì mã thông báo 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 mà bạn muốn gọi có một định nghĩa dịch vụ được xuất bản trong kho lưu trữ Google API trên GitHub, 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 một tài khoản dịch vụ như mô tả ở trên. Đừng quên lưu giữ tệp JSON mà bạn nhận được khi tạo tài khoản.
  2. Sử dụng bất kỳ thư viện JWT tiêu chuẩn nào, chẳng hạn như thư viện 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ã nhận dạng 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 hiện tại theo hệ thống Unix và đối với trường exp, hãy chỉ định thời gian chính xác sau 3600 giây, khi JWT hết hạn.

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

Ví dụ:

Java

Sử dụng google-auth-library-javajava-jwt:

import com.google.auth.oauth2.ServiceAccountCredentials;
...
GoogleCredentials credentials =
        GoogleCredentials.fromStream(new FileInputStream("MyProject-1234.json"));
PrivateKey privateKey = ((ServiceAccountCredentials) credentials).getPrivateKey();
String privateKeyId = ((ServiceAccountCredentials) credentials).getPrivateKeyId();

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 bằng cách sử dụng JWT đã ký làm mã thông báo của người mang:
    GET /v1/projects/abc/databases/123/indexes HTTP/1.1
    Authorization: Bearer SIGNED_JWT
    Host: firestore.googleapis.com

Triển khai tính năng Bảo vệ nhiều tài khoản

Một bước bổ sung mà bạn nên thực hiện để bảo vệ tài khoản của người dùng là triển khai tính năng Bảo vệ trên nhiều tài khoản bằng cách sử dụng Dịch vụ bảo vệ trên nhiều tài khoản của Google. Dịch vụ này cho phép bạn đăng ký nhận thông báo về sự kiện bảo mật. Thông báo này cung cấp thông tin cho ứng dụng của bạn về những thay đổi lớn đối với tài khoản người dùng. Sau đó, bạn có thể sử dụng thông tin này để thực hiện hành động tuỳ thuộc vào cách bạn quyết định phản hồi các sự kiện.

Sau đây là một số ví dụ về các loại sự kiện mà Dịch vụ bảo vệ trên nhiều tài khoản của Google gửi đến ứng dụng của bạn:

  • 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

Hãy xem trang Bảo vệ tài khoản người dùng bằng tính năng Bảo vệ nhiều tài khoản để biết thêm thông tin về cách triển khai tính năng Bảo vệ nhiều tài khoản và danh sách đầy đủ các sự kiện có sẵn.