aeat.adapters.outbound.aeat.auth._clave_movil module

Cl@ve Móvil auth provider for AEAT Sede Electrónica.

Implements the AuthProvider protocol for the human-in-the-loop Cl@ve Móvil flow against the live portal. Captures the URL template, form selectors, and polling endpoints needed to drive the AEAT QR page to a successful login handshake.

The provider returns AeatSession and AeatLoginAssertion records with Cl@ve-specific detail payloads. Fresh logins persist ClaveMovilSessionMetadata next to encrypted Playwright storage state; resume and diagnostic probes rebuild ClaveMovilSessionDetail from that metadata without carrying credential material in the session record.

Design summary:

  • Cl@ve Móvil is a human-in-the-loop flow. The provider observes only AEAT browser state; it cannot know whether the Cl@ve app displayed, accepted, rejected, or failed to receive a request unless the operator reports that separately.

  • The provider opens a headed Playwright window on fresh login so the operator can scan the QR visually. Resume-from-storage-state runs headlessly because no human interaction is required.

  • The persisted session stores Playwright state and provider metadata together in the encrypted session object. Kind-namespaced logical storage keys keep the Cl@ve and certificate sessions separate. Each persisted object is tagged with SensitivityClass SESSION so the storage substrate applies the correct at-rest treatment.

See also

ClaveMovilSessionMetadata for the encrypted persistence contract, ClaveMovilSessionDetail for the public session payload, and ClaveMovilApprovalTimeoutError for operator-reportable live-flow failures.

class ClaveMovilAuthProvider(settings, *, browser_session_factory=None, navigation_timeout_ms=30000)[source]

Bases: _ClaveMovilPageFlowMixin

Cl@ve Móvil implementation of the AuthProvider protocol.

Constructed by adapters.outbound.aeat.auth.select_provider() when kind == AuthProviderKind.CLAVE_MOVIL. Fresh login can run the configured non-QR confirmation flow or the alternate QR flow; resume runs headlessly because the stored cookies are sufficient.

The provider owns the lifecycle contract; _ClaveMovilPageFlowMixin supplies page-driving helpers and this class turns those browser outcomes into AeatSession, AeatLoginAssertion, and AuthProviderDescription records.

Parameters:
kind: AuthProviderKind
async authenticate(*, browser_session=None, target_url=None)[source]

Run the Cl@ve Móvil login flow and return an AeatSession.

Attempts to resume a cached session first. Falls back to the human-in-the-loop QR-scan flow (or the non-QR DNI/NIE + contraste fallback, when AEAT_CLAVE_PREFER_NON_QR=true). Fresh success writes ClaveMovilSessionMetadata and returns a session whose provider detail is ClaveMovilSessionDetail.

Return type:

AeatSession

Parameters:
async probe_persisted_session(*, browser_session=None, target_url=None)[source]

Probe the encrypted persisted session without side effects.

Unlike authenticate(), this method NEVER falls back to a fresh login and NEVER deletes the persisted session, even when the probe fails. Callers can therefore use it as a pure diagnostic without accidentally triggering a fresh operator-mediated Cl@ve request. A successful probe refreshes the stored idle deadline using the returned AeatLoginAssertion timestamp.

Return type:

tuple[AeatSession, AeatLoginAssertion]

Returns:

A 2-tuple of (AeatSession, AeatLoginAssertion).

Parameters:
async verify(session, *, target_url=None)[source]

Re-probe that session’s cookies still unlock a Sede page.

Explicit target_url probes go through AEAT’s selector dispatcher, because some Cl@ve-backed apps only establish target-local state when dispatched from the selector. When no explicit target is supplied and the session metadata has a concrete post-auth landing URL, the probe can navigate there directly.

The returned AeatLoginAssertion carries ClaveMovilLoginAssertionDetail, including the observed landing URL and whether the probe reached an authenticated AEAT page.

Return type:

AeatLoginAssertion

Returns:

An AeatLoginAssertion describing the probe outcome.

Parameters:
describe()[source]

Return an AuthProviderDescription for the Cl@ve Móvil provider state.

Severity is set explicitly here so a normal pending state — Cl@ve Móvil completion requires an operator-mediated tap on the phone — is paired with info, never the loudest error token (round-5 M5). A malformed identity is a real configuration fault and surfaces as warning; the no-identity case is an undeclared state and surfaces as info. Identity validation is delegated to classify_identity(), which raises ClaveMovilConfigurationError for unsupported DNI/NIE shapes.

Return type:

AuthProviderDescription

async close()[source]

Tear down any retained BrowserContextLike and browser session.

Return type:

None