Tính năng Đăng nhập bằng Google cho các ứng dụng phía máy chủ

Để thay mặt người dùng sử dụng các dịch vụ của Google khi người dùng không có kết nối mạng, bạn phải sử dụng quy trình kết hợp phía máy chủ, trong đó người dùng uỷ quyền cho ứng dụng của bạn trên ứng dụng bằng cách sử dụng ứng dụng khách API JavaScript và bạn gửi thông báo một lần đặc biệt mã uỷ quyền đến máy chủ của mình. Máy chủ của bạn trao đổi dữ liệu dùng một lần này để có được quyền truy cập của riêng mình và làm mới mã thông báo từ Google cho máy chủ thực hiện lệnh gọi API riêng. Bạn có thể thực hiện việc này khi người dùng không có kết nối mạng. Quy trình mã chỉ cần dùng một lần này có nhiều ưu điểm về bảo mật so với cả việc chỉ có một phía máy chủ và gửi mã truy cập đến máy chủ của bạn.

Quy trình đăng nhập để lấy mã truy cập phía máy chủ được minh hoạ bên dưới.

Mã dùng một lần có một số lợi thế về bảo mật. Khi có mã, Google cung cấp mã thông báo trực tiếp cho máy chủ của bạn mà không qua bất kỳ bên trung gian nào. Mặc dù chúng tôi không khuyến khích việc làm rò rỉ mã, nhưng các mã này rất khó sử dụng mà không có mật khẩu ứng dụng khách của bạn. Hãy giữ bí mật cho ứng dụng của bạn!

Triển khai quy trình mã một lần

Nút Đăng nhập bằng Google cung cấp cả mã truy cậpmã uỷ quyền. Mã này là mã dùng một lần mà máy chủ của bạn có thể trao đổi với máy chủ của Google để có mã truy cập.

Mã mẫu sau đây minh hoạ cách thực hiện quy trình mã một lần.

Xác thực quy trình Đăng nhập bằng Google bằng quy trình mã một lần yêu cầu bạn phải:

Bước 1: Tạo mã ứng dụng khách và mật khẩu ứng dụng khách

Để tạo ID ứng dụng khách và mật khẩu ứng dụng khách, hãy tạo dự án Bảng điều khiển Google API, thiết lập mã ứng dụng khách OAuth và đăng ký các nguồn gốc JavaScript của bạn:

  1. Chuyển đến Google API Console.

  2. Trong trình đơn thả xuống của dự án, hãy chọn dự án hiện có hoặc tạo dự án mới bằng cách chọn Create a new project (Tạo dự án mới).

  3. Trong thanh bên phía dưới "API và Dịch vụ", chọn Thông tin xác thực rồi nhấp vào Định cấu hình màn hình xin phép.

    Chọn Địa chỉ email, chỉ định Tên sản phẩm rồi nhấn Lưu.

  4. Trong thẻ Thông tin xác thực, hãy chọn trình đơn thả xuống Tạo thông tin xác thực rồi chọn Mã ứng dụng OAuth.

  5. Trong Application type (Loại ứng dụng), hãy chọn Web application (Ứng dụng web).

    Đăng ký nguồn gốc mà từ đó ứng dụng của bạn được phép truy cập vào Google API, như sau. Mỗi nguồn gốc là một tổ hợp duy nhất của giao thức, tên máy chủ và cổng.

    1. Trong trường Nguồn gốc JavaScript được cho phép, hãy nhập nguồn gốc cho ứng dụng của bạn. Bạn có thể nhập nhiều nguồn gốc để cho phép ứng dụng của mình chạy trên giao thức, miền hoặc miền con khác nhau. Bạn không được sử dụng ký tự đại diện. Trong ví dụ bên dưới, URL thứ hai có thể là URL phát hành chính thức.

      http://localhost:8080
      https://myproductionurl.example.com
      
    2. Trường URI chuyển hướng được phép không yêu cầu giá trị. Chuyển hướng URI không được dùng với API JavaScript.

    3. Nhấn vào nút Create (Tạo).

  6. Trong hộp thoại ứng dụng OAuth hiện ra, hãy sao chép Mã ứng dụng. Chiến lược phát hành đĩa đơn Mã ứng dụng khách cho phép ứng dụng của bạn truy cập vào các API của Google đã bật.

Bước 2: Đưa thư viện nền tảng của Google vào trang của bạn

Bao gồm các tập lệnh sau minh hoạ hàm ẩn danh sẽ chèn một tập lệnh vào DOM của trang web index.html này.

<!-- The top of file index.html -->
<html itemscope itemtype="http://schema.org/Article">
<head>
  <!-- BEGIN Pre-requisites -->
  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js">
  </script>
  <script src="https://apis.google.com/js/client:platform.js?onload=start" async defer>
  </script>
  <!-- END Pre-requisites -->

Bước 3: Khởi chạy đối tượng GoogleAuth

Tải thư viện auth2 và gọi gapi.auth2.init() để khởi chạy Đối tượng GoogleAuth. Chỉ định mã ứng dụng khách của bạn và những phạm vi mà bạn muốn yêu cầu khi bạn gọi init().

<!-- Continuing the <head> section -->
  <script>
    function start() {
      gapi.load('auth2', function() {
        auth2 = gapi.auth2.init({
          client_id: 'YOUR_CLIENT_ID.apps.googleusercontent.com',
          // Scopes to request in addition to 'profile' and 'email'
          //scope: 'additional_scope'
        });
      });
    }
  </script>
</head>
<body>
  <!-- ... -->
</body>
</html>

Bước 4: Thêm nút đăng nhập vào trang của bạn

Thêm nút đăng nhập vào trang web của bạn và đính kèm một trình xử lý lượt nhấp để gọi grantOfflineAccess() để bắt đầu quy trình dùng mã một lần.

<!-- Add where you want your sign-in button to render -->
<!-- Use an image that follows the branding guidelines in a real app -->
<button id="signinButton">Sign in with Google</button>
<script>
  $('#signinButton').click(function() {
    // signInCallback defined in step 6.
    auth2.grantOfflineAccess().then(signInCallback);
  });
</script>

Bước 5: Đăng nhập người dùng

Người dùng nhấp vào nút đăng nhập và cấp cho ứng dụng của bạn các quyền truy cập mà bạn yêu cầu. Sau đó, hàm callback mà bạn đã chỉ định trong Phương thức grantOfflineAccess().then() được truyền đối tượng JSON bằng một mã uỷ quyền. Ví dụ:

{"code":"4/yU4cQZTMnnMtetyFcIWNItG32eKxxxgXXX-Z4yyJJJo.4qHskT-UtugceFc0ZRONyF4z7U4UmAI"}

Bước 6: Gửi mã uỷ quyền đến máy chủ

code là mã dùng một lần mà máy chủ của bạn có thể trao đổi cho riêng nó mã truy cập và mã làm mới. Bạn chỉ có thể nhận được mã làm mới sau khi người dùng đã nhìn thấy hộp thoại uỷ quyền yêu cầu quyền truy cập ngoại tuyến. Nếu bạn đã chỉ định select-account prompt trong OfflineAccessOptions ở bước 4, bạn phải lưu trữ mã làm mới mà bạn truy xuất để sử dụng sau này vì các lần trao đổi tiếp theo sẽ trả về null cho mã làm mới. Quy trình này cung cấp khả năng bảo mật cao hơn so với quy trình OAuth 2.0 tiêu chuẩn.

Mã truy cập luôn được trả về cùng với việc trao đổi một yêu cầu uỷ quyền hợp lệ .

Tập lệnh sau đây định nghĩa hàm callback cho nút đăng nhập. Thời gian đăng nhập thành công, hàm này lưu trữ mã truy cập cho phía máy khách sử dụng và gửi mã dùng một lần đến máy chủ của bạn trên cùng một miền.

<!-- Last part of BODY element in file index.html -->
<script>
function signInCallback(authResult) {
  if (authResult['code']) {

    // Hide the sign-in button now that the user is authorized, for example:
    $('#signinButton').attr('style', 'display: none');

    // Send the code to the server
    $.ajax({
      type: 'POST',
      url: 'http://example.com/storeauthcode',
      // Always include an `X-Requested-With` header in every AJAX request,
      // to protect against CSRF attacks.
      headers: {
        'X-Requested-With': 'XMLHttpRequest'
      },
      contentType: 'application/octet-stream; charset=utf-8',
      success: function(result) {
        // Handle or verify the server response.
      },
      processData: false,
      data: authResult['code']
    });
  } else {
    // There was an error.
  }
}
</script>

Bước 7: Trao đổi mã uỷ quyền lấy mã truy cập

Trên máy chủ, hãy trao đổi mã xác thực để có quyền truy cập và làm mới mã thông báo. Sử dụng mã truy cập để gọi API của Google thay mặt cho người dùng và lưu trữ (không bắt buộc) mã làm mới để lấy mã truy cập mới khi mã truy cập hết hạn.

Nếu đã yêu cầu quyền truy cập vào hồ sơ, bạn cũng sẽ nhận được mã thông báo ID chứa thông tin cơ bản thông tin hồ sơ của người dùng.

Ví dụ:

Java
// (Receive authCode via HTTPS POST)


if (request.getHeader("X-Requested-With") == null) {
  // Without the `X-Requested-With` header, this request could be forged. Aborts.
}

// Set path to the Web application client_secret_*.json file you downloaded from the
// Google API Console: https://console.cloud.google.com/apis/credentials
// You can also find your Web application client ID and client secret from the
// console and specify them directly when you create the GoogleAuthorizationCodeTokenRequest
// object.
String CLIENT_SECRET_FILE = "/path/to/client_secret.json";

// Exchange auth code for access token
GoogleClientSecrets clientSecrets =
    GoogleClientSecrets.load(
        JacksonFactory.getDefaultInstance(), new FileReader(CLIENT_SECRET_FILE));
GoogleTokenResponse tokenResponse =
          new GoogleAuthorizationCodeTokenRequest(
              new NetHttpTransport(),
              JacksonFactory.getDefaultInstance(),
              "https://oauth2.googleapis.com/token",
              clientSecrets.getDetails().getClientId(),
              clientSecrets.getDetails().getClientSecret(),
              authCode,
              REDIRECT_URI)  // Specify the same redirect URI that you use with your web
                             // app. If you don't have a web version of your app, you can
                             // specify an empty string.
              .execute();

String accessToken = tokenResponse.getAccessToken();

// Use access token to call API
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken);
Drive drive =
    new Drive.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance(), credential)
        .setApplicationName("Auth Code Exchange Demo")
        .build();
File file = drive.files().get("appfolder").execute();

// Get profile info from ID token
GoogleIdToken idToken = tokenResponse.parseIdToken();
GoogleIdToken.Payload payload = idToken.getPayload();
String userId = payload.getSubject();  // Use this value as a key to identify a user.
String email = payload.getEmail();
boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
String name = (String) payload.get("name");
String pictureUrl = (String) payload.get("picture");
String locale = (String) payload.get("locale");
String familyName = (String) payload.get("family_name");
String givenName = (String) payload.get("given_name");
Python
from apiclient import discovery
import httplib2
from oauth2client import client

# (Receive auth_code by HTTPS POST)


# If this request does not have `X-Requested-With` header, this could be a CSRF
if not request.headers.get('X-Requested-With'):
    abort(403)

# Set path to the Web application client_secret_*.json file you downloaded from the
# Google API Console: https://console.cloud.google.com/apis/credentials
CLIENT_SECRET_FILE = '/path/to/client_secret.json'

# Exchange auth code for access token, refresh token, and ID token
credentials = client.credentials_from_clientsecrets_and_code(
    CLIENT_SECRET_FILE,
    ['https://www.googleapis.com/auth/drive.appdata', 'profile', 'email'],
    auth_code)

# Call Google API
http_auth = credentials.authorize(httplib2.Http())
drive_service = discovery.build('drive', 'v3', http=http_auth)
appfolder = drive_service.files().get(fileId='appfolder').execute()

# Get profile info from ID token
userid = credentials.id_token['sub']
email = credentials.id_token['email']