Skip to content

Cloud Anchors: keyless authorization silently fails (mode auto-disabled, no error) despite a fully-configured OAuth client — while token-auth works in the same project #1777

Description

@atdaga

Summary

On Android, ARCore keyless authorization for Cloud Anchors never authorizes. Config.CloudAnchorMode is committed as ENABLED at session creation, but ARCore silently downgrades it to DISABLED within ~1–2 seconds of the session starting (a native AR_ERROR_CLOUD_ANCHORS_NOT_CONFIGURED begins emitting immediately), and any subsequent host throws CloudAnchorsNotConfiguredException. There is no error in logcat — it fails closed silently.

Note this is distinct from the classic host-time ERROR_NOT_AUTHORIZED: because the mode is disabled before any host, hostCloudAnchorAsync(...) throws CloudAnchorsNotConfiguredException synchronously — the async host callback with ERROR_NOT_AUTHORIZED never fires. This points at keyless token acquisition failing at session-configure time, not at host time.

Server-side ARCore API metrics confirm the failure is upstream of the API: not a single request reaches arcore.googleapis.com under an OAuth-client credential. The keyless token is never successfully obtained on-device, so CloudAnchorService.CreateAnchor is never called.

Critically, every documented keyless prerequisite is verified present, and iOS token-auth Cloud Anchors work in the exact same GCP project — so the project, API, billing, and anchor service are all healthy. Only Android keyless is affected.

This is the same AR_ERROR_CLOUD_ANCHORS_NOT_CONFIGURED-despite-ENABLED symptom reported on this SDK before and never resolved (#1173, #1250), which we believe is the silent surfacing of the long-standing keyless ERROR_NOT_AUTHORIZED failure (#80, #58) — pinned here to a fully-verified configuration.

Environment

  • ARCore SDK: com.google.ar:core:1.54.0 (via SceneView io.github.sceneview:arsceneview)
  • Cloud Anchors: persistent, TTL 365 days (hostCloudAnchorAsync(anchor, 365, …)) → keyless is mandatory (API key caps at 24h)
  • Google Play services auth: com.google.android.gms:play-services-auth:21.1.1 (transitive via androidx.credentials:credentials-play-services-auth)
  • Device: Samsung SM-S731U, Android 16 / One UI 8.0 (build fingerprint samsung/r13ssqw/r13s:16); Google Play Services for AR (ARCore) 1.54.260890093 (versionCode 260890093) — the same build reported in FatalException from Session.resume() on Samsung SM-X520 (Galaxy Tab S10 FE) / One UI 8.0 — regression after Play Services for AR auto-update to 1.54.x #1762
  • App package (debug): com.korrelated.kanvio.dev
  • App minify/ProGuard: disabled (isMinifyEnabled = false) — the documented keep-rules are therefore not applicable

Expected behavior

With a valid OAuth 2.0 Android client ID (package + SHA-1) in the project that has the ARCore API enabled, keyless authorization should succeed and CloudAnchorMode.ENABLED should stay enabled, allowing hostCloudAnchorAsync / resolveCloudAnchorAsync.

Actual behavior

  1. sessionConfiguration sets config.cloudAnchorMode = Config.CloudAnchorMode.ENABLED. Reading session.config.cloudAnchorMode back immediately after onSessionCreated confirms it is committed ENABLED.
  2. Within ~1–2 s of the session starting, ARCore begins emitting the native error below, and session.config.cloudAnchorMode now reads DISABLED (verified by reading it again just before the host attempt).
  3. hostCloudAnchorAsync(anchor, 365, …) throws CloudAnchorsNotConfiguredException (synchronously, from nativeHostCloudAnchorAsync); the piece falls back to GPS-only.
  4. No authorization error is logged (logcat filtered for arcore|cloud.?anchor|NOT_AUTHORIZED|auth.?token|oauth|GoogleAuth|permission|token shows nothing beyond the mode-downgrade error below — including nothing from the com.google.android.gms process).
  5. No request reaches arcore.googleapis.com under an OAuth-client credential (server metrics show zero — see below).

The only native output is the silent downgrade itself, repeating from ~1–2 s after session start:

E native: ################ ARCore Native Error ##################
E native: ARCoreError: third_party/arcore/ar/persistence/cloud_anchor_manager.cc:288
E native: ARCoreError: third_party/arcore/ar/core/session.cc:4287
E native: Anchor Hosting is not configured. [type.googleapis.com/util.ErrorSpacePayload=
          'ArStatusErrorSpace::AR_ERROR_CLOUD_ANCHORS_NOT_CONFIGURED']
...
I native: cloud_anchor_manager.cc:530] Anchor Hosting is not configured.   (repeats ~1×/s)

No line names the token/OAuth denial — the failure is entirely inside Google Play services and is not surfaced to the app or logcat.

Configuration verified present (nothing missing per the docs)

Requirement Status
ARCore API (arcore.googleapis.com) enabled in the project ✅ ENABLED
Billing enabled on the project
play-services-auth 16+ ✅ 21.1.1
OAuth 2.0 Android client ID for the exact (package, SHA-1), in the same project as the ARCore API ✅ exists (<CLIENT_ID>.apps.googleusercontent.com, type ANDROID, package com.korrelated.kanvio.dev, cert 75:81:4F:83:86:8A:E0:11:4F:D6:53:22:2C:06:CC:77:59:86:80:D5)
Installed APK signing cert == that SHA-1 (apksigner verify --print-certs) ✅ verified equal
OAuth consent screen not blocking ✅ tried Internal, External + Testing (account added as test user), and External + In productionno change in any mode
ProGuard/R8 keep-rules N/A (minify disabled)

What we ruled out

  • Account / consent-screen gating — failure is identical across Internal, External+Testing, and External+In-production, so keyless authorization is not being gated by the OAuth consent screen or the device Google account.
  • Signing-key mismatch — verified against the actually-installed APK, not just the local build: pulled it (adb shell pm pathadb pull) and ran apksigner verify --print-certs, which reports certificate SHA-1 75:81:4F:83:86:8A:E0:11:4F:D6:53:22:2C:06:CC:77:59:86:80:D5 (SHA-256 f0290eb8dc7709c90f6b10ed89b118e60af7200c793b89de28aa491404ada682) — matching the OAuth client's registered SHA-1 exactly. Rules out a build/CI signing with a different debug key.
  • Project/API/billing — all enabled; and iOS token-auth (below) succeeds against the same project.
  • OAuth-client type ("dedicated" vs Firebase-auto-created) — attempting to create a separate dedicated OAuth Android client for the same (package, SHA-1) is rejected: "The Android package name and fingerprint are already in use." (Google tracking number c2962162132488701.) So the existing client is the single canonical client for that identity; there is no alternative client to switch to.

Contrast: token-auth works in the same project (proves the backend is healthy)

The iOS build of the same app authenticates via token authGARSession.setAuthToken(<JWT>) with a JWT signed by a project service account, aud: https://arcore.googleapis.com/. Two independent proofs the backend is healthy:

  1. In this bug's project (same project): iOS token-auth CloudAnchorService.CreateAnchor returns 200 — so the ARCore API, billing, and Cloud Anchor authorization all function in the very project where Android keyless fails. Android keyless, by contrast, produces zero requests to the API. (The read RPC GetAnchor is not exercised by the keyless failure and is irrelevant to it — the Android failure occurs before any request is sent, so GetAnchor's status either way has no bearing on this bug.)
  2. In our production project (same app, same architecture): the full host→resolve round-trip works end-to-end — CreateAnchor 200 and GetAnchor 200, with Cloud Anchors persisted as recently as this week.

So the ARCore Cloud Anchor service, API enablement, and billing are all functioning — the failure is specific to the Android keyless authorization path never obtaining a token.

Reproduction

  1. Android app with com.google.ar:core:1.54.0, Cloud Anchor mode ENABLED, persistent host TTL 365.
  2. Project with arcore.googleapis.com enabled, billing on, and an OAuth 2.0 Android client for the app's package + signing SHA-1 (here, auto-created by Firebase from the Android app's registered SHA-1).
  3. play-services-auth present; consent screen published.
  4. Launch, enter AR, attempt to host a Cloud Anchor.
  5. Observe: mode downgrades ENABLED → DISABLED within ~1–2 s of session start (AR_ERROR_CLOUD_ANCHORS_NOT_CONFIGURED begins emitting); hostCloudAnchorAsync throws CloudAnchorsNotConfiguredException; no logcat error; no request reaches the ARCore API.

Ask

  • Is there an additional, undocumented requirement for Android keyless authorization when the OAuth Android client was auto-created by Firebase (from the Firebase Android app's SHA registration) rather than hand-created — given that a hand-created one for the same (package, SHA-1) is rejected as a duplicate?
  • Can you surface the actual token-acquisition failure to logcat? Today it fails silently, which makes keyless ERROR_NOT_AUTHORIZED-class problems nearly undiagnosable from the client.

References (all open & unresolved)

Same AR_ERROR_CLOUD_ANCHORS_NOT_CONFIGURED symptom — mode ENABLED yet host/resolve reports "not configured" — reported on this SDK before and never resolved (both thinner than this report; neither identifies the keyless-auth cause):

Keyless-authorization ErrorNotAuthorized, open & unresolved (the likely underlying failure this report exposes as a silent mode-downgrade):

  • google-ar/arcore-unity-extensions fbx file load #80 — "Cloud Anchors ErrorNotAuthorized, keyless authentication"
  • google-ar/arcore-unity-extensions Asus Zenfone AR support #58 — "Persistent Cloud Anchor (keyless) Error Not Authorized … still not resolved"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions