user311302
user311302

Reputation: 97

How can I, in Javascript, get my a JSON list of my Google Photos album?

I am trying to build an Express.js based website which, when I navigate to a certain page, grabs a list of albums, links, ids, etc. I have a service account with all permissions. My Javascript is:

const oauth2Client = new google.auth.OAuth2(
  config.serviceAccount.client_id,
  config.oAuthclientSecret,
  config.oAuthCallbackUrl
);

google.options({auth: oauth2Client});

function getAlbumList(){
    var xhr = new XMLHttpRequest();
    var url = "https://photoslibrary.googleapis.com/v1/albums"
    xhr.open("GET",url,true);

    xhr.onreadystatechange = function () {
        console.log("making xhr")
        if (xhr.readyState == 4 && xhr.status == 200) {
            console.log(xhr);
        }
    }
    xhr.addEventListener('error',function(){
        console.log(xhr.statusMessage)
        console.log("xhr.status is ", xhr.status )
        console.log("ERROR");
    })
    xhr.addEventListener('timeout',function(){
        console.log("SERVER TIMEOUT")
    })
    // Sending our request 
    xhr.send();

}

However, I never even get a response back. I do have a service account, and my credential is:

{  "type": "service_account",
  "project_id": "myProj",
  "private_key_id": "8dxxxxxx46",
  "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQExxxxxxxxxQ==\n-----END PRIVATE KEY-----\n",
  "client_email": "[email protected]",
  "client_id": "10xxxxxxxxx6451",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/proj%40myproj.iam.gserviceaccount.com"}

I've looked at this but they don't tell you how to get a token and when I looked at the network tab to see the request and params going thru. I do have const {google} = require('googleapis') but I don't know how to get that token.

I want the service app to do all the authentication and so my website's visitors can see the photos without authenticating.

Upvotes: 1

Views: 909

Answers (2)

Linda Lawton - DaImTo
Linda Lawton - DaImTo

Reputation: 116868

Supported Authorization protocols

enter image description here

You are trying to use a service account, which is not supported by this api. You are also trying to use a service account with JavaScript which is also not supported. You need to go and create web application credetinals

You will need to switch to using Oauth2.

There is a sample in the documentation which shows how to do this. (As of November 2022 this sample has not been updated to take into account for the new identity model. Check below for a working example.)

<!DOCTYPE html>
<html>
<head>
    <title>Photos API Quickstart</title>
    <meta charset="utf-8" />
</head>
<body>
<p>Photos API Quickstart</p>

<!--Add buttons to initiate auth sequence and sign out-->
<button id="authorize_button" onclick="handleAuthClick()">Authorize</button>
<button id="signout_button" onclick="handleSignoutClick()">Sign Out</button>

<pre id="content" style="white-space: pre-wrap;"></pre>

<script type="text/javascript">
    /* exported gapiLoaded */
    /* exported gisLoaded */
    /* exported handleAuthClick */
    /* exported handleSignoutClick */

    // TODO(developer): Set to client ID and API key from the Developer Console
    const CLIENT_ID = '<YOUR_CLIENT_ID>';
    const API_KEY = '<YOUR_API_KEY>';

    // Discovery doc URL for APIs used by the quickstart
    const DISCOVERY_DOC = 'https://www.googleapis.com/discovery/v1/apis/photoslibrary/v1/rest';

    // Authorization scopes required by the API; multiple scopes can be
    // included, separated by spaces.
    const SCOPES = 'https://www.googleapis.com/auth/photoslibrary.readonly';

    let tokenClient;
    let gapiInited = false;
    let gisInited = false;

    document.getElementById('authorize_button').style.visibility = 'hidden';
    document.getElementById('signout_button').style.visibility = 'hidden';

    /**
     * Callback after api.js is loaded.
     */
    function gapiLoaded() {
        gapi.load('client', initializeGapiClient);
    }

    /**
     * Callback after the API client is loaded. Loads the
     * discovery doc to initialize the API.
     */
    async function initializeGapiClient() {
        await gapi.client.init({
            apiKey: API_KEY,
            discoveryDocs: [DISCOVERY_DOC],
        });
        gapiInited = true;
        maybeEnableButtons();
    }

    /**
     * Callback after Google Identity Services are loaded.
     */
    function gisLoaded() {
        tokenClient = google.accounts.oauth2.initTokenClient({
            client_id: CLIENT_ID,
            scope: SCOPES,
            callback: '', // defined later
        });
        gisInited = true;
        maybeEnableButtons();
    }

    /**
     * Enables user interaction after all libraries are loaded.
     */
    function maybeEnableButtons() {
        if (gapiInited && gisInited) {
            document.getElementById('authorize_button').style.visibility = 'visible';
        }
    }

    /**
     *  Sign in the user upon button click.
     */
    function handleAuthClick() {
        tokenClient.callback = async (resp) => {
            if (resp.error !== undefined) {
                throw (resp);
            }
            document.getElementById('signout_button').style.visibility = 'visible';
            document.getElementById('authorize_button').innerText = 'Refresh';
            await listAlbums();
        };

        if (gapi.client.getToken() === null) {
            // Prompt the user to select a Google Account and ask for consent to share their data
            // when establishing a new session.
            tokenClient.requestAccessToken({prompt: 'consent'});
        } else {
            // Skip display of account chooser and consent dialog for an existing session.
            tokenClient.requestAccessToken({prompt: ''});
        }
    }

    /**
     *  Sign out the user upon button click.
     */
    function handleSignoutClick() {
        const token = gapi.client.getToken();
        if (token !== null) {
            google.accounts.oauth2.revoke(token.access_token);
            gapi.client.setToken('');
            document.getElementById('content').innerText = '';
            document.getElementById('authorize_button').innerText = 'Authorize';
            document.getElementById('signout_button').style.visibility = 'hidden';
        }
    }

    /**
     * Print metadata for first 10 Albums.
     */
    async function listAlbums() {
        let response;
        try {
            response = await gapi.client.photoslibrary.albums.list({
                'pageSize': 10,
                'fields': 'albums(id,title)',
            });
        } catch (err) {
            document.getElementById('content').innerText = err.message;
            return;
        }
        const albums = response.result.albums;
        if (!albums || albums.length == 0) {
            document.getElementById('content').innerText = 'No albums found.';
            return;
        }
        // Flatten to string to display
        const output = albums.reduce(
            (str, album) => `${str}${album.title} (${album.id}\n`,
            'albums:\n');
        document.getElementById('content').innerText = output;
    }
</script>
<script async defer src="https://apis.google.com/js/api.js" onload="gapiLoaded()"></script>
<script async defer src="https://accounts.google.com/gsi/client" onload="gisLoaded()"></script>
</body>
</html>

Upvotes: 2

dreambold
dreambold

Reputation: 3050

You should add the token to your API request, otherwise, you'll get 401 unauthorized error.

Add the following code at the bottom of your index.js file, use your client Id and Secret instead of placeholders:

/*  Google AUTH  */
 
const GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
const GOOGLE_CLIENT_ID = 'our-google-client-id';
const GOOGLE_CLIENT_SECRET = 'our-google-client-secret';
passport.use(new GoogleStrategy({
    clientID: GOOGLE_CLIENT_ID,
    clientSecret: GOOGLE_CLIENT_SECRET,
    callbackURL: "http://localhost:3000/auth/google/callback"
  },
  function(accessToken, refreshToken, profile, done) {
      userProfile=profile;
      return done(null, userProfile);
  }
));
 
app.get('/auth/google', 
  passport.authenticate('google', { scope : ['profile', 'email'] }));
 
app.get('/auth/google/callback', 
  passport.authenticate('google', { failureRedirect: '/error' }),
  function(req, res) {
    // Successful authentication, redirect success.
    res.redirect('/success');
  });

You can have a look more detailed sample code here.

Upvotes: 1

Related Questions