Skip to content

Website: use shared Google API auth client in android proxy endpoints.#47810

Draft
eashaw wants to merge 5 commits into
mainfrom
website-update-android-proxy-authentication
Draft

Website: use shared Google API auth client in android proxy endpoints.#47810
eashaw wants to merge 5 commits into
mainfrom
website-update-android-proxy-authentication

Conversation

@eashaw

@eashaw eashaw commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Closes: #46496

Changes:

  • Updated the website's custom hook to create a Google API auth client and make it available at sails.googleAuthClient
  • Updated Android proxy endpoints to use the shared Google API auth client.

Summary by CodeRabbit

  • Refactor
    • Optimized Google API authentication handling for Android management features to improve system performance and reliability.

Copilot AI review requested due to automatic review settings June 17, 2026 23:03

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

fleet-release
fleet-release previously approved these changes Jun 17, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses repeated Google API auth client instantiation across the website’s Android Management API proxy endpoints by creating a single shared auth client at Sails lift time and reusing it for all AMAPI/PubSub requests.

Changes:

  • Initialize and cache a shared Google API auth client on sails.googleAuthClient during server startup.
  • Update Android proxy controllers/helpers to pass auth: sails.googleAuthClient into google.androidmanagement() / google.pubsub() instead of recreating GoogleAuth per request.
  • Remove per-request google.options({ auth }) usage, avoiding global mutable state per request.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
website/api/hooks/custom/index.js Creates the shared sails.googleAuthClient once at lift time using service account credentials and scopes.
website/api/helpers/android-proxy/get-is-enterprise-managed-by-fleet.js Uses sails.googleAuthClient when constructing the Android Management API client.
website/api/controllers/android-proxy/create-android-enrollment-token.js Reuses the shared auth client for enrollment token creation.
website/api/controllers/android-proxy/create-android-enterprise.js Reuses the shared auth client for AMAPI + Pub/Sub enterprise provisioning calls.
website/api/controllers/android-proxy/create-android-signup-url.js Reuses the shared auth client for signup URL creation.
website/api/controllers/android-proxy/create-enterprise-webapp.js Reuses the shared auth client for web app creation.
website/api/controllers/android-proxy/delete-android-device.js Reuses the shared auth client for device deletion.
website/api/controllers/android-proxy/delete-one-android-enterprise.js Reuses the shared auth client for enterprise deletion and Pub/Sub cleanup.
website/api/controllers/android-proxy/get-android-device.js Reuses the shared auth client for device retrieval.
website/api/controllers/android-proxy/get-android-devices.js Reuses the shared auth client for device listing.
website/api/controllers/android-proxy/get-android-enterprises.js Reuses the shared auth client for enterprise listing.
website/api/controllers/android-proxy/get-enterprise-applications.js Reuses the shared auth client for enterprise application retrieval.
website/api/controllers/android-proxy/issue-command-on-android-device.js Reuses the shared auth client for issuing device commands.
website/api/controllers/android-proxy/modify-android-device.js Reuses the shared auth client for device patch operations.
website/api/controllers/android-proxy/modify-android-policies.js Reuses the shared auth client for policy patch operations.
website/api/controllers/android-proxy/modify-enterprise-app-policy.js Reuses the shared auth client for policy application modifications.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 36 to 40
// Log into google.
// Reuse the shared Google API auth client created at server startup (see api/hooks/custom/).
let { google } = require('googleapis');
let androidmanagement = google.androidmanagement('v1');
let googleAuth = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/androidmanagement'],
credentials: {
client_email: sails.config.custom.androidEnterpriseServiceAccountEmailAddress,// eslint-disable-line camelcase
private_key: sails.config.custom.androidEnterpriseServiceAccountPrivateKey,// eslint-disable-line camelcase
},
});
let authClient = await googleAuth.getClient();
google.options({auth: authClient});
let androidmanagement = google.androidmanagement({version: 'v1', auth: sails.googleAuthClient});

Comment on lines 60 to +62
let { google } = require('googleapis');
let androidmanagement = google.androidmanagement('v1');
let googleAuth = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/androidmanagement'],
credentials: {
client_email: sails.config.custom.androidEnterpriseServiceAccountEmailAddress,// eslint-disable-line camelcase
private_key: sails.config.custom.androidEnterpriseServiceAccountPrivateKey,// eslint-disable-line camelcase
},
});
// Acquire the google auth client, and bind it to all future calls
let authClient = await googleAuth.getClient();
google.options({auth: authClient});
// Reuse the shared Google API auth client created at server startup (see api/hooks/custom/).
let androidmanagement = google.androidmanagement({version: 'v1', auth: sails.googleAuthClient});
Comment on lines 56 to +58
let { google } = require('googleapis');
let androidmanagement = google.androidmanagement('v1');
let googleAuth = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/androidmanagement'],
credentials: {
client_email: sails.config.custom.androidEnterpriseServiceAccountEmailAddress,// eslint-disable-line camelcase
private_key: sails.config.custom.androidEnterpriseServiceAccountPrivateKey,// eslint-disable-line camelcase
},
});
// Acquire the google auth client, and bind it to all future calls
let authClient = await googleAuth.getClient();
google.options({auth: authClient});
// Reuse the shared Google API auth client created at server startup (see api/hooks/custom/).
let androidmanagement = google.androidmanagement({version: 'v1', auth: sails.googleAuthClient});
Comment on lines 78 to +80
let { google } = require('googleapis');
let androidmanagement = google.androidmanagement('v1');
let googleAuth = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/androidmanagement'],
credentials: {
client_email: sails.config.custom.androidEnterpriseServiceAccountEmailAddress,// eslint-disable-line camelcase
private_key: sails.config.custom.androidEnterpriseServiceAccountPrivateKey,// eslint-disable-line camelcase
},
});
// Acquire the google auth client, and bind it to all future calls
let authClient = await googleAuth.getClient();
google.options({ auth: authClient });
// Reuse the shared Google API auth client created at server startup (see api/hooks/custom/).
let androidmanagement = google.androidmanagement({version: 'v1', auth: sails.googleAuthClient});
Comment on lines 67 to +69
let { google } = require('googleapis');
let androidmanagement = google.androidmanagement('v1');
let googleAuth = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/androidmanagement'],
credentials: {
client_email: sails.config.custom.androidEnterpriseServiceAccountEmailAddress,// eslint-disable-line camelcase
private_key: sails.config.custom.androidEnterpriseServiceAccountPrivateKey,// eslint-disable-line camelcase
},
});
// Acquire the google auth client, and bind it to all future calls
let authClient = await googleAuth.getClient();
google.options({ auth: authClient });
// Reuse the shared Google API auth client created at server startup (see api/hooks/custom/).
let androidmanagement = google.androidmanagement({version: 'v1', auth: sails.googleAuthClient});
Comment on lines 67 to 70
let { google } = require('googleapis');
let androidmanagement = google.androidmanagement('v1');
let googleAuth = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/androidmanagement'],
credentials: {
client_email: sails.config.custom.androidEnterpriseServiceAccountEmailAddress,// eslint-disable-line camelcase
private_key: sails.config.custom.androidEnterpriseServiceAccountPrivateKey,// eslint-disable-line camelcase
},
});
// Acquire the google auth client, and bind it to all future calls
let authClient = await googleAuth.getClient();
google.options({ auth: authClient });
// Reuse the shared Google API auth client created at server startup (see api/hooks/custom/).
let androidmanagement = google.androidmanagement({version: 'v1', auth: sails.googleAuthClient});
// [?]: https://googleapis.dev/nodejs/googleapis/latest/androidmanagement/classes/Resource$Enterprises$Applications.html#get
Comment on lines 164 to 167
let { google } = require('googleapis');
let androidmanagement = google.androidmanagement('v1');
let googleAuth = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/androidmanagement'],
credentials: {
client_email: sails.config.custom.androidEnterpriseServiceAccountEmailAddress,// eslint-disable-line camelcase
private_key: sails.config.custom.androidEnterpriseServiceAccountPrivateKey,// eslint-disable-line camelcase
},
});
// Acquire the google auth client, and bind it to all future calls
let authClient = await googleAuth.getClient();
google.options({ auth: authClient });
// Reuse the shared Google API auth client created at server startup (see api/hooks/custom/).
let androidmanagement = google.androidmanagement({version: 'v1', auth: sails.googleAuthClient});
// [?]: https://googleapis.dev/nodejs/googleapis/latest/androidmanagement/classes/Resource$Enterprises$Devices.html#issueCommand
Comment on lines 68 to 71
let { google } = require('googleapis');
let androidmanagement = google.androidmanagement('v1');
let googleAuth = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/androidmanagement'],
credentials: {
client_email: sails.config.custom.androidEnterpriseServiceAccountEmailAddress,// eslint-disable-line camelcase
private_key: sails.config.custom.androidEnterpriseServiceAccountPrivateKey,// eslint-disable-line camelcase
},
});
// Acquire the google auth client, and bind it to all future calls
let authClient = await googleAuth.getClient();
google.options({ auth: authClient });
// Reuse the shared Google API auth client created at server startup (see api/hooks/custom/).
let androidmanagement = google.androidmanagement({version: 'v1', auth: sails.googleAuthClient});
// [?]: https://googleapis.dev/nodejs/googleapis/latest/androidmanagement/classes/Resource$Enterprises$Devices.html#patch
Comment on lines 68 to 71
let { google } = require('googleapis');
let androidmanagement = google.androidmanagement('v1');
let googleAuth = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/androidmanagement'],
credentials: {
client_email: sails.config.custom.androidEnterpriseServiceAccountEmailAddress,// eslint-disable-line camelcase
private_key: sails.config.custom.androidEnterpriseServiceAccountPrivateKey,// eslint-disable-line camelcase
},
});
// Acquire the google auth client, and bind it to all future calls
let authClient = await googleAuth.getClient();
google.options({ auth: authClient });
// Reuse the shared Google API auth client created at server startup (see api/hooks/custom/).
let androidmanagement = google.androidmanagement({version: 'v1', auth: sails.googleAuthClient});
// [?]: https://googleapis.dev/nodejs/googleapis/latest/androidmanagement/classes/Resource$Enterprises$Policies.html#patch
Comment on lines 79 to 82
let { google } = require('googleapis');
let androidmanagement = google.androidmanagement('v1');
let googleAuth = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/androidmanagement'],
credentials: {
client_email: sails.config.custom.androidEnterpriseServiceAccountEmailAddress,// eslint-disable-line camelcase
private_key: sails.config.custom.androidEnterpriseServiceAccountPrivateKey,// eslint-disable-line camelcase
},
});
// Acquire the google auth client, and bind it to all future calls
let authClient = await googleAuth.getClient();
google.options({ auth: authClient });
// Reuse the shared Google API auth client created at server startup (see api/hooks/custom/).
let androidmanagement = google.androidmanagement({version: 'v1', auth: sails.googleAuthClient});

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

All 13 Android proxy controllers and one helper previously created a new google.auth.GoogleAuth instance on every request, called getClient(), and bound the result via google.options({ auth }). This PR moves that initialization into the Sails custom hook's initialize lifecycle: when the Android service-account credentials are present, a single GoogleAuth client is created, stored as sails.googleAuthClient, and an access token is pre-fetched. Each controller then constructs its google.androidmanagement (and google.pubsub where applicable) client by passing auth: sails.googleAuthClient directly, removing all per-request auth setup code.

Possibly related PRs

  • fleetdm/fleet#46107: Modifies issue-command-on-android-device.js with changes to the Google Android Management issueCommand authentication setup, the same file updated in this PR.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The description lacks required template sections including related issue link, checklist items, testing details, and database migration checks. Add missing PR description template sections, particularly the linked issue reference, relevant checklist items, and testing confirmation.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: consolidating Google API auth client usage across Android proxy endpoints.
Linked Issues check ✅ Passed All code changes directly address the two core problems identified in #46496: eliminating per-request auth client re-instantiation and removing global state mutation via google.options().
Out of Scope Changes check ✅ Passed All changes are scoped to implementing the shared Google auth client pattern; no unrelated modifications to other systems or functionality are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch website-update-android-proxy-authentication

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@website/api/controllers/android-proxy/get-enterprise-applications.js`:
- Around line 68-69: The code at the androidmanagement client instantiation uses
sails.googleAuthClient without verifying it has been properly initialized. Add a
guard check before creating the androidmanagement client to ensure
sails.googleAuthClient is defined and not null. If the shared Google auth client
is missing, throw or return an appropriate error response with a clear message
indicating that Android credentials were not properly configured during startup,
rather than letting the undefined client propagate into the Google API call
where it will produce opaque errors.

In `@website/api/hooks/custom/index.js`:
- Around line 162-164: The catch block for the shared Google API auth client
initialization in the Android Management API setup is only logging a warning
instead of failing fast. Replace the sails.log.warn call with code that throws
an error or exits the process to ensure startup fails deterministically when
this critical client cannot be initialized, rather than allowing the application
to continue and fail later when downstream Android proxy handlers attempt to use
the uninitialized shared client.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fd21caff-c26a-4874-a3ce-010754689032

📥 Commits

Reviewing files that changed from the base of the PR and between 435c6e1 and d10275f.

📒 Files selected for processing (16)
  • website/api/controllers/android-proxy/create-android-enrollment-token.js
  • website/api/controllers/android-proxy/create-android-enterprise.js
  • website/api/controllers/android-proxy/create-android-signup-url.js
  • website/api/controllers/android-proxy/create-enterprise-webapp.js
  • website/api/controllers/android-proxy/delete-android-device.js
  • website/api/controllers/android-proxy/delete-one-android-enterprise.js
  • website/api/controllers/android-proxy/get-android-device.js
  • website/api/controllers/android-proxy/get-android-devices.js
  • website/api/controllers/android-proxy/get-android-enterprises.js
  • website/api/controllers/android-proxy/get-enterprise-applications.js
  • website/api/controllers/android-proxy/issue-command-on-android-device.js
  • website/api/controllers/android-proxy/modify-android-device.js
  • website/api/controllers/android-proxy/modify-android-policies.js
  • website/api/controllers/android-proxy/modify-enterprise-app-policy.js
  • website/api/helpers/android-proxy/get-is-enterprise-managed-by-fleet.js
  • website/api/hooks/custom/index.js

Comment on lines +68 to +69
// Reuse the shared Google API auth client created at server startup (see api/hooks/custom/).
let androidmanagement = google.androidmanagement({version: 'v1', auth: sails.googleAuthClient});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect shared Google auth initialization and direct consumers.
rg -n -C3 'sails\.googleAuthClient|androidEnterpriseServiceAccountEmailAddress|androidEnterpriseServiceAccountPrivateKey|getAccessToken\(|catch\s*\(' \
  website/api/hooks/custom/index.js \
  website/api/controllers/android-proxy \
  website/api/helpers/android-proxy

Repository: fleetdm/fleet

Length of output: 22147


Guard the shared Google auth client before using it.

The startup hook can leave sails.googleAuthClient unset when Android credentials are not configured or when initialization fails (caught and logged as a warning at line 163 in website/api/hooks/custom/index.js). This endpoint and at least 12 other Android proxy call sites pass it directly into the Google API client without checking, causing runtime failures with opaque error messages. Add a shared fail-fast guard, or fail startup when Android proxy credentials are configured but the shared client cannot be created, so requests fail clearly rather than devolving into ambiguous Google API errors.

🛡️ Example localized guard
       let { google } = require('googleapis');
       // Reuse the shared Google API auth client created at server startup (see api/hooks/custom/).
+      if (!sails.googleAuthClient) {
+        throw new Error('Google API auth client was not initialized for Android proxy requests.');
+      }
       let androidmanagement = google.androidmanagement({version: 'v1', auth: sails.googleAuthClient});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@website/api/controllers/android-proxy/get-enterprise-applications.js` around
lines 68 - 69, The code at the androidmanagement client instantiation uses
sails.googleAuthClient without verifying it has been properly initialized. Add a
guard check before creating the androidmanagement client to ensure
sails.googleAuthClient is defined and not null. If the shared Google auth client
is missing, throw or return an appropriate error response with a clear message
indicating that Android credentials were not properly configured during startup,
rather than letting the undefined client propagate into the Google API call
where it will produce opaque errors.

Comment on lines +162 to +164
} catch (err) {
sails.log.warn('p1: Failed to initialize the shared Google API auth client for the Android Management API. Android proxy endpoints will not function until this is resolved. Error: '+err);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail fast when shared Android proxy auth client initialization fails.

At Line 162, initialization errors are only logged and startup continues, but downstream Android proxy handlers rely on this shared client. This creates partial startup and request-time failures instead of a deterministic lift-time failure.

Proposed fix
-        } catch (err) {
-          sails.log.warn('p1: Failed to initialize the shared Google API auth client for the Android Management API. Android proxy endpoints will not function until this is resolved. Error: '+err);
-        }
+        } catch (err) {
+          sails.log.error('Failed to initialize shared Google API auth client for Android proxy.', err);
+          throw new Error('Android proxy auth initialization failed with configured credentials.');
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (err) {
sails.log.warn('p1: Failed to initialize the shared Google API auth client for the Android Management API. Android proxy endpoints will not function until this is resolved. Error: '+err);
}
} catch (err) {
sails.log.error('Failed to initialize shared Google API auth client for Android proxy.', err);
throw new Error('Android proxy auth initialization failed with configured credentials.');
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@website/api/hooks/custom/index.js` around lines 162 - 164, The catch block
for the shared Google API auth client initialization in the Android Management
API setup is only logging a warning instead of failing fast. Replace the
sails.log.warn call with code that throws an error or exits the process to
ensure startup fails deterministically when this critical client cannot be
initialized, rather than allowing the application to continue and fail later
when downstream Android proxy handlers attempt to use the uninitialized shared
client.

@eashaw eashaw marked this pull request as draft June 18, 2026 15:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Google API auth client re-instantiated on every android-proxy request

3 participants