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

Để 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 ở phía máy khách bằng ứng dụng API JavaScript, đồng thời bạn gửi mã uỷ quyền một lần đặc biệt đến máy chủ của mình. Máy chủ của bạn sẽ trao đổi mã dùng một lần này để lấy mã truy cập và mã làm mới từ Google để máy chủ có thể thực hiện lệnh gọi API của riêng mình. 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. Luồng mã một lần này có các lợi thế bảo mật so với cả quy trình đơn thuần phía máy chủ và việc 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 cho ứng dụng 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. Thông qua 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ù bạn không nên làm rò rỉ mã, nhưng nếu không có mật khẩu ứng dụng khách thì bạn sẽ rất khó sử dụng những mã này. 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 để lấy 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 mã ứng dụng khách và mật khẩu ứng dụng khách, hãy tạo một dự án Bảng điều khiển Google API, thiết lập mã ứng dụng khách OAuth và đăng ký 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 trong phần "API và dịch vụ", hãy chọn Thông tin xác thực, rồi nhấp vào Định cấu hình màn hình đồng ý.

    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 danh sách thả xuống Tạo thông tin xác thực rồi chọn Mã ứng dụng khách OAuth.

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

    Đăng ký các nguồn gốc mà từ đó ứng dụng của bạn được phép truy cập vào các API của Google như sau. Nguồn gốc là tổ hợp duy nhất giữ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 chạy trên nhiều giao thức, miền hoặc miền con. 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ị. URI chuyển hướng không được sử 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. 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 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 tạo đối tượng GoogleAuth. Hãy chỉ định mã ứng dụng khách của bạn và các phạm vi mà bạn muốn yêu cầu khi 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 trình xử lý lượt nhấp để gọi grantOfflineAccess() nhằm bắt đầu quy trình gửi 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 quyền truy cập vào các quyền mà bạn đã yêu cầu. Sau đó, hàm callback mà bạn đã chỉ định trong phương thức grantOfflineAccess().then() được chuyển một đối tượng JSON kèm theo 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ể đổi lấy mã truy cập và mã làm mới riêng. Bạn chỉ có thể nhận 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 đã 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ượt trao đổi tiếp theo sẽ trả về null cho mã làm mới. Quy trình này tăng cường bảo mật so với quy trình OAuth 2.0 tiêu chuẩn.

Mã truy cập luôn được trả về khi trao đổi mã uỷ quyền hợp lệ.

Tập lệnh sau đây định nghĩa hàm callback cho nút đăng nhập. Khi bạn đăng nhập thành công, hàm này sẽ lưu trữ mã truy cập để sử dụng ở phía máy khách 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à tuỳ ý lưu trữ 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 mã nhận dạng chứa thông tin hồ sơ cơ bản 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']