This guide compiles real-world troubleshooting playbooks, root-cause explanations, and verification commands for the Device Trust Gateway & Approval Portal. It also includes ready-to-use workflows for troubleshooting with Gemini using server telemetry, Chrome policy exports, and Google Workspace audit logs.
- 🤖 Troubleshooting with Gemini (AI-Assisted Diagnostics)
- 🔍 End-to-End Client & Server Log Telemetry
- 💻 Personal Windows & macOS BYOD Devices Not Appearing or Stuck
⚠️ Understandingchrome://policyWarnings & Missing Policies- 🪟 Windows Git Bash (
MINGW64) Path Conversion &gcloud.pyErrors - 🔐 Domain-Wide Delegation (DWD) & Cloud Identity API Errors
- 🚫 Google Sign-In OAuth Errors (
redirect_uri_mismatch/App is blocked) - 🏢 Context-Aware Access (CAA) Lockouts & Policy Assignment Pitfalls
- 📱 Unexpected Devices or Auto-Approved Desktop BYOD Devices
- 🔄 Updating an Existing Deployment to the Latest Release
Because the Device Trust Gateway spans Google Cloud Run, Cloud Identity Devices API, Google Workspace Admin Console policies, and Chrome Browser Profile Reporting, the fastest way to isolate any issue is to collect your diagnostic bundle and ask Gemini (in Google Cloud Console, Gemini CLI / Code Assist, or Google Workspace) to analyze the logs alongside this repository.
The portal automatically forwards browser-side events ([CLIENT_LOG]) to the Cloud Run server logs alongside backend API traces ([devices.py], [cloud_identity.py], [config_service.py]). Run this command in your terminal to export the last 150 log entries:
gcloud logging read \
'resource.type="cloud_run_revision" AND resource.labels.service_name="device-trust-gateway"' \
--project=YOUR_GCP_PROJECT_ID \
--limit=150 \
--format=json > cloud_run_logs.jsonOn the personal Windows, macOS, or ChromeOS device you are testing:
- Open Chrome and sign into the Chrome profile with your managed Google Workspace account (
user@yourdomain.com). - Navigate to
chrome://policy. - Click Reload policies in the top-left corner.
- Click Export to JSON (or Save as JSON) and save the file as
policies.json.
- From Admin Console: Go to Reporting > Audit and investigation > Device log events, filter by your test user, and click Export (CSV).
- From Terminal: Run the included domain diagnostic script to inspect raw Cloud Identity and Directory API records:
WORKSPACE_ADMIN_EMAIL=admin@yourdomain.com \ GOOGLE_APPLICATION_CREDENTIALS=dwd_key.json \ ./backend/venv/bin/python backend/scripts/pull_domain_device_logs.py
Attach cloud_run_logs.json and policies.json (plus any Admin Console screenshots or CSV exports) to your Gemini chat and paste the following prompt:
I am troubleshooting our deployment of the Google Workspace Device Trust Gateway (`devicetrustportal`) on Cloud Run.
Here is my environment context:
- Google Workspace Domain: <YOUR_DOMAIN>
- GCP Project ID: <YOUR_GCP_PROJECT_ID>
- Test User Email: <TEST_USER_EMAIL>
- Test Device OS & Browser: <e.g., Windows 11 / macOS Sequoia, Chrome signed into managed profile>
- Symptom we are seeing: <Describe what happens in the portal or when opening Gmail/Drive>
I have attached:
1. `cloud_run_logs.json` (Cloud Run stdout/stderr including `[CLIENT_LOG]` frontend telemetry and `[devices.py]` Cloud Identity lookups)
2. `policies.json` (exported from `chrome://policy` on the test device)
Please check:
1. Are the Domain-Wide Delegation credentials (`/secrets/dwd_key.json` and `WORKSPACE_ADMIN_EMAIL`) initializing cleanly in `cloud_run_logs.json`, or are there `403 PERMISSION_DENIED` / `DefaultCredentialsError` failures?
2. What does `[CLIENT_LOG]` show for `OAUTH_SIGNIN_SUCCESS` and `LOAD_DEVICES_*`, and how many devices did `crawl_devices_for_user` scan in Cloud Identity?
3. In `policies.json`, are `CloudProfileReportingEnabled`, `UserSecuritySignalsReporting`, `UserSecurityAuthenticatedReporting`, and `EnterpriseHardwarePlatformAPIEnabled` active under the signed-in Chrome user profile, and is the Endpoint Verification extension (`callobklhcbilhphinckomhgkigmfocg`) force-installed?
4. What exact Google Admin Console setting or `gcloud` command should I run to fix this?To make debugging effortless without needing browser DevTools on student or employee machines, the frontend automatically sends structured telemetry to POST /api/client-logs, which prints to Cloud Run stdout under the [CLIENT_LOG] prefix.
| Log Event Tag | Meaning & What to Check |
|---|---|
OAUTH_CLIENT_ID_MISSING |
The frontend did not receive a google_client_id from /api/config/public or REACT_APP_GOOGLE_CLIENT_ID. Complete Phase 2/3 of ./deploy.sh or set the Client ID in #/admin. |
OAUTH_SIGNIN_SUCCESS |
Confirms the user authenticated via Google Sign-In and logs their email and hosted domain (hd). |
LOAD_DEVICES_START |
The browser requested GET /api/devices/my-devices. Look immediately below this line for INFO [devices.py]: Executing Cloud Identity lookup.... |
LOAD_DEVICES_EMPTY |
The backend returned 0 devices for the signed-in user. Check whether the user's device has registered in Cloud Identity (Admin Console > Devices > Mobile & endpoints > Devices). |
LOAD_DEVICES_SUCCESS |
Logs the exact count, device_user_name, model, approval_state, and owner_type rendered in the UI. |
LOAD_DEVICES_ERROR |
The backend returned an HTTP error (such as 500 due to missing DWD scopes or invalid WORKSPACE_ADMIN_EMAIL). |
APPROVE_DEVICE_SUCCESS / ERROR |
Logs the outcome of clicking ✓ Approve on a personal BYOD device (devices.deviceUsers.approve). |
REVOKE_DEVICE_SUCCESS / ERROR |
Logs the outcome of revoking device access (devices.deviceUsers.block). |
- A user signs into Chrome on a personal Windows or macOS laptop, installs the Endpoint Verification extension, and logs into the Device Trust Portal, but the portal says "No Registered Hardware Assets Discovered"—or
chrome://policyshows:CloudReportingEnabled: Ignored because machine is not enrolled.
Personal BYOD computers are not enrolled in machine-level Chrome Browser Cloud Management (CBCM). CloudReportingEnabled is a machine-level CBCM policy and is always ignored on unenrolled BYOD laptops.
Instead, personal Windows and macOS laptops register in Cloud Identity as PENDING_APPROVAL (ClientType.EVERY_DEVICE_SECURE) when Managed Chrome Profile Reporting and Chrome Signals Sharing are enabled for the user's Organizational Unit!
- Do NOT enroll personal BYOD machines in CBCM.
- Open Google Admin Console > Devices > Chrome > Settings > Users & browsers, select your target OU, and configure these 5 user/browser settings:
- Profile reporting (
CloudProfileReportingEnabled) → Enable profile reporting - Chrome signals sharing (
UserSecuritySignalsReporting&UserSecurityAuthenticatedReporting) → Enable signals sharing - Enterprise Hardware Platform API (
EnterpriseHardwarePlatformAPIEnabled) → Allow extensions to see hardware platform information - Browser sign-in → Force users to sign in to use the browser
- Managed accounts sign-in restriction (
ManagedAccountsSigninRestriction) → Block users from signing into secondary accounts (primary_account_strict)
- Profile reporting (
- Open Devices > Mobile & endpoints > Settings > Universal > Data access → Device signals and enable:
- Collect device signals from Chrome browser
- Collect device signals using endpoint verification
- Open Devices > Mobile & endpoints > Settings > Universal > Security → Device approvals and enable:
- Require admin approval
- On the personal laptop, open
chrome://policy, click Reload policies, click the Endpoint Verification extension icon (callobklhcbilhphinckomhgkigmfocg) → Sync now, and click Refresh Devices in the portal.
When inspecting chrome://policy on a personal Windows or macOS laptop, two common observations often look like errors but are actually expected Chromium behavior:
- Policies affected:
CloudPolicyOverridesPlatformPolicy,CloudUserPolicyOverridesCloudMachinePolicy. - Why it happens: In Chromium's policy engine, Policy Precedence metapolicies are machine-scoped (
per_profile: false). For security reasons, a signed-in user profile is not allowed to alter machine-wide policy precedence on an unenrolled computer, so Chrome displays "Ignored because the policy is not set at the machine scope". - What to do: This notice is completely benign and does not block device registration. You can return Policy precedence in the Admin Console back to its default setting; on unenrolled BYOD laptops with no local Windows Group Policy (GPO) or MDM profile,
Cloud userpolicies already take effect automatically.
Observation B: "Allow access to keys" (KeyPermissions) and "Allow enterprise challenge" (AttestationExtensionAllowlist) Do Not Appear in chrome://policy on Windows/Mac
- Why it happens: Even when you turn ON Allow access to keys and Allow enterprise challenge under
Devices > Chrome > Apps & extensions > Users & browsers > Endpoint Verification, Chromium's policy engine defines both policies as ChromeOS-only (supported_on: ["chrome_os"]) because they control ChromeOS Verified Access hardware TPM attestation. - What to do: Nothing—this is expected. On Windows and macOS, check
chrome://policyfor these policies instead:CloudProfileReportingEnabled:trueUserSecuritySignalsReporting:trueUserSecurityAuthenticatedReporting:trueEnterpriseHardwarePlatformAPIEnabled:trueExtensionInstallForcelist: containscallobklhcbilhphinckomhgkigmfocg
When deploying from a Windows machine using Git Bash (MINGW64), MSYS2 automatically translates POSIX-style arguments (starting with /) into Windows filesystem paths (C:/Program Files/Git/...). This can cause two distinct issues if not handled properly:
- Cause: Git Bash translated
--set-env-vars="GOOGLE_APPLICATION_CREDENTIALS=/secrets/dwd_key.json"intoC:/Program Files/Git/secrets/dwd_key.jsonbefore passing it to Cloud Run. - How it is prevented automatically:
./deploy.shsetsMSYS2_ARG_CONV_EXCL="--set-secrets;--set-env-vars;--update-env-vars;GOOGLE_APPLICATION_CREDENTIALS"so container paths are never converted.- The backend's
resolve_dwd_key_path()helper automatically detects mangled Windows paths inside the Linux container and repairsGOOGLE_APPLICATION_CREDENTIALSto/secrets/dwd_key.jsonat startup.
Issue B: python.exe: can't open file 'C:\c\Program Files (x86)\Google\Cloud SDK\...\gcloud.py': [Errno 2] No such file or directory
- Cause: Running
MSYS_NO_PATHCONV=1orMSYS2_ARG_CONV_EXCL="*"globally in Git Bash disables path translation for every argument—includinggcloud's own bash wrapper script when it passes/c/Program Files (x86)/Google/Cloud SDK/.../gcloud.pyto native Windowspython.exe. - Fix: Never set
MSYS_NO_PATHCONV=1orMSYS2_ARG_CONV_EXCL="*"globally when runninggcloudon Windows Git Bash. Instead, run:unset MSYS_NO_PATHCONV MSYS2_ARG_CONV_EXCL="--set-secrets;--set-env-vars;--update-env-vars;GOOGLE_APPLICATION_CREDENTIALS" \ gcloud run services update device-trust-gateway \ --region=us-central1 \ --project=YOUR_PROJECT_ID \ --update-env-vars="GOOGLE_APPLICATION_CREDENTIALS=/secrets/dwd_key.json"
The portal displays a red alert banner:
Failed to load approved devices: {"detail":"Cloud Identity API service is not initialized..."}Failed to load approved devices: {"detail":"Cloud Identity API device lookup failed (HttpError 403 ... Request had insufficient authentication scopes / unauthorized_client)..."}
- Verify the 6 Exact OAuth Scopes in Google Workspace Admin Console:
Open Security > Access and data control > API controls > Domain-wide Delegation and confirm your Service Account's Numeric Client ID has all six comma-separated scopes:
https://www.googleapis.com/auth/cloud-identity.devices,https://www.googleapis.com/auth/admin.directory.user.readonly,https://www.googleapis.com/auth/admin.directory.group.member.readonly,https://www.googleapis.com/auth/admin.directory.device.chromeos.readonly,https://www.googleapis.com/auth/admin.reports.audit.readonly,https://www.googleapis.com/auth/admin.directory.user.security - Verify
WORKSPACE_ADMIN_EMAILIs an Active Super Administrator: Domain-Wide Delegation requires impersonating a Google Workspace user who holds privileges to query Cloud Identity Devices and Directory APIs. Check the configured email on Cloud Run:gcloud run services describe device-trust-gateway \ --region=us-central1 \ --project=YOUR_PROJECT_ID \ --format="yaml(spec.template.spec.containers[0].env)" - Verify Required Google Cloud APIs Are Enabled:
gcloud services enable \ cloudidentity.googleapis.com \ admin.googleapis.com \ secretmanager.googleapis.com \ run.googleapis.com \ --project=YOUR_PROJECT_ID
- Cause: Your live Cloud Run HTTPS URL (
https://device-trust-gateway-xyz-uc.a.run.app) is missing from your OAuth 2.0 Web Client ID's Authorized JavaScript origins. - Fix:
- Open Google Cloud Console > APIs & Services > Credentials.
- Click your Web Application OAuth 2.0 Client ID.
- Add your exact Cloud Run URL (with
https://and no trailing slash) under Authorized JavaScript origins and Authorized redirect URIs, then click Save.
- Cause: Student or restricted Organizational Units often block unreviewed OAuth Client IDs by default.
- Fix:
- Open Google Admin Console > Security > Access and data control > API controls > App access control.
- Click Add app > OAuth app name or Client ID, paste your OAuth 2.0 Web Client ID, and set access to Trusted for your target OUs.
- After assigning the
Approved Devices OnlyContext-Aware Access level, administrators or staff are immediately blocked from Gmail/Drive, or the Device Trust Portal itself returns403 Forbidden.
- Never Assign CAA Policies to the Root OU (
/) First: InSecurity > Access and data control > Context-Aware Access > Assign to apps, the left-hand tree defaults to the Root OU (/). Always select a dedicated Pilot OU or/StudentsOU so Super Administrators in/Admincannot lock themselves out. - Keep the Device Trust Portal Accessible from Unapproved Home Devices (Standard Mode):
When
./deploy.shasks "Enable IAP Edge Defense & Corporate Network Gating? (y/N)", chooseN(Standard Mode) for K-12, Higher Ed, or hybrid workforces so users on unapproved home laptops can open the portal and approve their device.
- Cause: Deployments running a container built prior to commit
2562135fell back to sample demonstration data when Domain-Wide Delegation credentials failed to load (GOOGLE_APPLICATION_CREDENTIALSpath mangling). - Fix: Update your repository checkout (
git fetch origin && git reset --hard origin/main) and re-run./deploy.sh. All mock and simulation fallbacks have been completely removed from the codebase; the portal exclusively serves live Cloud Identity and Directory API records.
Issue B: Personal Windows/Mac Laptops Automatically Register as APPROVED Instead of PENDING_APPROVAL
- Cause:
- Devices that synced before Require admin approval (
Universal > Security > Device approvals) and Profile reporting (CloudProfileReportingEnabled) were enabled may already holdAPPROVEDstate in Cloud Identity. - Or a child Organizational Unit overrides Device approvals with Do not require admin approval.
- Devices that synced before Require admin approval (
- Fix:
- Verify Require admin approval is inherited across all target OUs.
- Run the one-time baseline mass revocation script to reset existing BYOD bindings to
BLOCKED:WORKSPACE_ADMIN_EMAIL=admin@yourdomain.com \ GOOGLE_APPLICATION_CREDENTIALS=dwd_key.json \ ./backend/venv/bin/python backend/scripts/mass_revoke_byod_approvals.py --force
Issue C: Android / iOS Users Prompted to Install "Google Device Policy" MDM App (or Account Removed on Decline)
-
Cause:
- In Google Admin Console (
Devices > Mobile & endpoints > Settings > Universal settings > General > Mobile management), setting Android or iOS to Advanced triggers Google's EMM workflow (ApprovalRequiredNotificationProcessor). Upon sign-in, Android/iOS forces the user to download the Google Device Policy app / Android Work Profile, and declining the prompt automatically removes the Google Workspace account from the phone. - Conversely, setting Admin Console to Basic alone causes Google's EMM backend (
CreateDeviceProducerModule) to ignore the Require admin approval checkbox and auto-createBasicmobile devices inAPPROVEDstate.
- In Google Admin Console (
-
Fix (Agentless
BasicMobile Auto-Quarantine — Zero MDM App Required):- In Google Admin Console (
Universal settings > General > Mobile management), switch Android and iOS (or Turn on mobile management) to Basic. - In the Device Trust Portal (
#/admin), toggle on 📱 Agentless (Basic) Mobile Management Auto-Quarantine (basic_mobile_auto_quarantine_enabled: true— OFF by default). - Explicitly check
Root Organization (/), specific Sub-OUs, or enterPriority #1 -> #NGoogle Security Groups (all checkboxes start unchecked by default so nothing is ever blocked until an admin chooses a target scope). -
Safe Rollout Guarantee: Pre-existing phones (
createTime > 15 minutesold) are automatically baselined intoportal_approved_devicesand never blocked, while newly added (<= 15m) unverifiedBasicphones are flipped fromAPPROVED$\rightarrow$ BLOCKEDuntil approved in the portal via Campus IP or a 6-Digit Pairing Code (+ Add Personal Device (15m Grace Pass)).
- In Google Admin Console (
- Context-Aware Access (CAA) blocks Gmail and Google Drive (
403 Access Denied), or you are testing on an Education Fundamentals domain without CAA, and a user who logs into an unapproved personal Mac or Windows laptop remains signed intoaccounts.google.comor the browser profile.
- New Installs Start Disabled by Default (
enforcement_mode: "DISABLED"): On a fresh installation (./deploy.sh), both Session Management (session_watch_enabled: false) and CAA Integration (caa_enforcement_enabled: false) start disabled until enabled by an administrator in#/admin. - CAA Blocks Apps at the Edge, Not
accounts.google.com: Even when CAA is active, Google permits initialaccounts.google.comauthentication so Chrome Profile Reporting and Endpoint Verification can register the device in Cloud Identity (PENDING_APPROVAL). - Re-Logins on Previously
BLOCKEDDevices: Once an unapproved Mac is transitioned fromPENDING_APPROVALtoBLOCKED, signing in again updatesDeviceUser.lastSyncTimein ~1.7 seconds, whilemanagementStatestaysBLOCKED(and Admin SDKloginaudit logs can lag 15–60 minutes).
- Open the Admin Portal (
#/admin) and enable Session Management (Education Fundamentals) (either alone or alongside Context-Aware Access Integration asBOTH). - Click 💾 Save Configurations, then click 🔄 Sync Inventory Cache to load your approved ChromeOS and BYOD serials into the SQLite/RAM cache.
- If testing with an administrator account, ensure Exempt Super Admins & Portal Admins (
session_watch_exempt_admins) is unchecked (false). - Once enabled, the 3-Layer Immediate Enforcement Pipeline terminates unapproved device sessions automatically:
- Layer 1 (
< 0.5sInline Portal Check +8sHeartbeat): Opening the portal on an unapproved orBLOCKEDdevice (without an active 15-minute onboarding lease) immediately firesusers.signOut+ OAuth grant revocation and signs the browser out with401 SESSION_REVOKED_UNAPPROVED_DEVICE. - Layer 2 (
~2–10sCloud IdentitylastSyncTimeSub-Polling): The 1-minute Cloud Scheduler job (session-watch-login-sweep) runs 5 sub-polls spaced 10s apart (t=0s, 10s, 20s, 30s, 40s), detecting anyPENDING_APPROVALor re-authenticatedBLOCKEDBYODlastSyncTimenewer than the user's last sign-out and immediately executingusers.signOut+directory.tokens.delete. - Layer 3 (Admin SDK Reports API Sweep): Catches external unmanaged browser logins that do not report via Chrome Profile Reporting.
- Layer 1 (
Whenever new fixes or UI improvements are pushed to the repository, update your local checkout and redeploy with:
cd ~/devicetrustportal
git fetch origin
git reset --hard origin/main
./deploy.sh(Note: ./deploy.sh also checks origin/main automatically at startup and fast-forwards your checkout before building the container, while preserving your existing Secret Manager device_trust_gateway_config settings).