Connects MDBList through its OAuth device-code grant, registered as a Device Code app so no client secret or redirect URI ships in the binary and TV, mobile and desktop all use the same flow. MDBList omits `verification_uri_complete`, but its device page seeds the code field from a `user_code` query parameter and the sign-in redirect preserves the query string, so the activation link is built locally and the dialog's open button lands on a filled-in form instead of an empty one. A server-supplied complete URL still wins if one ever appears. Poll state is read from the response body rather than the status code: `authorization_pending` and `slow_down` both arrive as HTTP 400, and a missing grant answers 404 `device_not_found`. Writes go out as real-time `/scrobble/*` reports plus `/sync/watched` for the marks that never pass through the player, with ratings on `/sync/ratings`. Matching uses IMDb and TMDb only — MDBList's id block has no `tvdb` field, so a TVDB-only item is skipped rather than written under an empty id block.
78 lines
3.5 KiB
Dart
78 lines
3.5 KiB
Dart
/// Preference keys whose stored values are credentials.
|
|
///
|
|
/// `shared_preferences` is the most credential-dense artifact in a Plezy
|
|
/// installation. On the desktop platforms it is a single plaintext JSON file
|
|
/// next to the database, and it holds:
|
|
///
|
|
/// * [credentialVaultKeyPref] — the AES-256 key that `CredentialVault` uses to
|
|
/// protect every server/profile token stored in the Drift
|
|
/// `connections.config_json` and `profile_connections.user_token` columns.
|
|
/// Losing it orphans every one of those ciphertexts permanently.
|
|
/// * tracker sessions — `TrackerAccountStore` persists `TrackerSession.encode()`
|
|
/// verbatim, so raw OAuth `access_token`/`refresh_token` pairs for MAL,
|
|
/// AniList, Simkl, Trakt and MDBList live here in plaintext.
|
|
/// * Seerr sessions — `SeerrSessionStore` persists a raw `connect.sid` cookie
|
|
/// alongside a vault-protected password.
|
|
/// * [legacyPlexTokenPref] — the pre-connection-registry Plex token slot. It is
|
|
/// drained by the connection migration but can linger on old installs.
|
|
///
|
|
/// Two subsystems consult this list, both added for #1732:
|
|
///
|
|
/// * the tolerant preference reads in `BaseSharedPreferencesService` must never
|
|
/// silently drop one of these keys — an unreadable credential has to surface
|
|
/// as an explicit repair prompt, not as a silent re-authentication;
|
|
/// * the corrupt-store repair in `PrefsRecovery` salvages exactly these keys
|
|
/// out of a damaged store before quarantining it.
|
|
///
|
|
/// Keep this list exhaustive. A credential slot that is missing here is
|
|
/// silently dropped on a type mismatch and silently lost on a repair.
|
|
///
|
|
/// This lives apart from `CredentialVault`, `TrackerAccountStore` and
|
|
/// `SeerrSessionStore` so `BaseSharedPreferencesService` can depend on it
|
|
/// without an import cycle.
|
|
library;
|
|
|
|
/// Key holding the base64 `CredentialVault` AES-256 key.
|
|
const String credentialVaultKeyPref = 'credential_vault_key_v1';
|
|
|
|
/// Legacy single-slot Plex token, superseded by the connection registry.
|
|
const String legacyPlexTokenPref = 'plex_token';
|
|
|
|
/// Unscoped base keys used by `TrackerAccountStore`, one per tracker service.
|
|
const List<String> trackerSessionBaseKeys = <String>[
|
|
'mal_session',
|
|
'anilist_session',
|
|
'simkl_session',
|
|
'trakt_session',
|
|
'mdblist_session',
|
|
];
|
|
|
|
/// Unscoped base key used by `SeerrSessionStore`.
|
|
const String seerrSessionBaseKey = 'seerr_session';
|
|
|
|
/// Every credential slot that is profile-scoped through `profileScopedPrefsKey`,
|
|
/// so a stored key is either the bare base key or `user_{scope}_{baseKey}`.
|
|
const List<String> profileScopedCredentialBaseKeys = <String>[...trackerSessionBaseKeys, seerrSessionBaseKey];
|
|
|
|
final RegExp _profileScopedCredentialPattern = RegExp(
|
|
'^(?:user_.+_)?(?:${profileScopedCredentialBaseKeys.join('|')})\$',
|
|
);
|
|
|
|
/// The unscoped base key [key] resolves to, or null when [key] is not a
|
|
/// profile-scoped credential slot.
|
|
String? profileScopedCredentialBaseKey(String key) {
|
|
if (!_profileScopedCredentialPattern.hasMatch(key)) return null;
|
|
for (final base in profileScopedCredentialBaseKeys) {
|
|
if (key == base || key.endsWith('_$base')) return base;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// Whether [key] is a profile-scoped or global Seerr session slot.
|
|
bool isSeerrSessionPrefKey(String key) => profileScopedCredentialBaseKey(key) == seerrSessionBaseKey;
|
|
|
|
/// Whether [key] holds a credential and must never be dropped or exported
|
|
/// without an explicit, informed user decision.
|
|
bool isSensitivePrefKey(String key) =>
|
|
key == credentialVaultKeyPref || key == legacyPlexTokenPref || profileScopedCredentialBaseKey(key) != null;
|