כניסה באמצעות חשבון Google לאפליקציות בצד השרת

כדי להשתמש בשירותי Google בשם משתמש כשהוא לא מחובר לאינטרנט, צריך להשתמש בתהליך היברידי בצד השרת שבו המשתמש נותן הרשאה לאפליקציה אצל הלקוח באמצעות לקוח ה-API של JavaScript, ושולחת בקשה חד-פעמית קוד הרשאה לשרת. השרת שלך מבצע החלפה של הפריט החד-פעמי הזה כדי לקבל גישה ואסימוני רענון משלו מ-Google עבור השרת יכולת לבצע קריאות משלו ל-API, שניתן לבצע כשהמשתמש לא מחובר לאינטרנט. לתהליך הקוד החד-פעמי הזה יש יתרונות אבטחה על פני גם בצד השרת ולהעביר אסימוני גישה לשרת שלכם.

תהליך הכניסה לקבלת אסימון גישה בצד השרת כפי שמתואר בהמשך.

לקודים חד-פעמיים יש כמה יתרונות אבטחה. עם קודים, Google מספק אסימונים ישירות לשרת ללא מתווכים. למרות שאנחנו לא ממליצים להדליף קודים, קשה מאוד להשתמש בהם. ללא סוד הלקוח שלכם. שמור על סוד הלקוח שלך!

הטמעת התהליך ליצירת קוד חד-פעמי

לחצן הכניסה באמצעות חשבון Google מספק אסימון גישה וגם קוד הרשאה. הקוד הוא קוד חד-פעמי שהשרת יכול להחליף בשרתים של Google בשביל אסימון גישה.

הקוד לדוגמה הבא מדגים איך לבצע את הפעולות הבאות: בקוד חד-פעמי.

כדי לאמת כניסה באמצעות חשבון Google באמצעות קוד חד-פעמי, צריך:

שלב 1: יוצרים מזהה לקוח וסוד לקוח

כדי ליצור מזהה לקוח וסוד לקוח, יוצרים פרויקט במסוף Google API, להגדיר מזהה לקוח ב-OAuth ולרשום את מקורות ה-JavaScript:

  1. עוברים אל Google API Console.

  2. מהתפריט הנפתח של הפרויקטים, בוחרים פרויקט קיים או יוצרים פרויקט חדש בוחרים באפשרות Create a new project (יצירת פרויקט חדש).

  3. בסרגל הצד, בקטע APIs & Services (שירותים), בוחרים באפשרות Credentials (פרטי כניסה) ולוחצים על Configure consent screen (הגדרת מסך הסכמה).

    בוחרים כתובת אימייל, מציינים שם מוצר ולוחצים על שמירה.

  4. בכרטיסייה Credentials, בוחרים את התפריט הנפתח Create credentials לרשימה, ובוחרים באפשרות OAuth Client ID (מזהה לקוח OAuth).

  5. בקטע Application type (סוג אפליקציה), בוחרים באפשרות Web application.

    רישום המקורות שמהם האפליקציה מורשית לגשת אל Google APIs, כמפורט בהמשך. מקור הוא שילוב ייחודי של פרוטוקול, שם המארח ויציאה.

    1. בשדה Authorized JavaScript Sources (מקורות JavaScript מורשים), מזינים את המקור של באפליקציה שלך. אפשר להזין כמה מקורות כדי לאפשר את הפעלת האפליקציה פרוטוקולים, דומיינים או תתי-דומיינים שונים. לא ניתן להשתמש בתווים כלליים לחיפוש. בדוגמה הבאה, כתובת ה-URL השנייה יכולה להיות כתובת URL לסביבת ייצור.

      http://localhost:8080
      https://myproductionurl.example.com
      
    2. השדה URI של הפניה אוטומטית להפניה אוטומטית לא מחייב ערך. הפניה לכתובת URL אחרת לא נעשה שימוש במזהי URI עם ממשקי API של JavaScript.

    3. לוחצים על הלחצן Create.

  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: מוסיפים לדף את לחצן הכניסה

צריך להוסיף את לחצן הכניסה לדף האינטרנט ולצרף רכיב handler של קליקים כדי להתקשר אליו 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: נכנסים לחשבון משתמש

המשתמש לוחץ על לחצן הכניסה ומעניק לאפליקציה גישה להרשאות שביקשת. לאחר מכן, את פונקציית הקריאה החוזרת שציינתם בקובץ ה-method grantOfflineAccess().then() מועברת אובייקט JSON עם קוד הרשאה. לדוגמה:

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

שלב 6: שולחים את קוד ההרשאה לשרת

code הוא הקוד החד-פעמי שהשרת יכול להחליף בעצמו אסימון גישה ואסימון רענון. אפשר לקבל אסימון רענון רק לאחר למשתמש הוצגה תיבת דו-שיח לאישור עם בקשה לגישה אופליין. אם ציינתם select-account prompt OfflineAccessOptions בשלב 4, יש לאחסן את אסימון הרענון שמאחזרים לשימוש במועד מאוחר יותר כי ההמרות הבאות יחזירו null עבור אסימון הרענון. התהליך הזה מספקת אבטחה מוגברת בזרימה הרגילה של OAuth 2.0.

אסימוני גישה תמיד מוחזרים עם החלפה של הרשאה חוקית

הסקריפט הבא מגדיר פונקציית קריאה חוזרת ללחצן הכניסה. מתי הכניסה בוצעה בהצלחה, הפונקציה מאחסנת את אסימון הגישה בצד הלקוח משתמש בקוד החד-פעמי ושולח אותו לשרת שלכם באותו דומיין.

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