Практическое руководство: авторизация (приложение)

Введение

В этом руководстве мы расскажем, как выполнять аутентификацию и авторизацию приложения C2C в проекте Frame.io.

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

Если вы еще не читали руководство Реализация C2C: настройка, просмотрите его перед тем, как продолжить!Также вы должны были получить от нашей команды client_id, который будет использоваться для идентификации вашей интеграции. Если вы не получили client_id, ознакомьтесь с этим введением в экосистему C2C и свяжитесь с нашей командой. Если вы получили client_secret вместо client_id, значит выполнена настройка как устройства, а не приложения C2C, и вам следует либо следовать руководству по авторизации устройства, либо обратиться к нашей команде для получения client_id.

Пошаговое руководство по потоку аутентификации приложения

Убедитесь, что у вас есть достаточно полное понимание ожидаемого пользовательского интерфейса для потока авторизации, который мы хотим реализовать. Ознакомьтесь со следующими ресурсами, чтобы увидеть этот поток с точки зрения пользователя, попробуйте загрузить Zoelog и войти в Frame.io, чтобы понять как работает процесс авторизации для приложений C2C!

Обзор OAuth

Приложения C2C используют поток OAuth 2.0 для аутентификации и авторизации. Это стандартизированный набор вызовов, который можно использовать для аутентификации и авторизации стороннего приложения или пользователя для службы. Можно подробнее прочитать о потоке OAuth здесь.

URI обратного вызова/перенаправления

В рамках потока OAuth наши серверы должны будут выполнить вызов HTTP к URI/URL, который вы контролируете. После того как пользователь войдет в Frame.io в своем браузере, мы перенаправим браузер на этот URI, чтобы предоставить вашему приложению некоторую информацию. Ваш URI перенаправления должен:

  • принадлежать вам;
  • быть статическим.

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

Во время потока OAuth мы проверим, является ли URI обратного вызова, запрашиваемый вашим приложением, одним из URI, которые у нас есть в файле. Если это не так, поток авторизации завершится сбоем. Если бы мы не проводили эту проверку, злоумышленник мог бы предоставить перенаправление на адрес, который он контролирует.

В целях разработки мы поддерживаем обратные вызовы без HTTPS на http://localhost.

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

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

Для приложений C2C мы называем это device_id устройства. При настройке реализации вам следует подумать о том, как вы будете это делать. Некоторые платформы предлагают API-интерфейс для создания идентификатора для устройства и приложения специально для этого случая:

ПЛАТФОРМАСПРАВОЧНАЯ ИНФОРМАЦИЯ
iOSidentifierForVendor
AndroidFID или GUID
Будьте осторожны, чтобы не раскрыть личную идентификационную информацию

Адрес электронной почты пользователя, например не является допустимым значением для использования в device_id. Также убедитесь, что у вас есть уникальным идентификатором. Не используйте MAC-адрес устройства, например. MAC-адрес не принадлежит вашему программному обеспечению и также может считаться личной идентифицирующей информацией.

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

Шаг 1. Аутентификация пользователя

Когда я решаю, что хочу подключиться к Frame.io в YourApp™, я перехожу в раздел конфигурации Frame.io и выбираю «Connect to Project» (или что-то подобное). Когда я нажимаю кнопку, меня перенаправляют на Frame.io для входа в систему и авторизации вашего приложения.

Мы делаем это, создавая URL-адрес и открывая его в веб-браузере. Рассмотрим псевдокод, похожий на Python:

Python
1def redirect_to_auth(config):
2 credentials = {
3 "response_type": "code",
4 "redirect_uri": "http://MyApp.io/frameio-callback",
5 "client_id": f"{MYAPP.client_id}",
6 "scope": "offline device.connect asset.create",
7 "state": str(uuid.uuid4()),
8 "device_id": f"{HARDWARE.get_vendor_id('com.mycompany.myapp')}",
9 }
10
11 encoded = parse.urlencode(credentials)
12 url = "https://applications.frame.io/oauth2/auth?" + encoded
13
14 webbrowser.open(url)

Наша «полезная нагрузка» кодируется в самом URL-адресе, и в полностью закодированном виде URL будет выглядеть примерно так:

https://applications.frame.io/oauth2/auth?response_type=code&redirect_uri=http%3A%2F%2FMyApp.io%2Fframeio-callback&client_id=[client_id]&scope=offline+device.connect+asset.create&state=[state]&device_id=62f88d2a-1ae1-45e7-a6a0-81954e0cf2ff

Разберем эти параметры подробнее:

response_type: ответ, который должен возвращать поток OAuth. Это значение всегда должно быть «code». Это указывает нашему серверу OAuth, что нужно отправить код обратно на URI перенаправления, который затем будет использоваться для получения фактических токенов авторизации. redirect_uri: адрес URL/URI, на который сервер OAuth должен отправить запрос GET при ответе на запрос авторизации. client_id: идентифицирует приложение. Для интеграций приложений это значение будет предоставлено Frame.io. scope: список разделенных пробелами разрешений, которые запрашивает ваше приложение. Следующие разрешения доступны для приложений C2C

  • offline: приложение может обновить свою авторизацию, когда срок действия первоначального токена истекает.
  • device.connect: устройство может получить список учетных записей и проектов, доступных для подключений C2C пользователем.
  • asset.create: приложение может добавлять ресурсы в проекты, к которым оно подключено.

Хотя можно запросить и получить часть этих областей доступа, вам всегда нужно будет запрашивать все три из них.

state: случайное значение, связанное с этим запросом. Мы используем state для проверки того, что вызовы нашего URI перенаправления предназначены для действительных запросов. Когда вы получаете обратный вызов на зарегистрированный URI, вы должны проверить, что state является ожидаемым.

Параметр state должен быть произвольным”> Если параметр state не является произвольным, вы подвергаете себя атакам CRSF, когда злоумышленник подделывает ваш параметр state и делает неправильный запрос к вашему обратному вызову. Можно узнать больше о параметре state в этом блоге Auth0

device_id: уникальный идентификатор для этого конкретного устройства/установки. Идентификатор устройства должен быть значением принадлежащим вам (то есть не MAC-адресом/серийным номером процессора и т. д.) и не должен содержать личную идентифицирующую информацию (то есть никаких адресов электронной почты, номеров социального страхования, хеш-отпечатков и т. д.). См. раздел выше о device_id для получения дополнительной информации.

Шаг 2. Получение ответа OAuth

После того как пользователь вошел в Frame.io и принял запрошенные области действия в своем браузере, запрос GET отправляется на ваш URI обратного вызова. Запрос содержит URL-кодированную полезную нагрузку со следующими параметрами запроса: code — код, который будет использоваться для получения фактических токенов авторизации из сервера Frame.io. state — значение state, которое было включено в исходный запрос аутентификации на шаге 1. scope — предоставленные области действия/разрешения.

Полный URI будет выглядеть примерно так:

https://MyApp.io/frameio-callback?code=[authorization_code]&scope=offline+device.connect+asset.create&state=[state]

Разбор URI может быть непростым, и ваша HTTP/серверная библиотека, скорее всего, имеет хорошие ресурсы для этого, поэтому ознакомьтесь с ними, прежде чем разбирать это значение самостоятельно!

Для тестирования мы можем быстро настроить сервер, чтобы просматривать запрос GET с помощью Python. URI обратного вызова должен быть настроен как http://localhost:8888/callback

$$ python -m http.server 8888

Теперь мы можем использовать следующий шаблон для запроса доступа к Frame.io. Заполните [client_id] и значение [state]. Можно создать случайный UUID здесь для state.

https://applications.frame.io/oauth2/auth?response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8888%2Fcallback&client_id=[client_id]&scope=offline+device.connect+asset.create&state=[state]&device_id=62f88d2a-1ae1-45e7-a6a0-81954e0cf2ff

Пройдя через поток аутентификации мы получим ошибку 404. Это потому, что Python не распознает запрашиваемый ресурс и не знает, как на него ответить. Но не беспокойтесь, запрос аутентификации все равно выполнен! Наш сервер должен вывести что-то вроде этого в наш терминал:

Serving HTTP on :: port 8888 (http://[::]:8888/) ...
::1 - - [08/Mar/2022 14:04:34] code 404, message File not found
::1 - - [08/Mar/2022 14:04:34] "GET /callback?code=[authentication_code]&scope=offline+device.connect+asset.create&state=[state] HTTP/1.1" 404 -

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

В реальном приложении обработчик обратных вызовов может выглядеть примерно так:

Python
1@handler("/frameio-callback")
2def do_get(request):
3 params = url.parse_query(request.url.parts.query)
4 if "error" in params:
5 raise AuthError(params["error"])
6
7 # Handles sending the authentication code and state to the proper user
8 MyApp.frameio_oauth_success(state=params["state"], code=params["code"])
9
10 # Render some sort of confirmation page for the user.
11 request.send_response(
12 code=200,
13 headers={"Content-type": "text/html"},
14 data=OauthSuccessPage()
15 )

Шаг 3. Получение наших токенов доступа

Теперь, когда у нас есть наш authorization_code, мы можем получить наш токен доступа! На данном этапе наш токен доступа уже предоставлен, нам просто нужно запросить его у сервера.

Сделаем следующий запрос:

$curl -X POST https://applications.frame.io/oauth2/token \
> --form 'client_id=[client_id]' \
> --form 'state=[state]' \
> --form 'code=[authorization_code]' \
> --form 'redirect_uri=http://localhost:8888/callback' \
> --form 'grant_type=authorization_code' \
> --form 'scope=offline device.connect asset.create' \
> | python -m json.tool
Конечные точки OAuth

Обратите внимание, что хост для этого запроса — applications.frame.io, в отличие от api.frame.io, который мы используем для большинства запросов. Также обратите внимание, что мы используем здесь данные формы, а не данные JSON. Конечные точки OAuth C2C принимают только данные формы.

После аутентификации другие конечные точки будут принимать полезную нагрузку application/json, но конечные точки аутентификации вернут ошибку, если вы отправите JSON вместо данных application/x-www-form-urlencoded.

Рассмотрим эти параметры:

client_id: идентификатор приложения OAuth, выданный нам Frame.io state: значение state, которое мы включили в наш исходный запрос на авторизацию в браузере и получили в обратном вызове. code: код авторизации, который мы получили в обратном вызове redirect_uri: тот же URI перенаправления, который мы зарегистрировали в сервере Frame.io. Если это значение отсутствует в списке URL-адресов, разделенных запятыми, которые есть у Frame.io для вашей интеграции, запрос завершится сбоем. grant type: для потока авторизации программного устройства всегда будет authorization_code. scope: должно совпадать с утвержденными областями доступа, возвращенными в обратном вызове.

Мы должны получить ответ, который выглядит примерно так:

1{
2 "access_token": "[access_token]",
3 "expires_in": 3599,
4 "refresh_token": "[refresh_token]",
5 "scope": "offline device.connect asset.create",
6 "token_type": "bearer"
7}

Наше устройство теперь авторизовано в Frame.io! Держите эти значения под рукой, так как они понадобятся нам для остальных запросов. Посмотрим, что содержится в полезной нагрузке:

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

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

Шаг 4. Список учетных записей

Далее нам нужно получить список учетных записей, к которым наш пользователь имеет право подключиться. Это первый вызов, для которого требуется наш токен доступа, и мы добавим его в заголовок:

$curl -X GET https://api.frame.io/v2/devices/accounts \
> --header "x-client-version: 2.0.0" \
> --header "Authorization: Bearer [access_token]" \
> | python -m json.tool
Спецификация конечной точки API-интерфейса

Документацию для /v2/devices/accounts можно найти здесь

Заголовок авторизации

Для каждой конечной точки, которая требует авторизации, нам нужно добавить access_token в заголовок Authorization. Обратите внимание, нам нужно добавить в начало Bearer (с пробелом!) к нашему токену доступа в качестве значения.

Этот вызов должен вернуть список учетных записей, к которым может подключиться пользователь:

1[
2 {
3 "_type": "account",
4 "display_name": "Hogwarts General",
5 "id": "46b7ea11-3041-4e2b-97f7-98fbf5c974c9"
6 },
7 {
8 "_type": "account",
9 "display_name": "Gryffindor",
10 "id": "e6007a3d-cad7-4666-9ee3-23c1af032060"
11 },
12 {
13 "_type": "account",
14 "display_name": "QUIDDITCH LEGENDS -- LETS GOOOOOOOOOO",
15 "id": "cc94119d-f957-4d6e-b8cf-0c095211b1b9"
16 }
17]

На этом этапе вы бы отобразили этот список пользователю и позволили ему выбрать учетную запись, к которой он хочет подключиться. Затем использовали бы ИД учетной записи на следующем этапе, чтобы перечислить проекты, к которым пользователь может подключить устройство C2C.

Шаг 5. Список проектов

Теперь нам нужно получить список проектов для учетной записи, которая нас интересует:

$curl -X GET https://api.frame.io/v2/devices/accounts/[account_id]/projects \
> --header "x-client-version: 2.0.0" \
> --header "Authorization: Bearer [access_token]" \
> | python -m json.tool
Спецификация конечной точки API-интерфейса

Документацию для /v2/devices/accounts/[account_id]/projects можно найти здесь

Нам нужно добавить в URL-адрес account_id, для которого мы пытаемся получить список проектов. Также обратите внимание, что общий путь к ресурсу начинается с /devices/.... Мы не просто получаем список проектов, мы получаем список проектов, на которые у пользователя есть разрешения для управления устройствами C2C. Если проект, к которому принадлежит пользователь, не отображается в списке, это означает, что у него нет разрешений для управления устройствами C2C для данного проекта.

Мы получим ответ, аналогичный тому, что получили для учетных записей:

1[
2 {
3 "_type": "project",
4 "id": "921480ec-1225-424a-9447-19c61a3a1ef2",
5 "name": "Match Recordings"
6 },
7 {
8 "_type": "project",
9 "id": "ed5dbf4a-f146-416b-add0-74de98201876",
10 "name": "Year Book Material"
11 }
12]

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

Шаг 6. Подключение к проекту

Теперь, когда пользователь выбрал проект, к которому хочет подключиться, мы готовы к работе! Остался лишь один последний шаг для завершения сопряжения нашего программного устройства с проектом Frame.io:

$curl -X POST https://api.frame.io/v2/devices/connect?project_id={project_id} \
> --header "x-client-version: 2.0.0" \
> --header "Authorization: Bearer [access_token]" \
> | python -m json.tool
Спецификация конечной точки API-интерфейса

Документацию для /v2/devices/connect можно найти здесь

Идентификатор проекта является параметром запроса URL, и нам все еще нужно передать наш заголовок авторизации!

Мы получаем такой ответ (некоторые данные опущены для краткости):

1{
2 "_type": "project_device",
3 "asset_type": "video",
4 "authorization": {
5 "_type": "project_device_authorization",
6 "creator": {
7 "_type": "user",
8 "account_id": "93f872fb-9924-4e31-a430-2574e0742260",
9 "deleted_at": null,
10 "email": "hpotter@hoggyhoggyhogwarts.edu",
11 "id": "d473b5e1-08d2-4842-82ac-a6b01233dc2c",
12 ...
13 "name": "Harry Potter",
14 ...
15 },
16 "creator_id": "d473b5e1-08d2-4842-82ac-a6b01233dc2c",
17 "expires_at": null,
18 "id": "ee9f5949-b7fa-4c71-8480-6d4c60877c51",
19 "inserted_at": "2022-03-09T18:14:21.893283Z",
20 "project_device_id": "6a55d7f6-dfb7-46a1-bff8-a3acb2d3d1aa",
21 "scopes": {
22 ...
23 "asset_create": true,
24 ...
25 "id": "1e174fe9-5b53-48db-b556-c310c0848898",
26 "offline": true,
27 ...
28 }
29 },
30 "channels": [
31 {
32 "_type": "project_device_channel",
33 "asset_type": "video",
34 ...
35 }
36 ],
37 "creator_id": "d473b5e1-08d2-4842-82ac-a6b01233dc2c",
38 "deleted_at": null,
39 "device_id": "a8a4f3bf-196c-4748-832b-28f1d0801515",
40 "id": "93af90e7-ee89-4b47-86e6-c2750f3790b6",
41 ...
42 "name": "MyApp-62f88d2a-1ae1-45e7-a6a0-81954e0cf2ff",
43 "project": {
44 "_type": "project",
45 "id": "921480ec-1225-424a-9447-19c61a3a1ef2",
46 "name": "Testbed"
47 },
48 "project_id": "921480ec-1225-424a-9447-19c61a3a1ef2",
49 "status": "online",
50 ...
51}
Один проект за раз

Можно выполнять сопряжение устройства только с одним проектом за раз, а выполнение операции сопряжения с другим проектом удаляет связь с ранее подключенным проектом.

Если полезная нагрузка вашего ответа выглядит именно так «Поздравляем!»,у вас получилось! вы авторизовали ваше первое устройство «С камеры в облако». Все идет отлично!

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

Использование сторонней библиотеки OAuth

Frame.io использует стандартный поток Oauth2.0. Для обеспечения безопасности мы применяем PKCE. Существует множество библиотек для обработки этой части интеграции за вас.

Вот несколько популярных библиотек Oauth:

Язык программированияИмяURL-адрес
SwiftOAuthSwiftGithub
Pythonrequests-oauthlibGithub
Flutteroauth2_clientGithub
Помните, что Frame.io добавляет поле device_id для идентификации конкретного устройства. Это дополнительное настраиваемое поле. Выбранная вами библиотека скорее всего поддерживает настраиваемые поля, но не забудьте добавить ее!

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

Если вы оказались здесь, что-то пошло не так! Интеграции с третьей стороной без ошибок почти не бывает В этом разделе приведен набор распространенных проблем и описаны действия, которые наиболее вероятно их решат. Просмотрите следующий список и выясните, похоже ли что-то на вашу проблему. Руководство по ошибкам также является отличным ресурсом для поиска ошибок API-интерфейса.

Если здесь нет подходящего решения, сообщите нам и мы добавим!

Учетная запись или проект, которые я хочу подключить, не возвращены: если при просмотре списка учетных записей и/или проектов тот, к которому вы хотите подключиться, не отображается, может происходить несколько вещей. В Frame.io перейдите в проект, к которому хотите подключиться, и нажмите вкладку Подключения C2C. Это поможет разобраться в том, что идет не так.

  • C2C не включен для вашей учетной записи: если экран пустой и отображается сообщение о том, что C2C недоступен для вашей учетной записи, менеджер учетной записи должен включить его для вашего проекта в настройках учетной записи!
  • Вы не являетесь менеджером устройства: если экран пустой и отображается сообщение об отсутствии разрешений, менеджер учетной записи должен либо изменить разрешения для пользователей, имеющих право подключать устройства C2C, либо добавить вас в роль с такими разрешениями.

Ошибка недопустимого клиента: invalid_client возвращается, когда предоставляемая вами информация об устройстве не соответствует ничему из наших записей. Скорее всего, это означает, что ваши client_secret, client_id или redirect_uri не соответствуют тому, что Frame.io хранит в файле на сервере. Ошибка неверного запроса: bad_request возвращается, когда данные запроса имеют неправильный формат. Удостоверьтесь, что вы не сделали ошибку в написании название поля или не забыли добавить обязательное поле.

Далее

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