راهنمای مهاجرت جریان خارج از باند (OOB).

نمای کلی

در ۱۶ فوریه ۲۰۲۲، ما برنامه‌هایی را برای ایمن‌تر کردن تعاملات Google OAuth با استفاده از جریان‌های امن‌تر OAuth اعلام کردیم . این راهنما به شما کمک می‌کند تا تغییرات و مراحل لازم برای مهاجرت موفقیت‌آمیز از جریان OAuth خارج از باند (OOB) به گزینه‌های پشتیبانی‌شده را درک کنید.

این تلاش، یک اقدام حفاظتی در برابر حملات فیشینگ و جعل هویت برنامه در حین تعامل با نقاط پایانی احراز هویت OAuth 2.0 گوگل است.

OOB چیست؟

OAuth out-of-band (OOB) ، که به عنوان گزینه کپی/چسباندن دستی نیز شناخته می‌شود، یک جریان قدیمی است که برای پشتیبانی از کلاینت‌های بومی توسعه داده شده است که پس از تأیید درخواست رضایت OAuth توسط کاربر، URI تغییر مسیر برای پذیرش اعتبارنامه‌ها ندارند. جریان OOB خطر فیشینگ از راه دور را ایجاد می‌کند و کلاینت‌ها باید برای محافظت در برابر این آسیب‌پذیری به یک روش جایگزین مهاجرت کنند.

جریان OOB برای همه انواع کلاینت‌ها، یعنی برنامه‌های وب، اندروید، iOS، پلتفرم جهانی ویندوز (UWP)، برنامه‌های کروم، تلویزیون‌ها و دستگاه‌های با ورودی محدود و برنامه‌های دسکتاپ، منسوخ شده است.

تاریخ‌های کلیدی انطباق

  • ۲۸ فوریه ۲۰۲۲ - استفاده جدید از OAuth برای جریان OOB مسدود شد
  • ۵ سپتامبر ۲۰۲۲ - ممکن است یک پیام هشدار برای کاربر در درخواست‌های OAuth ناسازگار نمایش داده شود.
  • ۳ اکتبر ۲۰۲۲ - جریان OOB برای کلاینت‌های OAuth که قبل از ۲۸ فوریه ۲۰۲۲ ایجاد شده‌اند، منسوخ شده است.
  • ۳۱ ژانویه ۲۰۲۳ - همه کلاینت‌های موجود مسدود شدند (از جمله کلاینت‌های معاف)

یک پیام خطا برای درخواست‌های نامطابق با قوانین نمایش داده می‌شود. این پیام به کاربران اطلاع می‌دهد که برنامه مسدود شده است و همزمان ایمیل پشتیبانی که در صفحه رضایت OAuth در کنسول API گوگل ثبت کرده‌اید را نمایش می‌دهد.

دو مرحله اصلی برای تکمیل فرآیند مهاجرت وجود دارد:
  1. مشخص کنید که آیا تحت تأثیر قرار گرفته‌اید یا خیر.
  2. اگر تحت تأثیر قرار گرفتید، به یک جایگزین امن‌تر مهاجرت کنید.

مشخص کنید که آیا تحت تأثیر قرار گرفته‌اید یا خیر

این منسوخ شدن فقط برای برنامه‌های تولیدی (یعنی برنامه‌هایی که وضعیت انتشار آنها روی «در حال تولید» تنظیم شده است) قابل اجرا است. این روند برای برنامه‌هایی که وضعیت انتشار آنها در حال آزمایش است، ادامه خواهد یافت.

وضعیت انتشار خود را در صفحه OAuth Branding در کنسول Google Cloud بررسی کنید و اگر از جریان OOB در پروژه‌ای با وضعیت انتشار "در حال تولید" استفاده می‌کنید، به مرحله بعدی بروید.

چگونه تشخیص دهیم که آیا برنامه ما از جریان OOB استفاده می‌کند یا خیر

کد برنامه یا فراخوانی شبکه خروجی (در صورتی که برنامه شما از کتابخانه OAuth استفاده می‌کند) را بررسی کنید تا مشخص شود که آیا درخواست مجوز Google OAuth که برنامه شما ارسال می‌کند، از یک مقدار URI تغییر مسیر OOB استفاده می‌کند یا خیر.

کد برنامه خود را بررسی کنید

بخشی از کد برنامه خود را که در آن به نقاط پایانی مجوز Google OAuth فراخوانی انجام می‌دهید، بررسی کنید و بررسی کنید که آیا پارامتر redirect_uri دارای مقادیر زیر است یا خیر:
  • redirect_uri=urn:ietf:wg:oauth:2.0:oob
  • redirect_uri=urn:ietf:wg:oauth:2.0:oob:auto
  • redirect_uri=oob
یک نمونه درخواست جریان تغییر مسیر OOB مانند نمونه زیر خواهد بود:
https://accounts.google.com/o/oauth2/v2/auth?
response_type=code&
scope=<SCOPES>&
state=<STATE>&
redirect_uri=urn:ietf:wg:oauth:2.0:oob&
client_id=<CLIENT_ID>

بررسی تماس‌های خروجی شبکه

روش بررسی فراخوانی‌های شبکه بسته به نوع کلاینت برنامه شما متفاوت خواهد بود.
هنگام بررسی فراخوانی‌های شبکه، درخواست‌های ارسال شده به نقاط پایانی مجوز Google OAuth را جستجو کنید و بررسی کنید که آیا پارامتر redirect_uri دارای مقادیر زیر است یا خیر:
  • redirect_uri=urn:ietf:wg:oauth:2.0:oob
  • redirect_uri=urn:ietf:wg:oauth:2.0:oob:auto
  • redirect_uri=oob
یک نمونه درخواست جریان تغییر مسیر OOB مانند نمونه زیر خواهد بود:
https://accounts.google.com/o/oauth2/v2/auth?
response_type=code&
scope=<SCOPES>&
state=<STATE>&
redirect_uri=urn:ietf:wg:oauth:2.0:oob&
client_id=<CLIENT_ID>

به یک جایگزین امن مهاجرت کنید

کلاینت‌های موبایل (اندروید / iOS)

اگر تشخیص دادید که برنامه شما از جریان OOB با نوع کلاینت OAuth اندروید یا iOS استفاده می‌کند، باید به استفاده از SDK های توصیه شده ( اندروید ، iOS ) مهاجرت کنید.

این SDK دسترسی به APIهای گوگل را آسان می‌کند و تمام فراخوانی‌ها به نقاط پایانی احراز هویت OAuth 2.0 گوگل را مدیریت می‌کند.

لینک‌های مستندات زیر اطلاعاتی در مورد نحوه استفاده از SDK های توصیه شده برای دسترسی به API های گوگل بدون استفاده از URI تغییر مسیر OOB ارائه می‌دهند.

دسترسی به API های گوگل در اندروید

دسترسی سمت کلاینت

مثال زیر نحوه دسترسی به API های گوگل در سمت کلاینت در اندروید را با استفاده از کتابخانه اندروید Google Identity Services که توصیه می‌شود، نشان می‌دهد.

  List requestedScopes = Arrays.asList(DriveScopes.DRIVE_APPDATA);
    AuthorizationRequest authorizationRequest = AuthorizationRequest.builder().setRequestedScopes(requestedScopes).build();
    Identity.getAuthorizationClient(activity)
            .authorize(authorizationRequest)
            .addOnSuccessListener(
                authorizationResult -> {
                  if (authorizationResult.hasResolution()) {
                    // Access needs to be granted by the user
                    PendingIntent pendingIntent = authorizationResult.getPendingIntent();
                    try {
    startIntentSenderForResult(pendingIntent.getIntentSender(),
    REQUEST_AUTHORIZE, null, 0, 0, 0, null);
                    } catch (IntentSender.SendIntentException e) {
                    Log.e(TAG, "Couldn't start Authorization UI: " + e.getLocalizedMessage());
                    }
                  } else {
                    // Access already granted, continue with user action
                    saveToDriveAppFolder(authorizationResult);
                  }
                })
            .addOnFailureListener(e -> Log.e(TAG, "Failed to authorize", e));

authorizationResult به متد تعریف شده خود ارسال کنید تا محتوا در پوشه درایو کاربر ذخیره شود. authorizationResult دارای متد getAccessToken() است که توکن دسترسی را برمی‌گرداند.

دسترسی سمت سرور (آفلاین)
مثال زیر نحوه دسترسی به API های گوگل در سمت سرور در اندروید را نشان می‌دهد.
  List requestedScopes = Arrays.asList(DriveScopes.DRIVE_APPDATA);
    AuthorizationRequest authorizationRequest = AuthorizationRequest.builder()
    .requestOfflineAccess(webClientId)
            .setRequestedScopes(requestedScopes)
            .build();
    Identity.getAuthorizationClient(activity)
            .authorize(authorizationRequest)
            .addOnSuccessListener(
                authorizationResult -> {
                  if (authorizationResult.hasResolution()) {
                    // Access needs to be granted by the user
                    PendingIntent pendingIntent = authorizationResult.getPendingIntent();
                    try {
    startIntentSenderForResult(pendingIntent.getIntentSender(),
    REQUEST_AUTHORIZE, null, 0, 0, 0, null);
                    } catch (IntentSender.SendIntentException e) {
                    Log.e(TAG, "Couldn't start Authorization UI: " + e.getLocalizedMessage());
                    }
                  } else {
                    String authCode = authorizationResult.getServerAuthCode();
                  }
                })
            .addOnFailureListener(e -> Log.e(TAG, "Failed to authorize", e));

authorizationResult دارای متد getServerAuthCode() است که کد مجوز را برمی‌گرداند و شما می‌توانید آن را به backend خود ارسال کنید تا یک توکن دسترسی و refresh دریافت کنید.

دسترسی به API های گوگل در یک برنامه iOS

دسترسی سمت کلاینت

مثال زیر نحوه دسترسی به API های گوگل در سمت کلاینت در iOS را نشان می‌دهد.

user.authentication.do { authentication, error in
  guard error == nil else { return }
  guard let authentication = authentication else { return }
  
  // Get the access token to attach it to a REST or gRPC request.
  let accessToken = authentication.accessToken
  
  // Or, get an object that conforms to GTMFetcherAuthorizationProtocol for
  // use with GTMAppAuth and the Google APIs client library.
  let authorizer = authentication.fetcherAuthorizer()
}

از توکن دسترسی برای فراخوانی API استفاده کنید، یا با قرار دادن توکن دسترسی در هدر درخواست REST یا gRPC ( Authorization: Bearer ACCESS_TOKEN ) یا با استفاده از مجوز دهنده‌ی واکشی ( GTMFetcherAuthorizationProtocol ) با کتابخانه‌ی کلاینت Google APIs برای Objective-C برای REST .

راهنمای دسترسی سمت کلاینت را در مورد نحوه دسترسی به APIهای گوگل در سمت کلاینت مرور کنید.

دسترسی از سمت سرور (آفلاین)
مثال زیر نحوه دسترسی به API های گوگل در سمت سرور برای پشتیبانی از یک کلاینت iOS را نشان می‌دهد.
GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { user, error in
  guard error == nil else { return }
  guard let user = user else { return }
  
  // request a one-time authorization code that your server exchanges for
  // an access token and refresh token
  let authCode = user.serverAuthCode
}

راهنمای دسترسی سمت سرور را در مورد نحوه دسترسی به API های گوگل از سمت سرور مرور کنید.

کلاینت برنامه کروم

اگر تشخیص دادید که برنامه شما از جریان OOB در کلاینت برنامه Chrome استفاده می‌کند، باید به استفاده از Chrome Identity API مهاجرت کنید.

مثال زیر نحوه دریافت تمام مخاطبین کاربر را بدون استفاده از URI تغییر مسیر OOB نشان می‌دهد.

window.onload = function() {
  document.querySelector('button').addEventListener('click', function() {

  
  // retrieve access token
  chrome.identity.getAuthToken({interactive: true}, function(token) {
  
  // ..........


  // the example below shows how to use a retrieved access token with an appropriate scope
  // to call the Google People API contactGroups.get endpoint

  fetch(
    'https://people.googleapis.com/v1/contactGroups/all?maxMembers=20&key=API_KEY',
    init)
    .then((response) => response.json())
    .then(function(data) {
      console.log(data)
    });
   });
 });
};

برای اطلاعات بیشتر در مورد نحوه دسترسی به احراز هویت کاربران و فراخوانی نقاط انتهایی گوگل با استفاده از API هویت کروم، راهنمای API هویت کروم را مرور کنید.

برنامه وب

اگر تشخیص دادید که برنامه شما از جریان OOB برای یک برنامه وب استفاده می‌کند، باید به استفاده از یکی از کتابخانه‌های کلاینت Google API ما مهاجرت کنید. کتابخانه‌های کلاینت برای زبان‌های برنامه‌نویسی مختلف در اینجا فهرست شده‌اند.

این کتابخانه‌ها دسترسی به APIهای گوگل و مدیریت تمام فراخوانی‌ها به نقاط انتهایی گوگل را آسان می‌کنند.

دسترسی از سمت سرور (آفلاین)
حالت دسترسی سمت سرور (آفلاین) شما را ملزم به انجام موارد زیر می‌کند:
  • یک سرور راه‌اندازی کنید و یک نقطه پایانی قابل دسترسی عمومی (URI تغییر مسیر) برای دریافت کد مجوز تعریف کنید.
  • پیکربندی URL تغییر مسیر در صفحه کلاینت‌های کنسول ابری گوگل

قطعه کد زیر یک مثال NodeJS از استفاده از API گوگل درایو برای فهرست کردن فایل‌های گوگل درایو کاربر در سمت سرور بدون استفاده از URI ریدایرکت OOB را نشان می‌دهد.

async function main() {
  const server = http.createServer(async function (req, res) {

  if (req.url.startsWith('/oauth2callback')) {
    let q = url.parse(req.url, true).query;

    if (q.error) {
      console.log('Error:' + q.error);
    } else {
      
      // Get access and refresh tokens (if access_type is offline)
      let { tokens } = await oauth2Client.getToken(q.code);
      oauth2Client.setCredentials(tokens);

      // Example of using Google Drive API to list filenames in user's Drive.
      const drive = google.drive('v3');
      drive.files.list({
        auth: oauth2Client,
        pageSize: 10,
        fields: 'nextPageToken, files(id, name)',
      }, (err1, res1) => {
        // TODO(developer): Handle response / error.
      });
    }
  }
}

راهنمای برنامه وب سمت سرور را در مورد نحوه دسترسی به API های گوگل از سمت سرور مرور کنید.

دسترسی سمت کلاینت

قطعه کد زیر، به زبان جاوا اسکریپت، مثالی از استفاده از API گوگل برای دسترسی به رویدادهای تقویم کاربر در سمت کلاینت را نشان می‌دهد.


// initTokenClient() initializes a new token client with your
// web app's client ID and the scope you need access to

const client = google.accounts.oauth2.initTokenClient({
  client_id: 'YOUR_GOOGLE_CLIENT_ID',
  scope: 'https://www.googleapis.com/auth/calendar.readonly',
  
  // callback function to handle the token response
  callback: (tokenResponse) => {
    if (tokenResponse && tokenResponse.access_token) { 
      gapi.client.setApiKey('YOUR_API_KEY');
      gapi.client.load('calendar', 'v3', listUpcomingEvents);
    }
  },
});

function listUpcomingEvents() {
  gapi.client.calendar.events.list(...);
}

راهنمای برنامه وب سمت کلاینت را در مورد نحوه دسترسی به API های گوگل از سمت کلاینت مرور کنید.

کلاینت دسکتاپ

اگر تشخیص دادید که برنامه شما از جریان OOB در یک کلاینت دسکتاپ استفاده می‌کند، باید به استفاده از جریان آدرس IP loopback ( localhost یا 127.0.0.1 ) مهاجرت کنید.