ScreenSync MCP guide

Testing Android Passkeys with Claude Code and MCP

Automate Android Credential Manager, FIDO2 WebAuthn passkeys, biometric authentication challenges, and credential bottom sheets on real devices using Claude Code and ScreenSync MCP.

Testing Android Passkeys with Claude Code and MCP

Testing Android passkey authentication, FIDO2 cryptographic handshakes, and Credential Manager bottom sheets on physical mobile hardware represents one of the most critical yet structurally elusive disciplines in modern mobile quality assurance. In the modern Android operating system, passwordless authentication has transitioned from an experimental convenience into a mandatory architectural standard. Google's Credential Manager API (androidx.credentials:credentials:1.3.0 and newer) has unified disparate authentication mechanisms—including legacy Smart Lock for Passwords, FIDO2 WebAuthn public key credentials, federated Identity Providers like Sign-In with Google, and device-bound biometric authenticators—into a single, consolidated user experience. However, testing these passwordless journeys under real-world operating system conditions has historically paralyzed continuous integration pipelines. Conventional automated testing frameworks like Espresso and Robolectric fail completely when evaluating Credential Manager because the authentication bottom sheet, account picker, and biometric verification dialogs are rendered out-of-process by Google Play Services (com.google.android.gms) and the Android System UI (com.android.systemui). In-process test harnesses cannot inspect out-of-process view trees, cannot dispatch touch events to system modals without encountering security sandbox violations, and cannot evaluate hardware-backed Trusted Execution Environment (TEE) cryptographic attestation. By integrating Anthropic's autonomous terminal coding agent Claude Code with the Model Context Protocol (MCP) using ScreenSync MCP, mobile engineering teams can bridge desktop AI reasoning with physical Android hardware. This architecture automates end-to-end passkey registration, selects credentials from Google Play Services bottom sheets, simulates biometric cancellations, and analyzes real-time cryptographic telemetry without requiring device root privileges.

Key Takeaways

  • Out-of-process credential sheet inspection: ScreenSync MCP inspects system-level accessibility node trees across process boundaries using get_ui_hierarchy, enabling Claude Code to locate and assert Google Play Services passkey bottom sheets that standard Espresso runners cannot touch.
  • Low-latency non-invasive visual verification: Using Android's native MediaProjection API, ScreenSync MCP streams display frames to the workstation hub at approximately 94 milliseconds median latency without root or ADB overhead, allowing Claude Code's visual models to verify cryptographic shield icons and biometric prompts.
  • Deterministic passkey touch dispatch: Claude Code drives Google Play Services account choosers and passkey confirmation dialogs directly across physical Android hardware using coordinate and element-based touch tools like control_tap.
  • Real-time WebAuthn diagnostic telemetry: Claude Code monitors real-time get_logcat telemetry during passkey creation and assertion, triaging public key challenge mismatches, Digital Asset Links errors, and FIDO provider cancellations.
  • Hardware-in-the-loop Keystore and TEE validation: Testing on physical Android smartphones running Android 7.0 or higher uncovers OEM-specific hardware Keystore and StrongBox cryptographic storage bugs across Samsung One UI, Xiaomi HyperOS, and Google Pixel that virtual emulators fail to reproduce.
  • Unified 226-tool developer automation plane: ScreenSync MCP unites 29 mobile and desktop tools (including 12 control_*, 3 os_*, and 14 capture tools) with 197 browser automation tools (web_*), empowering AI agents to verify mobile passkey logins against cloud WebAuthn relying party servers within a single test execution loop.

The Passkey Testing Dilemma: Why Credential Manager Breaks In-Process Suites

Android Credential Manager breaks in-process testing suites because authentication bottom sheets execute within Google Play Services rather than the application process. Frameworks like Espresso cannot query out-of-process view trees, intercept biometric verification challenges, or automate cross-window system dialogs without encountering security sandbox exceptions or test suite deadlocks.

To understand why mobile engineering teams struggle to automate passkey testing, developers must analyze the architectural security boundaries separating modern Android applications from the Credential Manager subsystem. When an Android application implements androidx.credentials, the local application code does not render the account selection modal or handle the cryptographic private key directly. Instead, the application invokes CredentialManager.getCredential() or CredentialManager.createCredential(), passing a cryptographic challenge payload generated by the backend Relying Party (RP) server.

The Android framework intercepts this request and initiates an Inter-Process Communication (IPC) transaction with Google Play Services (com.google.android.gms.auth.api.credentials). Google Play Services evaluates the calling application's cryptographic signature, verifies the domain relationship against the server's Digital Asset Links (/.well-known/assetlinks.json), retrieves stored public key credentials from Google Password Manager or on-device hardware-backed security modules, and renders a system-level modal bottom sheet directly on top of the active display window.

This architecture introduces insurmountable barriers for traditional Android automated testing harnesses:

  1. Window Isolation and Security Sandboxing: Frameworks such as Espresso and Robolectric execute strictly within the host application process and bind to the active Activity window. Because the Credential Manager bottom sheet is rendered by an external system process (com.google.android.gms), Espresso's ViewMatcher APIs cannot traverse the view hierarchy. Attempting to click a "Continue" button or select a passkey account throws an immediate NoMatchingViewException.
  2. BiometricPrompt Modal Interception: Passkey creation (CreatePublicKeyCredentialRequest) and assertion (GetPublicKeyCredentialOption) frequently mandate user verification via biometric sensors (fingerprint or 3D face unlock) or device screen lock (PIN, pattern, or password). The BiometricPrompt UI is rendered by SystemUI inside a secure system overlay window. In-process test runners freeze or time out because they possess no mechanism to interact with the system lock screen or biometric hardware daemon.
  3. Digital Asset Links Domain Validation: Android enforces strict domain binding between the native Android app package name, its signing certificate SHA-256 fingerprint, and the Relying Party identifier (RP ID) hosted at https://example.com/.well-known/assetlinks.json. Virtual emulators lacking Google Play Services or executing in disconnected CI containers cannot validate these asset links, generating false-positive CreateCredentialCustomException failures that do not reflect production behavior.
  4. Hardware-Backed Keystore vs Software Mocks: Production passkeys rely on hardware-backed cryptographic modules—such as Android Keystore, StrongBox Keymaster, and ARM TrustZone Trusted Execution Environments (TEE). Synthetic test runners like Robolectric mock cryptographic key generation in the host Java Virtual Machine. These JVM mocks completely bypass hardware key attestation checks, hardware rate limiting, and OEM-specific cryptographic driver quirks found on physical Qualcomm, MediaTek, and Samsung Exynos silicon.
Evaluation Metric / Feature JVM Unit Tests (Robolectric) Standard Espresso / UIAutomator Android Emulator Console (adb emu) ScreenSync MCP + Claude Code (Physical Hardware)
Execution Surface Local JVM / Mocked Android Framework In-App Process Instrumentation Virtual AOSP System Image Authentic Physical Android Device (Android 7.0+)
Credential Bottom Sheet Access Completely Mocked (No OS IPC) Blind (Google Play Services Isolated) ADB Shell Only (No Agent Cognition) Full System Access via get_ui_hierarchy
Biometric Challenge Triage Mocked Callback Invocations Unsupported (SystemUI Blocked) Manual CLI Fingerprint Injection End-to-End Triage: UI Trees + get_logcat Telemetry
Digital Asset Links Validation Bypassed / Mocked Incomplete in Local CI Variable (Depends on Google Play Image) Real-World Validation against Production RP Servers
Hardware TEE / StrongBox Fidelity Zero (Host Software Crypto) Low (Software Keystore Emulation) Generic Goldfish / Ranchu Drivers Authentic Silicon TEE (Qualcomm, MediaTek, Exynos)
Display Capture Overhead Zero Visual Plane In-Memory Bitmap Scraping High-Latency Virtual Framebuffer ~94 ms Median LAN Latency via Native MediaProjection
Diagnostic Logcat Analysis Console STDOUT Only Post-Mortem Crash Logs Uncoordinated Shell Scripts Real-Time Logcat Filtering & State Assertion (get_logcat)
Tool Ecosystem Breadth In-Memory Assertions Only Android Matcher Matchers Only Command-Line ADB Only 226 Tools: Mobile Hardware, Desktop OS, & Browser Suite

Architecture of ScreenSync MCP: Orchestrating Passkey and FIDO2 Verification

ScreenSync MCP orchestrates Android passkey verification by bridging workstation AI reasoning with physical Android devices over Model Context Protocol stdio streams. Android native MediaProjection streams display frames at approximately 94 milliseconds median latency without root, while system accessibility node dumping and ADB control channels automate out-of-process credential selection.

ScreenSync MCP resolves the passkey testing bottleneck by organizing developer automation across four synchronized operational planes operating between your workstation and connected mobile hardware:

  • Autonomous Agent Reasoning Plane: Claude Code executes in your workstation terminal over local stdio JSON-RPC, parsing test directives, inspecting visual display feeds, and analyzing structured accessibility trees without human intervention.
  • Workstation Desktop Hub Gateway: The ScreenSync MCP hub runs on Node.js 18 or newer (serving HTTP on port 3000 and authenticated with SCREEN_SYNC_TOKEN), brokering high-throughput communication between Claude Code, device sockets, and browser extensions.
  • Native MediaProjection Visual Plane: The ScreenSync MCP mobile application runs on Android 7.0 (API level 24) or newer. Upon user consent during session initialization, it captures the hardware SurfaceFlinger composite display and streams frames over local Wi-Fi or USB at approximately 94 ms median latency without requiring device root privileges.
  • Android Debug Bridge Telemetry Plane: ADB bridge channels execute shell commands, inject touch events (control_tap) into out-of-process bottom sheets, and stream real-time logcat buffers (get_logcat) directly into Claude Code's context window.

1. Cross-Window Accessibility Node Inspection

When Claude Code invokes get_ui_hierarchy, ScreenSync MCP dumps the active window tree using Android's system accessibility framework. Unlike Espresso, which is strictly bounded by the host application's active activity window, Android's accessibility layer spans system overlay windows. Claude Code can inspect bottom sheets rendered by com.google.android.gms, locate the "Continue with Passkey" action button, assert the presence of specific user account emails or usernames, and verify security disclaimer text. To understand why structural node trees must accompany visual pixels to prevent UI coordinate drift during automated sweeps, consult our detailed analysis on Android UI hierarchy vs computer vision: why LLMs need structural node trees to prevent drift.

2. High-Speed SurfaceFlinger Frame Capture

While accessibility node trees provide structured text assertions, cryptographic quality assurance requires verifying that visual security cues render correctly for the end user. ScreenSync MCP utilizes Android's native MediaProjection API. Because this API intercepts frames directly from the hardware display compositor (SurfaceFlinger), it captures the entire screen surface, including Google Play Services bottom sheets, fingerprint glyph animations, and lock screen security prompts. The capture engine operates with zero root requirements and zero ADB overhead, delivering live visual telemetry to Claude Code with minimal CPU overhead on the mobile device.

3. Out-of-Process Diagnostic Telemetry via Logcat

When passkey authentication fails, the error frequently occurs inside the Google Play Services daemon before reaching the application callback. ScreenSync MCP provides get_logcat, enabling Claude Code to stream operating system log buffers continuously. Claude Code monitors core security tags:

  • CredentialManager: Tracks credential request creation, provider routing, and client response packaging.
  • Fido2PendingIntent: Monitors WebAuthn public key challenge generation, cryptographic signature validation, and Digital Asset Links verification.
  • PasskeyCredentialHelper: Validates JSON serialization of authenticatorData, clientDataJSON, and signature blocks.
  • BiometricPrompt: Tracks biometric sensor activation, user authentication success, and cancellation signals.
Passkey Subsystem MCP Inspection Tool Underlying Android Primitive Primary QA Verification Objective
Credential Bottom Sheet get_ui_hierarchy Accessibility Node Hierarchy (AccessibilityNodeInfo) Locating passkey account rows, username labels, and cancel buttons
Visual Verification get_latest_screenshot Native MediaProjection & SurfaceFlinger Asserting visual rendering of biometric glyphs and security shields
Passkey Selection control_tap Linux Input Subsystem (/dev/input/event*) Tapping Google Play Services credential accounts and continue buttons
WebAuthn Challenge Triage get_logcat Android Logcat Daemon (logd) Asserting challenge signing, clientDataJSON, and FIDO provider codes
Biometric Challenge Injection ADB Shell Commands BiometricService & FingerprintService Simulating biometric acceptance or sensor cancellation
Backend RP Validation 197 web_* Extension Tools Chrome DevTools Protocol & Web Tabs Verifying registered passkeys in cloud authentication dashboards

Step-by-Step Setup: Connecting Claude Code to Physical Android Hardware

Connecting Claude Code to physical Android hardware requires deploying the ScreenSync MCP desktop hub on Node.js 18, registering the stdio MCP server in Claude Code, installing the free ScreenSync MCP mobile application on Android 7.0+, granting MediaProjection screen capture consent, and establishing ADB connectivity over USB or wireless debugging.

Setting up an automated passkey testing pipeline on Windows, macOS, or Linux requires fewer than five minutes:

Step 1: Workstation Environment Preparation

Ensure that your development workstation contains the necessary runtimes and command-line tools:

  1. Operating System: Windows 10/11, macOS (Intel or Apple Silicon), or Linux.
  2. Node.js Runtime: Node.js 18.0.0 or higher (node --version).
  3. Android Platform Tools: Android Debug Bridge (adb) installed and available in system PATH (adb version).
  4. Physical Android Device: A real smartphone running Android 7.0 (API level 24) or higher, connected via USB data cable or paired over local Wi-Fi.

Step 2: Launch the ScreenSync MCP Desktop Hub

Download the desktop hub distribution package from screensyncmcp.tech and extract the contents to a dedicated directory. Launch the hub using the platform-specific launcher:

# On Windows (PowerShell or Command Prompt)
cd C:\tools\screensync-hub
.\start-hub.bat

# On macOS or Linux (Terminal)
cd ~/tools/screensync-hub
chmod +x start-hub.sh
./start-hub.sh

The hub starts a local HTTP server listening on port 3000 to receive encrypted MediaProjection display frames and initializes the MCP stdio interface. The terminal displays your session bearer token (SCREEN_SYNC_TOKEN) and presents an ASCII pairing QR code. For detailed parameter reference, consult the official ScreenSync MCP setup guide.

Step 3: Register ScreenSync MCP with Claude Code

In your workstation terminal, register the desktop hub with the Claude Code CLI:

claude mcp add screensync node "/path/to/screensync-hub/screensync-hub.js"

If you are using Claude Desktop, open your claude_desktop_config.json configuration file (accessible via Settings > Developer > Edit Config) and add the server entry:

{
  "mcpServers": {
    "screensync": {
      "command": "node",
      "args": ["/path/to/screensync-hub/screensync-hub.js"],
      "env": {
        "SCREEN_SYNC_TOKEN": "your_secure_bearer_token_here"
      }
    }
  }
}

(Note: Claude Code, Claude Desktop, Cursor, and VS Code agent mode communicate with ScreenSync MCP via local stdio processes and are fully supported. The ChatGPT web application requires remote HTTPS webhooks and does not support local stdio MCP servers).

Step 4: Configure the Physical Android Smartphone

  1. On your physical Android device, download and install the free ScreenSync MCP mobile application (com.screensync.mcp) from Google Play.
  2. Open the app. Allow mDNS discovery to automatically pair with the desktop hub over your local Wi-Fi subnet, or scan the QR code displayed in your workstation terminal.
  3. Tap Start Projection and confirm the native Android MediaProjection screen capture consent dialog.
  4. Enable Developer Options (Settings > About Phone > tap Build Number 7 times) and turn on USB Debugging. If you prefer a cable-free testing environment, review our detailed guide on Android wireless debugging for MCP servers.
  5. Verify ADB connectivity in your terminal:
    adb devices

Step 5: Android Application Passkey Implementation

In your Android application, implement modern passkey registration and authentication using androidx.credentials:credentials:1.3.0 and androidx.credentials:credentials-play-services-auth:1.3.0. The following Kotlin class encapsulates passkey creation and assertion workflows using coroutines:

package com.example.screensync.security

import android.content.Context
import android.util.Log
import androidx.credentials.CreatePublicKeyCredentialRequest
import androidx.credentials.CreatePublicKeyCredentialResponse
import androidx.credentials.CredentialManager
import androidx.credentials.GetCredentialRequest
import androidx.credentials.GetCredentialResponse
import androidx.credentials.GetPublicKeyCredentialOption
import androidx.credentials.PublicKeyCredential
import androidx.credentials.exceptions.CreateCredentialCancellationException
import androidx.credentials.exceptions.CreateCredentialCustomException
import androidx.credentials.exceptions.CreateCredentialException
import androidx.credentials.exceptions.GetCredentialCancellationException
import androidx.credentials.exceptions.GetCredentialException
import androidx.credentials.exceptions.NoCredentialException

class PasskeyAuthManager(private val context: Context) {

    private val credentialManager = CredentialManager.create(context)

    companion object {
        const val TAG = "ScreenSyncPasskeyQA"
    }

    suspend fun registerPasskey(
        creationJsonPayload: String
    ): Result<CreatePublicKeyCredentialResponse> {
        Log.i(TAG, "Initiating passkey registration request")
        val request = CreatePublicKeyCredentialRequest(creationJsonPayload)

        return try {
            val response = credentialManager.createCredential(
                context = context,
                request = request
            ) as CreatePublicKeyCredentialResponse
            Log.i(TAG, "Passkey registration successful: ${response.registrationResponseJson}")
            Result.success(response)
        } catch (e: CreateCredentialCancellationException) {
            Log.w(TAG, "Passkey creation cancelled by user: ${e.message}")
            Result.failure(e)
        } catch (e: CreateCredentialCustomException) {
            Log.e(TAG, "Custom credential exception: ${e.type} - ${e.message}", e)
            Result.failure(e)
        } catch (e: CreateCredentialException) {
            Log.e(TAG, "Passkey creation failed: ${e.message}", e)
            Result.failure(e)
        }
    }

    suspend fun authenticateWithPasskey(
        requestJsonPayload: String
    ): Result<String> {
        Log.i(TAG, "Initiating passkey authentication request")
        val getPublicKeyCredentialOption = GetPublicKeyCredentialOption(requestJsonPayload)
        val getCredRequest = GetCredentialRequest(
            listOf(getPublicKeyCredentialOption)
        )

        return try {
            val result: GetCredentialResponse = credentialManager.getCredential(
                context = context,
                request = getCredRequest
            )
            val credential = result.credential
            if (credential is PublicKeyCredential) {
                val responseJson = credential.authenticationResponseJson
                Log.i(TAG, "Passkey authentication successful: $responseJson")
                Result.success(responseJson)
            } else {
                Log.e(TAG, "Unexpected credential type returned: ${credential.type}")
                Result.failure(IllegalStateException("Unsupported credential type"))
            }
        } catch (e: GetCredentialCancellationException) {
            Log.w(TAG, "User cancelled passkey selection: ${e.message}")
            Result.failure(e)
        } catch (e: NoCredentialException) {
            Log.w(TAG, "No passkey credentials available on device: ${e.message}")
            Result.failure(e)
        } catch (e: GetCredentialException) {
            Log.e(TAG, "Passkey authentication error: ${e.message}", e)
            Result.failure(e)
        }
    }
}

When building apps that pair passkey authentication with biometric prompts or hardware-backed keystores, explore our companion guide on testing Android biometrics with Claude Code and MCP. If your application requires camera-based QR code passkey cross-device pairing, review our in-depth analysis on testing Android runtime permissions with Claude Code and MCP.

Core QA Workflows: Automating Registration, Passkey Selection, and Biometric Fallback

Automating Android passkey quality assurance requires validating three essential flows: initial passkey registration with WebAuthn challenge signing, 1-tap passkey authentication from the Google Play Services bottom sheet, and graceful fallback to device screen lock or password autofill when biometric hardware verification fails.

Workflow 1: Validating New Passkey Creation Flow

Validating new passkey creation requires asserting that the application receives a cryptographic challenge from the server, triggers the Google Play Services passkey enrollment bottom sheet, prompts the user to save the passkey to Google Password Manager, and returns valid WebAuthn clientDataJSON and attestationObject payloads.

Developer Instruction:
"Claude, launch com.example.screensync.security, navigate to the account settings screen, tap 'Create Passkey', inspect the Google Play Services bottom sheet using get_ui_hierarchy, tap 'Continue', stream get_logcat to verify that registrationResponseJson was received, and assert that the UI displays 'Passkey Registered'."

Step 1: Launch Application and Trigger Passkey Enrollment

Claude Code invokes control_launch_app { package: "com.example.screensync.security" }. Upon screen rendering, Claude Code dispatches control_tap on the "Create Passkey" button, triggering PasskeyAuthManager.registerPasskey() with a mock WebAuthn challenge:

{
  "challenge": "dGVzdF9jaGFsbGVuZ2Vfbm9uY2VfMTI4Yml0cw==",
  "rp": {
    "name": "ScreenSync Security Lab",
    "id": "screensyncmcp.tech"
  },
  "user": {
    "id": "dXNlcl84OTQyMWFiYw==",
    "name": "[email protected]",
    "displayName": "Staff Mobile Engineer"
  },
  "pubKeyCredParams": [
    { "type": "public-key", "alg": -7 },
    { "type": "public-key", "alg": -257 }
  ],
  "timeout": 60000,
  "attestation": "none"
}

Step 2: Assert Google Play Services Bottom Sheet via UI Hierarchy

Claude Code invokes get_ui_hierarchy to inspect the out-of-process modal:

<node class="android.widget.FrameLayout" package="com.google.android.gms">
  <node class="android.widget.TextView" resource-id="com.google.android.gms:id/header_title" text="Create a passkey" />
  <node class="android.widget.TextView" resource-id="com.google.android.gms:id/account_email" text="[email protected]" />
  <node class="android.widget.TextView" resource-id="com.google.android.gms:id/provider_name" text="Saved to Google Password Manager" />
  <node class="android.widget.Button" resource-id="com.google.android.gms:id/continue_button" text="Continue" />
</node>

Step 3: Tap Continue and Stream Logcat Verification

Claude Code calls control_tap on the coordinates of the continue_button. It immediately streams get_logcat with tag ScreenSyncPasskeyQA:

I ScreenSyncPasskeyQA: Initiating passkey registration request
D Fido2PendingIntent: Constructing MakeCredentialRequest for RP: screensyncmcp.tech
D KeymasterUtils: Hardware keypair generated in StrongBox Keymaster (Algorithm: EC, Curve: P-256)
I ScreenSyncPasskeyQA: Passkey registration successful: {"id":"AbCdEf123...","rawId":"...","response":{"clientDataJSON":"...","attestationObject":"..."}}

Claude Code calls get_ui_hierarchy to assert that the application displays "Passkey Registered" with a green verification badge.

Workflow 2: Automating Passkey Sign-In and Account Selection

Automating passkey sign-in requires validating that Credential Manager queries local and synchronized passkeys, displays an interactive account picker bottom sheet, handles user account selection, and securely signs the server challenge.

Developer Instruction:
"Claude, log out of com.example.screensync.security, tap 'Sign in with Passkey', verify that the Google Play Services account picker appears with get_ui_hierarchy, select the account '[email protected]', and assert that authentication succeeds."

Step 1: Trigger Sign-In Flow

Claude Code navigates to the login screen and taps "Sign in with Passkey". The application issues GetCredentialRequest with GetPublicKeyCredentialOption:

{
  "challenge": "YXV0aF9jaGFsbGVuZ2Vfbm9uY2VfMjU2Yml0cw==",
  "rpId": "screensyncmcp.tech",
  "timeout": 60000,
  "userVerification": "required"
}

Step 2: Inspect Account Picker Bottom Sheet

Claude Code executes get_ui_hierarchy to parse the account selection list rendered by com.google.android.gms:

<node class="android.widget.LinearLayout" package="com.google.android.gms" resource-id="com.google.android.gms:id/credential_container">
  <node class="android.widget.TextView" resource-id="com.google.android.gms:id/title" text="Choose a passkey" />
  <node class="android.widget.RelativeLayout" resource-id="com.google.android.gms:id/credential_item">
    <node class="android.widget.TextView" resource-id="com.google.android.gms:id/user_name" text="[email protected]" />
    <node class="android.widget.TextView" resource-id="com.google.android.gms:id/display_name" text="Staff Mobile Engineer" />
  </node>
</node>

Step 3: Select Credential and Assert WebAuthn Signature

Claude Code calls control_tap directly on the credential row. It reads get_logcat:

I ScreenSyncPasskeyQA: Initiating passkey authentication request
D Fido2PendingIntent: Processing GetAssertionRequest for RP: screensyncmcp.tech
D KeymasterUtils: Hardware signature generated via Keystore TEE
I ScreenSyncPasskeyQA: Passkey authentication successful: {"id":"AbCdEf123...","response":{"authenticatorData":"...","clientDataJSON":"...","signature":"MEQCICx4...","userHandle":"..."}}

Claude Code captures get_latest_screenshot to confirm that the user has been routed to the authenticated home dashboard. To understand how to control physical hardware seamlessly without rooting, explore our foundational guide on how to control an Android phone with AI without root.

Workflow 3: Testing Biometric Cancellation, Timeout, and Password Fallback

Robust mobile applications must handle real-world edge cases: users dismissing the passkey bottom sheet, biometric sensor timeouts, and hardware authentication failures. Claude Code validates that the application catches GetCredentialCancellationException and presents an immediate fallback to traditional password or PIN fields.

Developer Instruction:
"Claude, trigger passkey sign-in, simulate user cancellation by tapping outside the Google Play Services modal, assert via get_logcat that GetCredentialCancellationException was caught, and verify that the UI falls back to manual email and password input fields."

Step 1: Trigger Sign-In and Dismiss Modal

Claude Code triggers passkey login, rendering the Google Play Services bottom sheet. Claude Code dispatches control_tap on coordinates outside the modal bounds (e.g., top margin (W*0.5, H*0.1)), simulating a user tap-to-dismiss gesture.

Step 2: Assert Cancellation Exception in Logcat

Claude Code streams get_logcat:

W ScreenSyncPasskeyQA: User cancelled passkey selection: androidx.credentials.exceptions.GetCredentialCancellationException: User cancelled the modal dialog
I LoginActivity: Credential Manager cancelled; displaying fallback password login form

Step 3: Assert Fallback Form via UI Hierarchy

Claude Code calls get_ui_hierarchy to assert that the application revealed the manual credential inputs:

<node class="android.widget.EditText" resource-id="com.example.screensync.security:id/password_input" />
<node class="android.widget.Button" resource-id="com.example.screensync.security:id/submit_password_button" text="Sign In with Password" />
Passkey QA Scenario ScreenSync MCP Tools Primary Detection Mechanism Critical Vulnerability Caught
Passkey Registration get_ui_hierarchy, get_logcat Accessibility tree match + registrationResponseJson Server challenge rejected or missing RP ID configuration
Passkey Sign-In control_tap, get_logcat Bottom sheet tap + authenticationResponseJson Signature assertion mismatch or expired WebAuthn token
User Dismissal control_tap, get_ui_hierarchy Logcat GetCredentialCancellationException App freezes indefinitely when user taps outside bottom sheet
No Stored Passkeys get_logcat, get_ui_hierarchy Logcat NoCredentialException handling App fails to render password fallback for new users
Digital Asset Links Error get_logcat Logcat TYPE_CREATE_CREDENTIAL_PROVIDER_CONFIGURATION SHA-256 fingerprint missing from assetlinks.json
Biometric Sensor Lockout ADB Shell, get_logcat Logcat BiometricService: ERROR_LOCKOUT App crashes when too many invalid biometric attempts occur
Hybrid Web-to-App Login 197 web_* Extension Tools Browser tab check + mobile passkey state Cross-device QR code handshake fails to complete

Triaging Passkey Failures: Digital Asset Links, User Cancellation, and OEM KeyStore Bugs

Triaging Android passkey failures requires diagnosing system error signatures emitted by Google Play Services, Digital Asset Links verifiers, and OEM KeyStore daemons. Claude Code captures real-time logcat traces for domain mismatches, cryptographic origin validation failures, and biometric timeouts, verifying that mobile applications handle authentication exceptions gracefully.

When passkey operations fail on physical devices, the underlying cause is rarely an application runtime exception. Instead, failure modes originate within the operating system security daemons. By analyzing telemetry through get_logcat, Claude Code autonomously triages and remediates common failure modes:

1. Triaging Digital Asset Links Mismatches

The most prevalent passkey failure in continuous deployment pipelines is the CreateCredentialCustomException triggered by asset links validation failure. When the signing certificate used for the local debug APK does not match the SHA-256 fingerprint registered in the server's assetlinks.json, Google Play Services refuses to associate the domain with the app.

  • Diagnostic Logcat Signature:
    E CredentialManager: Provider failure: androidx.credentials.TYPE_CREATE_CREDENTIAL_PROVIDER_CONFIGURATION_EXCEPTION
    E Fido2PendingIntent: Caller package com.example.screensync.security is not linked to origin screensyncmcp.tech
    E Fido2PendingIntent: Asset Links verification failed: SHA-256 [14:8B:2A:...] not found in assetlinks.json
  • Autonomous Remediation Action: Claude Code detects the configuration mismatch, extracts the debug keystore's SHA-256 fingerprint via keytool -list -v -keystore ~/.android/debug.keystore, and outputs the updated assetlinks.json snippet required for the server. For applications that embed authentication within WebViews, review our guide on testing Android WebViews and hybrid apps with Claude Code and MCP.

2. Triaging Hardware TEE KeyStore Lockups on Custom OEM Skins

Custom OEM distributions (such as Samsung One UI running Knox or Xiaomi HyperOS) enforce proprietary cryptographic rate-limiting policies within their hardware Keymaster daemons. If an automated test suite rapidly generates and discards passkeys during regression testing, the hardware TEE enters a temporary lockout state.

  • Diagnostic Logcat Signature:
    E KeymasterUtils: Hardware keypair creation aborted: KeymasterResponseCode(-28) KM_ERROR_OPERATION_CANCELLED
    W KeyStore: System Keystore rate limit exceeded. TEE lock active for 30 seconds.
    E ScreenSyncPasskeyQA: Passkey creation failed: androidx.credentials.exceptions.CreateCredentialInterruptedException
  • Autonomous Remediation Action: Claude Code identifies the hardware TEE rate-limiting signature, inserts an exponential backoff sleep loop into the test harness, and prevents the continuous integration worker from marking the test as an application defect.

3. Triaging Biometric Sensor Lockouts

When evaluating fallback flows, repeated failed biometric attempts cause the Android BiometricService to enforce temporary or permanent sensor lockouts (BIOMETRIC_ERROR_LOCKOUT_PERMANENT).

  • Diagnostic Logcat Signature:
    W BiometricService: Too many failed attempts. Fingerprint sensor locked.
    E CredentialManager: Biometric prompt returned ERROR_LOCKOUT. Falling back to device credential.
  • Autonomous Remediation Action: Claude Code dispatches an ADB command to reset the biometric sensor lockout state or provides device PIN credentials to unlock the Keystore daemon. To learn more about securing communications between the workstation hub and mobile devices, read our architecture review on security and privacy in mobile screen MCP servers.
Passkey Error Signature Subsystem / Component Root Cause Description Engineering Remediation
ProviderConfigurationException Google Play Services / FIDO2 Calling app signature missing from server assetlinks.json Register debug keystore SHA-256 in /.well-known/assetlinks.json
GetCredentialCancellationException CredentialManager Framework User dismissed account bottom sheet or tapped backdrop Display fallback password or magic link authentication UI
NoCredentialException Google Password Manager No matching passkeys or credentials found for RP ID Prompt user to register a new passkey or use password autofill
CreateCredentialInterruptedException Hardware Keymaster / TEE Hardware Keystore rate limit or concurrent crypto lock Implement test retry backoff; avoid rapid key generation bursts
BiometricErrorLockout BiometricService / SystemUI Too many invalid biometric verification attempts Unlock device with primary device PIN/password via ADB
WebAuthnClientDataJsonMismatch Server RP Middleware Challenge nonce expired or RP ID domain mismatch Ensure challenge timestamp is valid (< 5 min) and matches origin
UnsupportedCredentialTypeException CredentialManager Client App received password or federation credential unexpectedly Implement multi-credential handlers (CustomCredential, PasswordCredential)

Production Checklist for Automated Android Passkey Testing

Automating Android passkey testing requires verifying workstation runtime environments, local desktop hub pairing, mobile application MediaProjection screen capture consent, ADB shell access, and Digital Asset Links configurations before deploying autonomous mobile testing pipelines across physical continuous integration device fleets.

  • [ ] Workstation environment runs Node.js 18+ with Android Platform Tools adb configured in system PATH.
  • [ ] ScreenSync MCP desktop hub launched and actively listening on HTTP port 3000 (start-hub.bat or start-hub.sh).
  • [ ] ScreenSync MCP registered in Claude Code via claude mcp add screensync node "/path/to/screensync-hub.js".
  • [ ] Free ScreenSync MCP client (version 2.5.7+) installed on physical Android 7.0+ smartphone from Google Play.
  • [ ] Target device and workstation paired over local Wi-Fi subnet; mDNS discovery or QR code pairing authenticated.
  • [ ] MediaProjection screen capture consent granted on device for ~94 ms low-latency visual streaming without root.
  • [ ] USB debugging or wireless debugging authorized on target Android hardware (adb devices confirms authorized status).
  • [ ] Application implements androidx.credentials:credentials:1.3.0 and credentials-play-services-auth:1.3.0.
  • [ ] Digital Asset Links (/.well-known/assetlinks.json) deployed on production and staging RP domains with correct SHA-256 fingerprints.
  • [ ] Claude Code monitors CredentialManager, Fido2PendingIntent, and BiometricPrompt logcat tags using get_logcat.
  • [ ] Test scripts simulate user cancellation and verify that the UI gracefully reveals manual email/password fallbacks.
  • [ ] Test harness evaluates hardware Keystore resilience across genuine physical devices rather than virtual emulators.
Architecture Layer Verification Objective MCP Command / Diagnostic Tool Acceptance Criteria
Visual Capture Plane Low-Latency Display Stream get_latest_screenshot Frame delivered in ~94 ms over LAN via MediaProjection
Bottom Sheet Modal Credential Chooser Display get_ui_hierarchy Modal rendered by com.google.android.gms with valid account list
Touch Event Dispatch Account Row Selection control_tap on account row Account item triggers WebAuthn public key signing
Biometric Challenge Visual Sensor Glyph get_latest_screenshot Biometric fingerprint or face glyph visible without graphical tearing
Logcat Telemetry Cryptographic Assertion get_logcat (filter: ScreenSyncPasskeyQA) authenticationResponseJson returned with valid signature
Fallback Form State Graceful Degradation get_ui_hierarchy Password fields rendered upon GetCredentialCancellationException
Full-Stack Assertion Cloud Dashboard Sync 197 web_* Extension Tools Cloud identity portal confirms passkey token creation and login session

Conclusion

Automating Android passkey authentication, FIDO2 WebAuthn cryptographic verification, and Credential Manager bottom sheets with Claude Code and ScreenSync MCP bridges a critical divide in modern mobile quality assurance. By combining non-invasive MediaProjection screen capture, system-wide accessibility hierarchy inspection across Google Play Services windows, deterministic touch event dispatch via ADB, and out-of-process logcat telemetry, engineering teams can validate complex passwordless journeys across genuine physical Android hardware.

ScreenSync MCP provides the ScreenSync MCP Android app, ScreenSync MCP hub, and ScreenSync MCP browser extension for engineering teams worldwide. Explore our comprehensive technical documentation on our developer portal, consult the setup guide to deploy your local workstation gateway, and download the free mobile application directly: Get it on Google Play to start automating real physical Android hardware today.

FAQ

Questions, answered

Why do traditional Android test frameworks fail to test Credential Manager passkeys?

Traditional frameworks like Espresso fail because the Credential Manager bottom sheet and biometric verification dialogs are rendered out-of-process by Google Play Services and System UI. In-process test runners cannot access external window hierarchies, cannot click system modals, and cannot evaluate hardware-backed Trusted Execution Environment operations.

How does ScreenSync MCP inspect Google Play Services passkey bottom sheets without root?

ScreenSync MCP inspects Google Play Services bottom sheets by querying Android's system-level accessibility framework via get_ui_hierarchy. Unlike Espresso, Android's accessibility node trees span system overlay windows, allowing AI agents to locate passkey account rows, usernames, and action buttons without device root privileges.

Can Claude Code automate passkey account selection on physical Android devices?

Yes, Claude Code automates passkey selection by combining get_ui_hierarchy to locate the coordinates of target account rows with control_tap to dispatch Linux touch events via the desktop hub. This allows agents to choose specific passkey accounts and tap continue buttons deterministically.

What is the role of Digital Asset Links in Android passkey authentication testing?

Digital Asset Links verify domain binding between the native Android app package, its signing certificate SHA-256 fingerprint, and the server Relying Party ID. If the debug keystore fingerprint is missing from assetlinks.json, Google Play Services throws an immediate ProviderConfigurationException.

How does ScreenSync MCP stream mobile screen frames during passkey testing without ADB overhead?

ScreenSync MCP streams screen frames using Android's native MediaProjection API after session initialization consent. The mobile client captures hardware SurfaceFlinger composite frames and streams them directly to the workstation desktop hub over local Wi-Fi or USB at approximately 94 milliseconds median latency without consuming ADB bandwidth.

How does Claude Code detect passkey cancellation and fallback to password login?

Claude Code detects cancellation by streaming logcat output via get_logcat, asserting the presence of GetCredentialCancellationException when the modal is dismissed, and immediately executing get_ui_hierarchy to confirm that the application UI dynamically reveals traditional email and password input fields.

Why is testing passkeys on physical Android hardware superior to testing on emulators?

Physical hardware testing is superior because production passkeys rely on hardware-backed KeyStore modules and StrongBox Trusted Execution Environments. Virtual emulators often lack Google Play Services or use synthetic software mocks that mask hardware driver timing issues, cryptographic rate limiting, and OEM-specific battery killers.

Which desktop operating systems and AI clients are supported by ScreenSync MCP?

The ScreenSync MCP desktop hub requires Node.js 18 or newer on Windows, macOS, or Linux, communicating over local stdio JSON-RPC. It supports Claude Code, Claude Desktop, Cursor, and VS Code agent mode. Web-based LLM interfaces requiring remote HTTPS webhooks are not supported.

Written by

ScreenSync MCP team

The ScreenSync MCP team at EpsolDev builds the Android app, the desktop MCP hub and the browser extension that let AI agents see and control a real phone. These guides come out of that work.