Google Sign-In สำหรับแอปฝั่งเซิร์ฟเวอร์

หากต้องการใช้บริการของ Google ในนามของผู้ใช้เมื่อผู้ใช้ออฟไลน์อยู่ คุณต้องดำเนินการต่อไปนี้ ใช้ขั้นตอนฝั่งเซิร์ฟเวอร์แบบผสมที่ผู้ใช้ให้สิทธิ์แอปของคุณในไคลเอ็นต์ โดยใช้ไคลเอ็นต์ JavaScript API และส่งแบบครั้งเดียว รหัสการให้สิทธิ์ไปยังเซิร์ฟเวอร์ของคุณ เซิร์ฟเวอร์จะแลกเปลี่ยนแบบใช้ครั้งเดียวนี้ เพื่อรับสิทธิ์การเข้าถึงของตนเองและรีเฟรชโทเค็นจาก Google สำหรับเซิร์ฟเวอร์เพื่อ ให้สามารถเรียก API ของตัวเอง ซึ่งสามารถทำได้ในขณะที่ผู้ใช้ออฟไลน์ การทำงานของโค้ดแบบใช้ครั้งเดียวนี้มีข้อดีด้านความปลอดภัยมากกว่าทั้งฝั่งเซิร์ฟเวอร์ และการส่งโทเค็นเพื่อการเข้าถึงไปยังเซิร์ฟเวอร์ของคุณ

ขั้นตอนการลงชื่อเข้าใช้เพื่อรับโทเค็นเพื่อการเข้าถึงสำหรับฝั่งเซิร์ฟเวอร์ ตามภาพที่แสดงด้านล่างนี้

รหัสแบบใช้ครั้งเดียวมีข้อดีด้านความปลอดภัยหลายประการ เมื่อใช้โค้ด Google ให้โทเค็นไปยังเซิร์ฟเวอร์ของคุณโดยตรงโดยไม่ต้องอาศัยคนกลาง แม้ว่าเราจะไม่แนะนำรหัสที่รั่วไหล แต่รหัสเหล่านี้ใช้งานยากมาก โดยไม่มีรหัสลับไคลเอ็นต์ เก็บรหัสลับไคลเอ็นต์ไว้!

การใช้ขั้นตอนการใช้โค้ดแบบครั้งเดียว

ปุ่ม Google Sign-In จะมีทั้งโทเค็นเพื่อการเข้าถึงและ รหัสการให้สิทธิ์ โค้ดดังกล่าวเป็นรหัสแบบใช้ครั้งเดียวที่เซิร์ฟเวอร์แลกเปลี่ยนได้ กับเซิร์ฟเวอร์ของ Google เพื่อขอโทเค็นเพื่อการเข้าถึง

โค้ดตัวอย่างต่อไปนี้จะแสดงวิธีการ โค้ดแบบครั้งเดียว

คุณต้องทำดังนี้เพื่อตรวจสอบสิทธิ์ Google Sign-In ด้วยรหัสแบบครั้งเดียว

ขั้นตอนที่ 1: สร้างรหัสไคลเอ็นต์และรหัสลับไคลเอ็นต์

หากต้องการสร้างรหัสไคลเอ็นต์และรหัสลับไคลเอ็นต์ ให้สร้างโปรเจ็กต์คอนโซล Google API ตั้งค่ารหัสไคลเอ็นต์ OAuth และลงทะเบียนต้นทางของ JavaScript ดังนี้

  1. ไปที่คอนโซล Google API

  2. จากเมนูแบบเลื่อนลงของโปรเจ็กต์ ให้เลือกโปรเจ็กต์ที่มีอยู่หรือสร้างโปรเจ็กต์ใหม่ โดยเลือก Create a new project

  3. ในแถบด้านข้างใต้ "API และ Services" ให้เลือกข้อมูลเข้าสู่ระบบ แล้วคลิก กำหนดค่าหน้าจอคำยินยอม

    เลือกอีเมล ระบุชื่อผลิตภัณฑ์ แล้วกดบันทึก

  4. ในแท็บข้อมูลเข้าสู่ระบบ ให้เลือกรายการแบบเลื่อนลงสร้างข้อมูลเข้าสู่ระบบ แล้วเลือกรหัสไคลเอ็นต์ OAuth

  5. ในส่วนประเภทแอปพลิเคชัน ให้เลือกเว็บแอปพลิเคชัน

    ลงทะเบียนต้นทางที่แอปของคุณได้รับอนุญาตให้เข้าถึง Google APIs ดังนี้ ต้นทางเป็นการรวมโปรโตคอลที่ไม่ซ้ำกัน ชื่อโฮสต์ และพอร์ต

    1. ในช่องต้นทาง JavaScript ที่ได้รับอนุญาต ให้ป้อนต้นทางสำหรับ แอปของคุณ คุณป้อนต้นทางได้หลายต้นทางเพื่ออนุญาตให้แอปทำงาน โปรโตคอล โดเมน หรือโดเมนย่อยที่แตกต่างกัน คุณไม่สามารถใช้ไวลด์การ์ด ในตัวอย่างด้านล่าง URL ที่ 2 อาจเป็น URL เวอร์ชันที่ใช้งานจริง

      http://localhost:8080
      https://myproductionurl.example.com
      
    2. ช่อง URI การเปลี่ยนเส้นทางที่ได้รับอนุญาตไม่จำเป็นต้องมีค่า เปลี่ยนเส้นทาง URI ไม่ได้ใช้กับ JavaScript API

    3. กดปุ่มสร้าง

  6. คัดลอกรหัสไคลเอ็นต์จากกล่องโต้ตอบไคลเอ็นต์ OAuth ที่ปรากฏ Client-ID จะช่วยให้แอปของคุณเข้าถึง Google API ที่เปิดใช้อยู่ได้

ขั้นตอนที่ 2: ใส่ไลบรารีแพลตฟอร์มของ Google ในหน้าเว็บ

รวมสคริปต์ต่อไปนี้ ซึ่งสาธิตฟังก์ชันที่ไม่ระบุชื่อที่ จะแทรกสคริปต์ลงใน DOM ของหน้าเว็บ index.html นี้

<!-- 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 -->

ขั้นตอนที่ 3: เริ่มต้นออบเจ็กต์ GoogleAuth

โหลดไลบรารี auth2 และเรียก gapi.auth2.init() เพื่อเริ่มต้น GoogleAuth ออบเจ็กต์ ระบุรหัสไคลเอ็นต์และขอบเขตที่คุณต้องการส่งคำขอ เมื่อโทรหา 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>

ขั้นตอนที่ 4: เพิ่มปุ่มลงชื่อเข้าใช้ลงในหน้าเว็บของคุณ

เพิ่มปุ่มลงชื่อเข้าใช้ลงในหน้าเว็บ และแนบเครื่องจัดการการคลิกเพื่อเรียก grantOfflineAccess() เพื่อเริ่มขั้นตอนการใช้โค้ดแบบครั้งเดียว

<!-- 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>

ขั้นตอนที่ 5: ลงชื่อเข้าใช้ผู้ใช้

ผู้ใช้คลิกปุ่มลงชื่อเข้าใช้และให้สิทธิ์แอปของคุณเข้าถึง ที่คุณขอ จากนั้น ฟังก์ชัน Callback ที่คุณระบุไว้ใน เมธอด grantOfflineAccess().then() มีการส่งผ่านออบเจ็กต์ JSON ที่มีเอลิเมนต์ รหัสการให้สิทธิ์ของคุณ เช่น

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

ขั้นตอนที่ 6: ส่งรหัสการให้สิทธิ์ไปยังเซิร์ฟเวอร์

code คือรหัสแบบใช้ครั้งเดียวที่เซิร์ฟเวอร์ของคุณแลกเปลี่ยนเป็นรหัสของตัวเองได้ โทเค็นเพื่อการเข้าถึงและโทเค็นการรีเฟรช คุณสามารถรับโทเค็นการรีเฟรชได้หลังจาก ผู้ใช้ได้รับกล่องโต้ตอบการให้สิทธิ์ซึ่งขอการเข้าถึงแบบออฟไลน์ หากคุณระบุ select-account prompt ใน OfflineAccessOptions ในขั้นตอนที่ 4 คุณจะต้องจัดเก็บโทเค็นการรีเฟรชที่ดึงขึ้นมาเพื่อใช้ในภายหลัง เนื่องจากการแลกเปลี่ยนครั้งต่อๆ ไปจะส่งกลับ null สำหรับโทเค็นการรีเฟรช ขั้นตอนนี้ ช่วยเพิ่มความปลอดภัยให้มากกว่าขั้นตอน OAuth 2.0 มาตรฐาน

โทเค็นเพื่อการเข้าถึงจะส่งคืนพร้อมการแลกเปลี่ยนการให้สิทธิ์ที่ถูกต้อง โค้ด

สคริปต์ต่อไปนี้จะกำหนดฟังก์ชัน Callback สำหรับปุ่มลงชื่อเข้าใช้ วันและเวลา เมื่อลงชื่อเข้าใช้สำเร็จ ฟังก์ชันจะจัดเก็บโทเค็นเพื่อการเข้าถึงสำหรับฝั่งไคลเอ็นต์ ใช้และส่งรหัสแบบใช้ครั้งเดียวไปยังเซิร์ฟเวอร์ของคุณในโดเมนเดียวกัน

<!-- 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>

ขั้นตอนที่ 7: แลกเปลี่ยนรหัสการให้สิทธิ์สำหรับโทเค็นเพื่อการเข้าถึง

แลกเปลี่ยนรหัสการตรวจสอบสิทธิ์ในเซิร์ฟเวอร์เพื่อการเข้าถึงและรีเฟรชโทเค็น ใช้เมนู โทเค็นเพื่อการเข้าถึงเพื่อเรียกใช้ Google APIs ในนามของผู้ใช้และจัดเก็บ โทเค็นการรีเฟรชเพื่อรับโทเค็นเพื่อการเข้าถึงใหม่เมื่อโทเค็นเพื่อการเข้าถึงหมดอายุ

หากคุณขอสิทธิ์เข้าถึงโปรไฟล์ คุณจะได้รับโทเค็นรหัสที่มีฟังก์ชันพื้นฐาน ข้อมูลโปรไฟล์ของผู้ใช้

เช่น

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']