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": "ihfphotograb@myproj.iam.gserviceaccount.com",
"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.
Supported Authorization protocols
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>