【问题标题】:How can a webapp using the Google Drive REST API share files with another user using the same app?使用 Google Drive REST API 的网络应用程序如何与使用同一应用程序的其他用户共享文件?
【发布时间】:2020-03-15 23:04:16
【问题描述】:

我的网络应用程序是一个没有后端服务器的 3D 虚拟游戏桌面...相反,它使用 Google Drive 来存储用户数据和图像。我有一个具体的问题,但我会更全面地描述我的情况,以防在我的问题级别之上的某个地方有解决方案。

用例

  1. 用户A,游戏大师(GM),使用该应用上传一张地图的图像,以及一些怪物的一些图像。应用将图像上传到它在用户 A 的 Google 云端硬盘中创建的文件夹结构中,然后应用通过链接将它们标记为可读。
  2. 在用户 A 进行配置后,应用程序会显示一个虚拟桌面,这是一个 3D 空间,其中包含一张地图,上面有一些怪物。描述桌面的 JSON 数据也存储在 Google Drive 中,应用程序将其标记为可通过链接读取。
  3. 用户 A 与播放器用户 B 共享 URL。该链接指向我的应用,但包含用户 A 驱动器中描述桌面的 JSON 文件的 Google 驱动器文件 ID。
  4. 用户 B 访问该链接。应用加载完毕,使用 ID 从用户 A 的驱动器中读取 JSON 文件,然后呈现桌面,根据需要从用户 A 的驱动器中加载图像。

问题

Google Drive 的 drive.files oAuth 范围足以执行用例的步骤 1-3 - 该应用能够为用户 A 创建和读取 Google Drive 文件。

但是,仅使用 drive.files 范围似乎无法执行第 4 步。

  • 用户 B 已授予应用 drive.files 对其云端硬盘的访问权限。
  • 文件由应用创建(在步骤 1-2 中)
  • 用户 B 具有文件的读取权限(在步骤 1-2 中授予)
  • Google 云端硬盘不允许应用访问文件,因为用户 B 未授予应用访问文件的权限。

drive.files 的文档将其描述为“对应用程序创建或打开的文件的按文件访问。文件授权是按用户授予的,并在用户取消对应用程序的授权时被撤销。”但是,这似乎并不完全正确,因为 Drive 似乎不会记录文件是否由应用程序创建。似乎相反,当应用程序创建文件时,当前用户对这些文件的访问权限被隐式授予应用程序,然后忘记了该文件是由应用程序创建的事实。

当前的解决方法是应用还需要 drive.readonly oAuth 范围。这是一个不合理的访问级别,我知道许多用户(相当合理地)决定他们不愿意授予我的应用程序对其整个驱动器的只读访问权限。这也是一个“受限”的 oAuth 范围,但我已经通过 Google 完成了应用验证流程。

问题

是否可以让我的应用程序授予用户 B 对文件的读取访问权限,而无需使用受限级别的 oAuth 范围,无需用户 B 做太多工作,同时保持纯粹的客户端?如果有,怎么做?

有问题的解决方案

如上所述,使用drive.readonly oAuth 范围有效,但不合理。

我相信可以为我的应用程序创建云端硬盘集成,这将允许用户右键单击文件并“打开”我的应用程序,这将授予应用程序访问文件的权限。然而,

  • 涉及大量文件 - 描述桌面的 JSON 文件、桌面上每张地图和生物的单独图像,以及其他文件。此外,可以在游戏中途将新图像拖放到桌面上。
  • 用户 B 是一名玩家,并且没有(也不应该有)在 Drive GUI 中浏览 GM 文件的权限,以便右键单击它们并“打开方式”应用程序。他们应该看不到尚未添加到桌面的怪物或地图。为此,应用通过链接授予文件读取权限,但不授予包含这些文件的目录的读取权限。

可以有一个自定义服务器来提供来自云端硬盘的内容,但我试图让应用程序纯粹是客户端。

技术

如果相关,应用程序是用 Javascript(实际上是 Typescript)编写的,Drive API 调用是通过 Javascript Google Drive REST API 完成的。

【问题讨论】:

    标签: javascript web-applications google-drive-api google-oauth


    【解决方案1】:

    如你所见in the official documentationdrive.files 让你:

    查看和管理使用此应用打开或创建的 Google 云端硬盘文件和文件夹

    由于文件不是代表User B 创建的,因此该应用无权访问它们。

    恐怕没有不受限制的范围会授予这个权限,所以你应该使用drive.readonly,或者完全重新设计共享过程。

    参考:

    【讨论】:

    • 谢谢!我怕是这样。您对如何使用云端硬盘重新设计共享流程并仍然允许类似于原始用例的内容有任何想法吗?
    • @RobertRendell 不客气。关于你的问题,我真的不知道。也许可以代表用户 B 创建文件的副本?或者,也许您可​​以考虑不为此使用云端硬盘。
    【解决方案2】:

    我找到了一种解决方法,可以处理我的用例的所有四个步骤。解决方法仅适用于我要共享的文件“任何人都可以通过链接读取” - 如果文件仅与某些人共享,它将不起作用。

    我在 iframe 中创建了第二个 GAPI 客户端,该客户端未经身份验证(甚至没有配置 oAuth 客户端 ID 或范围)。主 GAPI 客户端与以前一样用于登录用户(具有drive.file oAuth 范围)。

    function addGapiScript() {
        return new Promise((resolve, reject) => {
            const iframe = document.createElement('iframe');
            iframe.onload = () => {
                if (!iframe || !iframe.contentDocument || !iframe.contentWindow) {
                    reject(new Error('Failed to add iframe'));
                    return;
                }
                const script = iframe.contentDocument.createElement('script');
                script.onload = () => {
                    resolve(iframe.contentWindow['gapi']);
                };
                script.onerror = reject;
                script.src = 'https://apis.google.com/js/api.js';
                iframe.contentDocument.head.appendChild(script);
            };
            iframe.onerror = reject;
            iframe.src = '/blank.html'; // A src is required because gapi refuses to init in an iframe with a location of about:blank.
            document.body.appendChild(iframe);
        });
    }
    
    // Discovery docs for the Google Drive API.
    const DISCOVERY_DOCS = ['https://www.googleapis.com/discovery/v1/apis/drive/v3/rest'];
    // Authorization scopes required by the API; multiple scopes can be included, separated by spaces.
    const SCOPES = 'https://www.googleapis.com/auth/drive.file';
    
    let anonymousGapi;
    
    async function initialiseFileAPI(signInHandler, onerror) {
        // Jump through some hoops to get two gapi clients.
        // The first is "anonymous", i.e. does not log in
        anonymousGapi = window['anonymousGapi'] = await addGapiScript();
        anonymousGapi.load('client', {
            callback: async () => {
                await anonymousGapi.client.init({
                    apiKey: API_KEY,
                    discoveryDocs: DISCOVERY_DOCS
                });
            },
            onerror
        });
        // The second is the normal gapi that we log in.
        gapi.load('client:auth2', {
            callback: async () => {
                await gapi.client.init({
                    apiKey: API_KEY,
                    discoveryDocs: DISCOVERY_DOCS,
                    clientId: CLIENT_ID,
                    scope: SCOPES
                });
                // Listen for sign-in state changes.
                gapi.auth2.getAuthInstance().isSignedIn.listen(signInHandler);
                // Handle initial sign-in state.
                signInHandler(gapi.auth2.getAuthInstance().isSignedIn.get());
            },
            onerror
        });
    }
    

    如果经过身份验证的客户端无法读取文件,代码将退回到未经过身份验证的客户端,该客户端能够匿名读取文件。

    async function driveFilesGet(params) {
        // Do a regular drive.files.get, but fall back to anonymous if it throws a 404 error
        try {
            return await gapi.client.drive.files.get(params);
        } catch (err) {
            if (err.status === 404) {
                // Attempt to get the file data anonymously
                return await anonymousGapi.client.drive.files.get(params);
            }
            throw err;
        }
    }
    

    很遗憾,响应匿名请求的云端硬盘文件元数据似乎不包含该应用的任何 appProperties 数据,尽管请求中存在应用的 API_KEY。但是,元数据可以包含properties

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-17
      • 2014-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-20
      相关资源
      最近更新 更多