Moja aplikacja internetowa to wirtualny stół do gier 3D bez serwera zaplecza ... zamiast tego używa Dysku Google do przechowywania danych i obrazów użytkowników. Mam konkretne pytanie, ale dokładniej opiszę swoją sytuację na wypadek, gdyby było rozwiązanie gdzieś powyżej poziomu mojego pytania.

Przypadek użycia

  1. Użytkownik A, Game Master (GM), używa aplikacji do przesłania obrazu mapy i niektórych obrazów niektórych potworów. Aplikacja przesyła obrazy do struktury folderów, którą utworzyła na Dysku Google użytkownika A, a aplikacja oznacza je jako czytelne za pomocą linku.
  2. Po skonfigurowaniu przez użytkownika A aplikacja wyświetla wirtualny blat, przestrzeń 3D zawierającą mapę z kilkoma potworami. Dane JSON opisujące blat są również przechowywane na Dysku Google, a aplikacja oznacza go jako czytelny za pomocą linku.
  3. Użytkownik A udostępnia adres URL Użytkownikowi B, graczowi. Link prowadzi do mojej aplikacji, ale zawiera identyfikator pliku JSON na Dysku Google na dysku użytkownika A opisujący blat.
  4. Użytkownik B uzyskuje dostęp do łącza. Aplikacja ładuje się, używa identyfikatora do odczytu pliku JSON z Dysku użytkownika A, a następnie renderuje blat, ładując obrazy z Dysku użytkownika A.

Problem

Zakres oAuth drive.files Dysku Google jest wystarczający do wykonania kroków 1-3 przypadku użycia - aplikacja może tworzyć i odczytywać pliki na Dysku Google dla użytkownika A.

Jednak z zakresem tylko drive.files nie wydaje się możliwe wykonanie kroku 4.

  • Użytkownik B przyznał aplikacji drive.files dostęp do swojego Dysku.
  • Pliki zostały utworzone przez aplikację (w krokach 1-2)
  • Użytkownik B ma prawa odczytu plików (przyznane w krokach 1-2)
  • Dysk Google nie zezwala aplikacji na dostęp do plików, ponieważ Użytkownik B nie udzielił aplikacji uprawnień dostępu do plików.

Dokumentacja drive.files opisuje to jako „Dostęp do plików utworzonych lub otwartych przez aplikację. Autoryzacja plików jest przyznawana dla każdego użytkownika i jest cofana, gdy użytkownik cofnie autoryzację aplikacji”. Jednak nie wydaje się to do końca zgodne z prawdą, ponieważ Dysk nie wydaje się rejestrować, czy plik został utworzony przez aplikację. Wygląda na to, że zamiast tego, gdy aplikacja tworzy pliki, dostęp jest niejawnie przyznawany aplikacji dla bieżącego użytkownika dla tych plików, a następnie zapomina się o tym, że plik został utworzony przez aplikację.

Obecne obejście polega na tym, że aplikacja wymaga również zakresu OAuth drive.readonly. To nieracjonalny poziom dostępu i znam wielu użytkowników, którzy (całkiem rozsądnie) zdecydowali, że nie chcą przyznać mojej aplikacji dostępu tylko do odczytu do całego Dysku. Jest to również „ograniczony” zakres OAuth, ale przeszedłem przez proces weryfikacji aplikacji w Google.

Pytanie

Czy jest możliwe, aby moja aplikacja przyznała użytkownikowi B dostęp do odczytu plików bez korzystania z zakresów OAuth o ograniczonym poziomie, bez konieczności wykonywania zbyt dużej pracy ze strony użytkownika B i pozostając wyłącznie po stronie klienta? Jeśli tak to jak?

Problematyczne rozwiązania

Korzystanie z zakresu drive.readonly oAuth działa, ale jest nierozsądne, jak omówiono powyżej.

Uważam, że możliwe jest utworzenie integracji Dysku dla mojej aplikacji, która pozwoliłaby użytkownikowi kliknąć plik prawym przyciskiem myszy i „Otwórz za pomocą” mojej aplikacji, co umożliwi aplikacji dostęp do pliku. Jednak,

  • W grę wchodzi wiele plików - plik JSON opisujący blat, indywidualne obrazy dla każdej mapy i stworzenia na stole, a także inne pliki. Ponadto nowe obrazy mogą zostać umieszczone na stole w trakcie gry.
  • Użytkownik B jest graczem i nie ma (i nie powinien mieć) uprawnień do przeglądania plików GM w graficznym interfejsie użytkownika Dysku w celu kliknięcia ich prawym przyciskiem myszy i „Otwórz za pomocą” aplikacji. Nie powinni widzieć potworów ani map, które nie zostały jeszcze dodane do blatu. W tym celu aplikacja przyznaje dostęp do odczytu przez łącze do plików, ale nie do katalogów zawierających te pliki.

Byłoby możliwe, aby mieć niestandardowy serwer, który obsługuje zawartość z Dysku, ale staram się, aby aplikacja była czysto po stronie klienta.

Technologia

W przypadku, gdy jest to istotne, aplikacja jest napisana w języku JavaScript (a właściwie Typescript), a wywołania interfejsu Drive API są wykonywane za pośrednictwem interfejsu API REST dysku Google Drive w języku JavaScript.

0
Robert Rendell 16 marzec 2020, 02:04

2 odpowiedzi

Najlepsza odpowiedź

Znalazłem obejście, które pozwala obsłużyć wszystkie cztery kroki mojego przypadku użycia. To obejście działa tylko dlatego, że pliki, które chcę udostępnić, są „czytelne dla każdego, kto ma łącze” - nie działałoby, gdyby pliki były udostępniane tylko określonym osobom.

Tworzę drugiego klienta GAPI w ramce iframe, która pozostaje nieuwierzytelniona (nie jest nawet skonfigurowana z identyfikatorem klienta OAuth ani zakresem). Główny klient GAPI jest używany tak jak poprzednio do logowania użytkownika (z zakresem 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
    });
}

Jeśli uwierzytelniony klient nie odczyta pliku, kod wróci do nieuwierzytelnionego klienta, który może odczytać plik anonimowo.

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;
    }
}

Niestety wygląda na to, że metadane pliku na Dysku w odpowiedzi na anonimowe żądanie nie będą zawierać żadnych danych appProperties dla aplikacji, mimo że API_KEY aplikacji jest obecny w żądaniu. Metadane mogą jednak zawierać properties.

0
Robert Rendell 15 kwiecień 2020, 00:55

Jak widać w oficjalnej dokumentacji, drive.files pozwala Ci:

Wyświetlanie plików i folderów na Dysku Google, które Ty otworzyłeś lub utworzyłeś w tej aplikacji, oraz zarządzaj nimi

Ponieważ pliki nie zostały utworzone w imieniu User B, aplikacja nie ma uprawnień dostępu do nich.

Obawiam się, że nie ma nieograniczonego zakresu, który udzieli tego uprawnienia, dlatego należy użyć drive.readonly lub całkowicie przeprojektować proces udostępniania.

Odniesienie:

0
Iamblichus 16 marzec 2020, 13:27