> This page is for С камеры в облако.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://next.developer.frame.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://next.developer.frame.io/_mcp/server.

# Практическое руководство: авторизация

## Введение





В руководстве показан процесс аутентификации и авторизации для устройств «С камеры в облако» (C2C) в проекте Frame.io. Мы рассмотрим как стандартный способ ввода кода вручную, так гораздо более удобный для пользователей способ сопряжения с помощью QR-кода.





## Что потребуется?

Если вы этого еще не сделали, прочитайте руководство [Перед внедрением](./implementing-c2c-setting-up). Вы должны были получить от нашей команды `client_secret` для идентификации вашей интеграции. Если нет, ознакомьтесь с [введением](./getting-started-with-cloud-device-integrations) в экосистему C2C и свяжитесь с нашей командой.

### Предварительные требования для сопряжения по URL и QR-коду





Для реализации сопряжения по URL и QR-коду должны быть выполнены следующие требования:





* **Совместимость устройства**: убедитесь, что ваше устройство поддерживает генерацию URL-адреса/QR-кода во время процесса сопряжения.




## Прохождение процесса авторизации





Чтобы понять поток авторизации с точки зрения пользователя, обратитесь к следующим ресурсам:




* [Статья справки](https://help.frame.io/ru/collections/8960335-frame-io-c2c) по добавлению новых устройств.
* [Обучающее видео](https://help.frame.io/ru/articles/5091124-camera-to-cloud-training-series) по авторизации Teradek Cube.




Этот процесс авторизации минимизирует требования к реализации. Вам не потребуется:




* Перенаправлять пользователей в веб-браузеры (за исключением использования сопряжения URL-кодов)
* Обрабатывать аутентификацию пользователей Frame.io
* Представлять интерфейсы выбора аккаунта/проекта
* Разрабатывать сложные компоненты пользовательского интерфейса помимо базового отображения информации




### Повышать удобство работы пользователей с сопряжением URL-кодов





Современным пользователям необходимо эффективное взаимодействие с устройствами. Хотя текущий процесс сопряжения вручную функционирует нормально, его можно оптимизировать.





Внедрив сопряжение с помощью URL-адреса и QR-кода — как в стриминговых сервисах, таких как Netflix или Disney+, — мы можем значительно оптимизировать процесс, снизить до минимума число ошибок ввода и сократить время сопряжения.





## Идентификация устройства (client_id)





Каждому физическому устройству требуется уникальный идентификатор для отслеживания подключения в рамках проекта пользователя.

Для устройств этим идентификатором является параметр `client_id`, который необходим при авторизации. При реализации рассмотрите подходящие источники идентификаторов, такие как серийные номера устройств, UUID или другие уникальные строки. Если вы выполняете интеграцию на устройстве Apple, мы рекомендуем использовать уникальный постоянный UUID, который остается неизменным при перезагрузке устройства. Будьте осторожны с персональными данными. Адреса электронной почты пользователей не подходят в качестве значений `client_id`.

Кроме того, убедитесь, что вы контролируете идентификатор. MAC-адреса устройств не подходят, поскольку они не принадлежат вашему программному обеспечению и могут содержать персональные данные.





Если вам нужна помощь в выборе подходящего идентификатора, наша команда может помочь определить подходящее значение, которое упростит интеграцию.





## Шаг 1. Запрос кода устройства

Чтобы начать реализацию, запросите код устройства через конечную точку `/v2/auth/device/code`:

### Традиционный метод сопряжения





```
curl -X POST https://api.frame.io/v2/auth/device/code \
    --form 'client_id=[client_id]' \
    --form 'client_secret=[client_secret]' \
    --form 'scope=asset_create offline' \
    | python -m json.tool
```





### Включение сопряжения через URL-код





Для сопряжения через URL-код измените вызов API, добавив дополнительные заголовки:





```
curl -X POST https://api.frame.io/v2/auth/device/code \
    --header "x-client-version: 2.0.0" \
    --header "x-client-platypus-enabled: true" \  # New header to enable URL pairing
    --form 'client_id=[client_id]' \
    --form 'client_secret=[client_secret]' \
    --form 'scope=asset_create offline' \
    | python -m json.tool
```

**Примечание.** Эти конечные точки аутентификации принимают исключительно данные форм, а не JSON. После аутентификации другие конечные точки будут принимать данные JSON, но конечные точки аутентификации будут отклонять запросы JSON.

#### Параметры полезной нагрузки




* **client_id**: уникальный идентификатор вашего физического устройства. Он должен быть гарантированно уникальным, например серийный номер или UUID.
* **client_secret**: предоставляется службой поддержки Frame.io для идентификации модели вашего устройства. Это конфиденциальное значение должно быть недоступно пользователям и храниться в зашифрованном виде.
* **scope**: запрашиваемые разрешения, разделенные пробелами. Устройства могут запрашивать:

* `asset_create`: разрешает создание и добавление ресурсов. * `офлайн`: разрешает обновление авторизации с помощью токена обновления. Без этой области пользователям потребуется повторно авторизовать свое устройство каждые 8 часов, поскольку срок действия токенов авторизации ограничен.

В практических реализациях устройства обычно запрашивают обе области.





### Понимание ответа API-интерфейса





Запрос генерирует ответ, который выглядит как:





#### Ответ традиционного сопряжения





```
{
  "device_code": "[device_code]",
  "expires_in": 120,
  "interval": 5,
  "name": "MyDevice-[client_id]",
  "user_code": "573131"
}
```





#### Ответ сопряжения URL





```
{
  "device_code": "[device_code]",
  "expires_in": 120,
  "interval": 5,
  "name": "MyDevice-[client_id]",
  "user_code": "573131",
  "verification_uri": "https://next.frame.io/pair",
  "verification_uri_complete": "https://next.frame.io/pair/573131"
}
```





#### Разбор ответа




* **device_code**: этот внутренний идентификатор должен оставаться скрытым от пользователей. Он идентифицирует запрос авторизации во время опроса.
* **expires_in**: срок действия кода в секундах.
* **interval**: рекомендуемый интервал опроса в секундах.
* **имя**: идентификатор подключаемого устройства.
* **user_code**: шестизначный код для ввода вручную в Frame.io для сопряжения устройства.
* **verification_uri**: базовый URL для ввода вручную, если сканирование QR-кода недоступно.
* **verification_uri_complete**: полный URL, содержащий код сопряжения и предназначенный для создания гиперссылки в мобильном приложении или генерации QR-кода для упрощения навигации пользователя к интерфейсу сопряжения.




### Отображение QR-кода для пользователя

Используя `verification_uri_complete`, создайте и отобразите QR-код на экране устройства, чтобы пользователь мог его отсканировать. Это сделает сопряжение удобнее и эффективнее.

#### Пример: экран устройства с показанным QR-кодом

![Пример экрана сопряжения устройства с QR-кодом](/_fern-img/193d938af906dbcbc9699953e9da5ddbeeee8cd75fdfabf0661127c2b528cdb4.webp) Всегда предоставляйте резервные варианты: отображайте `user_code` и `verification_uri` для ввода вручную, если отсканировать QR-код невозможно. Можно также показать `verification_uri` в виде статического QR-кода для сканирования с мобильного устройства. Для интеграции мобильных приложений включите `verification_uri_complete` как действующую гиперссылку, поскольку пользователи не могут сканировать QR-коды с устройства, на котором работает приложение.

## Шаг 2. Опрос для авторизации пользователя





После предоставления кода сопряжения или URL-кода проверьте ввод пользователя с помощью этого запроса:





```
curl -X POST https://api.frame.io/v2/auth/token \
    --form 'client_id=[client_id]' \
    --form 'device_code=[device_code]' \
    --form 'grant_type=urn:ietf:params:oauth:grant-type:device_code' \
    | python -m json.tool
```





### Параметры полезной нагрузки




* **client_id**: тот же идентификатор, который использовался в шаге 1.
* **device_code**: значение `device_code`, возвращенное ранее.
* **grant_type**: идентификатор типа разрешения OAuth, неизменно `urn:ietf:params:oauth:grant-type:device_code` для данной реализации.




Первоначальные попытки опроса обычно возвращают:





```
{
  "error": "authorization_pending"
}
```





Эта некритическая ошибка указывает, что пользователь не завершил ввод кода. Продолжайте опрос до завершения.



<blockquote>**Примечание для устройств под управлением iOS:** если пользователь переключается на iOS-приложение Frame.io для ввода кода сопряжения, ваше приложение может перейти в фоновый режим. Когда ваше приложение снова станет активным, например в `applicationDidBecomeActive`, возобновите опрос, чтобы процесс авторизации продолжался, и пользователям не приходилось перезапускать сопряжение.</blockquote>



Если вы получаете:





```
{
  "error": "expired_token"
}
```





Срок действия кода истек до ввода пользователем. Создайте новый код/QR-код, повторив шаг 1, предоставьте его пользователю и возобновите опрос.





При успешной авторизации выводится сообщение:





```
{
  "access_token": "[access_token]",
  "expires_in": 28800,
  "refresh_token": "[refresh_token]",
  "token_type": "bearer"
}
```





Поздравляем с успешной авторизацией вашего устройства «С камеры в облако»!





Рассмотрим этот ответ:




* **access_token**: учетные данные аутентификации для доступа к серверной части Frame.io, необходимые в заголовках для будущих запросов API-интерфейса.
* **expires_in**: срок действия токена доступа в секундах, после которого необходимо обновление.
* **refresh_token**: используется для управления токенами доступа, в основном для обновления авторизации, но также применим для отзыва.
* **token_type**: постоянно `bearer` для реализации API-интерфейса C2C , не требует действий.




## Объединяем шаги





Теперь реализуем эти вызовы API-интерфейса в псевдокоде Python, обрабатывая возможное истечение срока действия кода устройства:





**`Python`**

```python title="Python"
def authorize_with_frame():
    """
    Handles authorizing our device with Frame.io.
    """

    # Our client ID can be a serial number, UUID, or some other unique string.
    client_id = THIS_DEVICE.get_serial_number()

    while True:
        # Make the call to Frame.io to get our device codes.
        pairing_codes = c2c.get_device_codes(client_id)

        # We need to keep track of how long we have been polling for
        polling_started = datetime.now()

        # Now we are going to poll for authorization until the user enters the code.
        while True:

            # Re-write this output each time we poll. Note: This message will only update once
            # per `interval` (e.g., 5 seconds), so if a smooth countdown is desired, that will
            # need a different implementation.
            print(
                f"\rPAIRING CODE: {pairing_codes.user_code}, "
                f"EXPIRES IN: {pairing_codes.expires_in - (datetime.now() - polling_started).seconds} seconds"
            )

            # Wait for `interval` before polling each time.
            sleep(pairing_codes.interval)

            # Make a call to Frame.io to see if the user has entered the code and authorized
            # the device.
            authorization, error = c2c.poll_for_authorization(
                client_id, pairing_codes.device_code
            )

            if error and error.message == "authorization_pending":
                # If the authorization is pending, try again.
                continue
            elif error and error.message == "expired_token":
                # If the pairing codes have expired, break to generate new codes.
                break
            elif error:
                # If there was some other error, raise it.
                raise error
            else:
                # If there was no error, we have our authorization!
                return authorization

        # If we get here, our pairing codes expired. Let's try again.
        print("\nPairing code expired. Generating a new one...")
```

**Примечание.** Внешний цикл обрабатывает случаи, когда срок действия кодов сопряжения истекает и требуются новые коды.

На последнем шаге извлеките и отобразите информацию о проекте из Frame.io для подтверждения успешного сопряжения с намеченным проектом. Мы рассмотрим это в следующем руководстве.





## Создание и отображение QR-кодов для сопряжения

При реализации сопряжения по URL/QR-коду вам потребуется cоздать QR-код из значения `verification_uri_complete` в ответе. Вот примеры с использованием популярных библиотек на разных языках программирования:

### Пример на Python с использованием qrcode





**`Python`**

```python title="Python"
import qrcode
from PIL import Image
import io

def generate_qr_code(verification_uri_complete, size=250):
    """
    Generate a QR code from the verification_uri_complete URL.
    
    Args:
        verification_uri_complete (str): The complete verification URI returned by Frame.io
        size (int, optional): Size of the QR code in pixels. Defaults to 250.
    
    Returns:
        PIL.Image: QR code image that can be displayed or saved
    """
    qr = qrcode.QRCode(
        version=1,
        error_correction=qrcode.constants.ERROR_CORRECT_L,
        box_size=10,
        border=4,
    )
    qr.add_data(verification_uri_complete)
    qr.make(fit=True)
    
    img = qr.make_image(fill_color="black", back_color="white")
    
    # Resize the image if needed
    img = img.resize((size, size))
    return img

# Example usage in authorization flow
def display_qr_for_pairing(pairing_codes):
    """
    Generate and display QR code along with manual pairing instructions.
    """
    if hasattr(pairing_codes, 'verification_uri_complete'):
        # Generate QR code from the verification URI
        qr_img = generate_qr_code(pairing_codes.verification_uri_complete)
        
        # Display the QR code on screen
        # For GUI applications like Tkinter, PyQt, etc.
        # display_image(qr_img)
        
        # For headless devices or testing, save to file
        qr_img.save("frame_io_pairing_qr.png")
        
        print(f"Scan the QR code or visit: {pairing_codes.verification_uri}")
        print(f"Manual code: {pairing_codes.user_code}")
    else:
        # Fallback for devices that received traditional pairing response
        print(f"Enter code on Frame.io: {pairing_codes.user_code}")
```





### Пример на JavaScript (веб-версия или Electron)





```javascript
import QRCode from 'qrcode';

/**
 * Generate and display a QR code from the verification URI
 * @param {string} verificationUriComplete - The complete verification URI from Frame.io
 * @param {string} elementId - ID of the HTML element to display the QR code in
 */
function displayQRCode(verificationUriComplete, elementId = 'qrcode-container') {
  const element = document.getElementById(elementId);
  
  if (!element) {
    console.error(`Element with ID ${elementId} not found`);
    return;
  }
  
  // Clear any existing content
  element.innerHTML = '';
  
  // Generate QR code
  QRCode.toCanvas(element, verificationUriComplete, { width: 250 }, function(error) {
    if (error) {
      console.error('Error generating QR code:', error);
      // Fallback to displaying the URL as a link
      element.innerHTML = `<a href="${verificationUriComplete}" target="_blank">Click here to pair</a>`;
    }
  });
  
  // Also display manual pairing information
  const manualInfoDiv = document.createElement('div');
  manualInfoDiv.innerHTML = `
    <p>Scan the QR code or <a href="${verificationUriComplete}" target="_blank">click here</a> to pair your device.</p>
    <p>Manual code: ${userCode}</p>
  `;
  element.parentNode.appendChild(manualInfoDiv);
}

// Example usage in authorization flow
async function requestDeviceCode() {
  try {
    const response = await fetch('https://api.frame.io/v2/auth/device/code', {
      method: 'POST',
      headers: {
        'x-client-version': '2.0.0',
        'x-client-platypus-enabled': 'true'
      },
      body: new URLSearchParams({
        'client_id': YOUR_CLIENT_ID,
        'client_secret': YOUR_CLIENT_SECRET,
        'scope': 'asset_create offline'
      })
    });
    
    const data = await response.json();
    
    if (data.verification_uri_complete) {
      displayQRCode(data.verification_uri_complete);
      window.userCode = data.user_code; // Store for display purposes
    } else {
      // Fallback for traditional pairing
      displayManualPairingCode(data.user_code);
    }
    
    // Begin polling for authorization
    beginPollingForAuthorization(data.device_code, data.interval);
    
  } catch (error) {
    console.error('Error requesting device code:', error);
  }
}
```





### Пример для Android (Java)





```java
import android.graphics.Bitmap;
import android.widget.ImageView;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import com.journeyapps.barcodescanner.BarcodeEncoder;

public void generateAndDisplayQRCode(String verificationUriComplete, ImageView qrCodeImageView) {
    try {
        MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
        BitMatrix bitMatrix = multiFormatWriter.encode(verificationUriComplete, 
            BarcodeFormat.QR_CODE, 250, 250);
        BarcodeEncoder barcodeEncoder = new BarcodeEncoder();
        Bitmap bitmap = barcodeEncoder.createBitmap(bitMatrix);
        
        // Display in ImageView
        qrCodeImageView.setImageBitmap(bitmap);
        
    } catch (Exception e) {
        e.printStackTrace();
        // Fallback to displaying the URL as text
    }
}
```





### Пример для iOS (Swift)





```swift
import UIKit
import CoreImage

func generateQRCode(from string: String) -> UIImage? {
    let data = string.data(using: String.Encoding.utf8)
    
    if let filter = CIFilter(name: "CIQRCodeGenerator") {
        filter.setValue(data, forKey: "inputMessage")
        filter.setValue("H", forKey: "inputCorrectionLevel")
        
        if let outputImage = filter.outputImage {
            // Scale the image
            let transform = CGAffineTransform(scaleX: 10, y: 10)
            let scaledImage = outputImage.transformed(by: transform)
            
            // Convert to UIImage
            let context = CIContext()
            if let cgImage = context.createCGImage(scaledImage, from: scaledImage.extent) {
                return UIImage(cgImage: cgImage)
            }
        }
    }
    
    return nil
}

// Usage in your view controller
func displayPairingQRCode(verificationUriComplete: String) {
    if let qrCodeImage = generateQRCode(from: verificationUriComplete) {
        qrCodeImageView.image = qrCodeImage
        
        // Also show manual pairing information
        pairingInstructionsLabel.text = "Scan the QR code or enter code manually"
        pairingCodeLabel.text = userCode
    } else {
        // Fallback to manual code display
        pairingInstructionsLabel.text = "Enter this code on Frame.io:"
        pairingCodeLabel.text = userCode
    }
}
```





## Лучшие практики для отображения QR-кодов





При реализации сопряжения через QR-код учитывайте эти рекомендации, чтобы сделать работу пользователей удобнее:




1. **Оптимальный размер**. Отображайте QR-коды размером не менее 200–250 пикселей по каждой стороне для надежного сканирования.




2. **Контрастность**. Необходима высокая контрастность между QR-кодом и фоном (идеальный вариант — черный на белом).




3. **Исправление ошибок**. Используйте умеренные уровни исправления ошибок (L или M), чтобы обеспечить баланс между плотностью кода и надежностью.




4. **Четкие инструкции**. Давайте четкие указания по сканированию кода, например «Для сопряжения устройства отсканируйте этот код камерой вашего смартфона».




5. **Несколько вариантов**. В качестве резервного варианта всегда предоставляйте вместе с QR-кодом код для сопряжения вручную:


   

```
   Scan to pair:
   [QR CODE]
   
   Or enter code manually: 573131
```




6. **Гиперссылка для мобильных приложений**. Если ваша интеграция — это мобильное приложение, включите `verification_uri_complete` как действующую гиперссылку, поскольку пользователи не могут отсканировать QR-код с того же устройства.




7. **Тестирование**. Протестируйте QR-коды с различными устройствами и в разных условиях освещения, чтобы гарантировать надежное сканирование.

![Пример отображения QR-кода](/_fern-img/193d938af906dbcbc9699953e9da5ddbeeee8cd75fdfabf0661127c2b528cdb4.webp)

## Поиск и устранение неисправностей





Если возникают проблемы, ознакомьтесь со следующими распространенными сценариями и вариантами решения проблем.




* **Кнопка «Подключить устройство» не отображается**. При доступе к панели управления C2C это может указывать на:

* **Отсутствие необходимых разрешений**. Если вы видите сообщение, касающееся разрешений, обратитесь к менеджеру учетной записи для получения разрешений или назначения соответствующей роли. * **Существующее подключение устройства**. После подключения одного устройства вместо первоначальной кнопки «Добавить новое устройство» появляется меню с тремя точками в правом верхнем углу панели «Подключения C2C».
* **Неверный клиент**. Ответ `invalid_client` указывает на несоответствие информации об устройстве, обычно из-за неправильного `client_secret`.
* **Неправильный запрос**. Ответ `bad_request` указывает на неправильно сформированные данные запроса. Проверьте имена полей и убедитесь, что включены все обязательные поля.




Если ваша проблема здесь не описана, поделитесь с нами своим опытом, чтобы мы могли улучшить раздел, посвященный устранению неполадок.





## Дальнейшие шаги

Мы рекомендуем вам связаться с нашей командой и перейти к [руководству по управлению авторизацией](./how-to-authorization-management). Мы ждем ваших отзывов!