Skip to main content

ya-passport-auth

Async Yandex Passport (mobile) authentication library for Music Assistant providers.

CI PyPI Python 3.12+ License: MIT

Features

  • Three login methods — QR code scan, OAuth Device Flow (short code on ya.ru/device, plus a refresh_token for silent re-auth), and cookie-based login.
  • Full token-derivation graph — x_token exchange, music_token refresh, Passport cookie refresh (redirect-following), Quasar CSRF, Glagol device token, account info.
  • Security-first — SecretStr redacts tokens in repr/str/format/tracebacks and blocks pickling; host allow-list with HTTPS-only enforcement; CSRF extraction; per-request rate limiting; response size caps; log redaction via RedactingFilter.
  • Async-native — built on aiohttp with asyncio.Lock-protected rate limiter and connection management.
  • Strictly typed — mypy --strict clean, PEP 561 py.typed marker.
  • Well tested — 386 tests, 97 % branch coverage.

Installation

pip install ya-passport-auth

With the Music Assistant credential helpers (token refresh/rotation, shared accounts, and cookie parsing used by the MA Yandex providers):

pip install ya-passport-auth[ma]

Quick start

QR login

from ya_passport_auth import PassportClient

async def qr_login():
    async with PassportClient.create() as client:
        qr = await client.start_qr_login()
        print(f"Scan QR: {qr.qr_url}")
        creds = await client.poll_qr_until_confirmed(qr)

        info = await client.fetch_account_info(creds.x_token)
        print(f"Logged in as {info.display_login} (uid={info.uid})")

Device flow

OAuth 2.0 Device Authorization Grant. The user opens ya.ru/device on any device, enters the short user_code, and the library receives an x_token plus a long-lived refresh_token for silent re-auth later.

from ya_passport_auth import DeviceCodeSession, PassportClient

async def device_login():
    def on_code(session: DeviceCodeSession) -> None:
        print(f"Open {session.verification_url} and enter: {session.user_code}")

    async with PassportClient.create() as client:
        creds = await client.login_device_code(on_code=on_code)
        # creds.refresh_token is populated (only for this flow).
        # Persist creds — the access token is valid for ~1 year.

After the x_token expires, mint a new one without user interaction:

new_creds = await client.refresh_credentials(creds)

Only device-flow credentials carry a refresh_token; QR/cookie-login credentials have refresh_token=None and cannot be silently refreshed.

Device flow for a caller-owned OAuth application

Service providers can use the same polling implementation with their own Yandex OAuth application. The returned tokens are not converted to Passport or Yandex Music credentials:

from ya_passport_auth import OAuthDeviceClient

async def service_login():
    async with OAuthDeviceClient.create(
        client_id="your-client-id",
        client_secret="your-client-secret",
        scope="cloud_api:disk.read",
    ) as client:
        tokens = await client.login_device_code(
            on_code=lambda code: print(code.verification_url, code.user_code)
        )
        # Persist tokens.refresh_token for silent rotation.

Music Assistant providers present the code in their own setup UI and can use the MA helper for silent rotation:

from ya_passport_auth.ma import refresh_oauth_tokens

tokens = await refresh_oauth_tokens(
    client_id=client_id,
    client_secret=client_secret,
    refresh_token=tokens.refresh_token,
    scope="service.scope",
    session=mass.http_session,
)

Cookie login

from ya_passport_auth import PassportClient

async def cookie_login():
    cookies = "Session_id=...; sessionid2=..."  # from browser
    async with PassportClient.create() as client:
        creds = await client.login_cookies(cookies)
        print(f"x_token acquired, music_token ready")

Architecture

PassportClient  (public facade)
├── SafeHttpClient  (host allow-list, HTTPS enforcement, size caps, rate limiting)
│   └── AsyncMinDelayLimiter
└── Flows
    ├── QrLoginFlow        → CSRF scrape → session create → poll → x_token
    ├── DeviceCodeFlow     → device_code → ya.ru/device → x_token + refresh_token
    ├── CookieLoginFlow    → raw cookies → x_token
    ├── _token_exchange    → cookies→x_token, x_token→music_token  (shared)
    ├── PassportSessionRefresher  → x_token → session cookies (follows redirects)
    ├── AccountInfoFetcher → x_token → uid/login/avatar
    ├── QuasarCsrfFetcher  → CSRF token for IoT API
    └── GlagolDeviceTokenFetcher → music_token → Glagol device token

OAuthDeviceClient  (caller-owned Yandex OAuth application)
└── DeviceCodeFlow → service-scoped access_token + refresh_token

API overview

PassportClient

Method Description
start_qr_login() Begin QR login, returns QrSession
poll_qr_until_confirmed(qr) Poll until scanned, returns Credentials
complete_qr_login(qr) Exchange confirmed QR for tokens
start_device_login(...) Begin Device Flow, returns DeviceCodeSession
poll_device_until_confirmed(session, ...) Poll until confirmed, returns Credentials
login_device_code(on_code=..., ...) Full Device Flow with callback
refresh_credentials(creds) Mint fresh Credentials via refresh_token
login_cookies(cookies) Exchange browser cookies for Credentials
refresh_music_token(x_token) x_token → music_token
refresh_passport_cookies(x_token) Refresh session cookies (follows redirect chain)
get_quasar_csrf_token() Quasar CSRF token
get_glagol_device_token(music_token, ...) Glagol device token
fetch_account_info(x_token) Account metadata
validate_x_token(x_token) Check if token is valid

OAuthDeviceClient

Method Description
start_device_login(...) Begin Device Flow for a caller-owned OAuth app
poll_device_until_confirmed(session, ...) Poll and return unchanged OAuthTokens
login_device_code(on_code=..., ...) Run both phases with a UI callback
refresh(refresh_token) Rotate the service OAuth token pair

SecretStr

Opaque wrapper — repr() and str() return ***, pickling raises TypeError. Access plaintext only via get_secret().

Credentials

Frozen, slotted dataclass returned by poll_qr_until_confirmed() and login_cookies():

Field Type
x_token SecretStr
music_token SecretStr | None
uid int | None
display_login str | None
refresh_token SecretStr | None (device flow only)

Exception hierarchy

YaPassportError
├── NetworkError
│   └── UnexpectedHostError
└── AuthFailedError
    ├── InvalidCredentialsError
    ├── CsrfExtractionError
    ├── RateLimitedError
    ├── QRPendingError
    ├── QRTimeoutError
    └── DeviceCodeTimeoutError

Security

  • HTTPS-only — _check_host() rejects any non-https URL, preventing protocol-downgrade attacks via redirect Location headers.
  • Host allow-list — every request is validated against a frozen set of allowed Yandex hosts. Redirect targets are checked at each hop.
  • Token redaction — SecretStr hides values in repr/str/format; RedactingFilter scrubs OAuth headers and hex tokens from log output.
  • No pickling — SecretStr and Credentials block pickle/copy.
  • Response size caps — 1 MiB for JSON, 2 MiB for HTML.
  • See SECURITY.md for the full threat model (T1–T14).

Used by

Security disclaimer

The Passport-specific client interacts with Yandex using public mobile OAuth client IDs and secrets extracted from official Yandex Android applications. These values are well-known and present in many open-source projects; they are treated here as constants, not secrets. OAuthDeviceClient, by contrast, accepts credentials for an application owned by its caller.

There is no official Yandex API for the mobile Passport flow. Endpoints, response shapes, and regex patterns may break without notice.

License

MIT. See LICENSE and NOTICE for third-party attribution.

Metadata

Release files for ya-passport-auth 2.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ya-passport-auth 2.0.0
File Size Uploaded
ya_passport_auth-2.0.0.tar.gz 85.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ya-passport-auth 2.0.0
File Interpreter ABI Platform
ya_passport_auth-2.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 143.3 kB

Release files / ya_passport_auth-2.0.0.tar.gz

Download URL ya_passport_auth-2.0.0.tar.gz
Size 85.3 kB
Tags Source
SHA-256 checksum
How to use checksums
ec1b3ed3d319829f89ff6e0b2c025e3790faaf7e58f387e2ea1cc23529564ffc
BLAKE2b-256 checksum
How to use checksums
5a3d9ce75891951f825175438f55fdb40bd3d6e6a1e32631c1b9d253bcf3a30b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 28, 2026.

Transparency log

Release files / ya_passport_auth-2.0.0-py3-none-any.whl

Download URL ya_passport_auth-2.0.0-py3-none-any.whl
Size 58.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2b9e4e942bb2b286d515b8ab83910d409aad544bc2ae63ee08fbb6e420cd17f1
BLAKE2b-256 checksum
How to use checksums
e04d92004caffcc8bb24de8c11dfaa2e3a69500ebc88137352a31c72bdaaab18
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 28, 2026.

Transparency log

Release history Release notifications | RSS feed

2.0.1

2 release files

This release

2.0.0 This release

2 release files

1.8.0

2 release files

1.7.0

2 release files

1.6.0

2 release files

1.5.0

2 release files

1.4.1

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.3

2 release files

1.2.2

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page