eufy-security-client 4.0.0 → 4.1.0-dev.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +32 -0
  2. package/build/eufysecurity.d.ts +15 -0
  3. package/build/eufysecurity.js +58 -6
  4. package/build/eufysecurity.js.map +1 -1
  5. package/build/http/api.js +1 -1
  6. package/build/http/api.js.map +1 -1
  7. package/build/http/decodeImageV2.d.ts +12 -8
  8. package/build/http/decodeImageV2.js +17 -12
  9. package/build/http/decodeImageV2.js.map +1 -1
  10. package/build/http/index.d.ts +2 -0
  11. package/build/http/index.js +2 -0
  12. package/build/http/index.js.map +1 -1
  13. package/build/http/interfaces.d.ts +2 -0
  14. package/build/http/megaApi.d.ts +186 -0
  15. package/build/http/megaApi.js +513 -0
  16. package/build/http/megaApi.js.map +1 -0
  17. package/build/http/megaCrypto.d.ts +84 -0
  18. package/build/http/megaCrypto.js +129 -0
  19. package/build/http/megaCrypto.js.map +1 -0
  20. package/build/http/megaInterfaces.d.ts +83 -0
  21. package/build/http/megaInterfaces.js +3 -0
  22. package/build/http/megaInterfaces.js.map +1 -0
  23. package/build/http/megaTransition.d.ts +103 -0
  24. package/build/http/megaTransition.js +203 -0
  25. package/build/http/megaTransition.js.map +1 -0
  26. package/build/http/station.d.ts +1 -0
  27. package/build/http/station.js +4 -0
  28. package/build/http/station.js.map +1 -1
  29. package/build/http/types.d.ts +7 -1
  30. package/build/http/types.js +6 -0
  31. package/build/http/types.js.map +1 -1
  32. package/build/http/utils.d.ts +10 -0
  33. package/build/http/utils.js +34 -10
  34. package/build/http/utils.js.map +1 -1
  35. package/build/interfaces.d.ts +2 -0
  36. package/build/p2p/interfaces.d.ts +2 -0
  37. package/build/p2p/session.js +17 -0
  38. package/build/p2p/session.js.map +1 -1
  39. package/build/p2p/types.d.ts +1 -0
  40. package/build/p2p/types.js +1 -0
  41. package/build/p2p/types.js.map +1 -1
  42. package/coverage/clover.xml +15420 -0
  43. package/coverage/coverage-final.json +37 -0
  44. package/coverage/lcov-report/base.css +224 -0
  45. package/coverage/lcov-report/block-navigation.js +87 -0
  46. package/coverage/lcov-report/favicon.png +0 -0
  47. package/coverage/lcov-report/index.html +176 -0
  48. package/coverage/lcov-report/prettify.css +1 -0
  49. package/coverage/lcov-report/prettify.js +2 -0
  50. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  51. package/coverage/lcov-report/sorter.js +210 -0
  52. package/coverage/lcov.info +29542 -0
  53. package/package.json +1 -1
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.megaDecryptBody = exports.megaEncryptBody = exports.sharedKeyToAesKey = exports.sharedKeySigningKey = exports.finalizeKeyExchange = exports.buildKeyExchange = exports.presetDecrypt = exports.presetEncrypt = exports.generateKeyIdent = exports.xSignature = exports.MEGA_PRESET_KEY = void 0;
4
+ const crypto_1 = require("crypto");
5
+ /**
6
+ * Eufy "eufy_mega" v6 transport crypto.
7
+ *
8
+ * Two layers:
9
+ * 1. Bootstrap (handshake): body + signature use a STATIC per-app `presetKey`.
10
+ * 2. Regular requests (post-handshake): body + signature use the per-cluster
11
+ * ECDH `sharedKey` derived from the key/exchange.
12
+ */
13
+ /** Static preset key for the `eufy_security` category (`*.eufy.com`). Extracted from the app's
14
+ * `ESIotAppConfig`, stored in `MegaAppDomain.presetKeyMap` (one key per product category). */
15
+ exports.MEGA_PRESET_KEY = "2500a7d5617812f9d52515b2c8f20a3d";
16
+ /** NIST P-256 (prime256v1) — same curve the lib already uses for login. */
17
+ const CURVE = "prime256v1";
18
+ /**
19
+ * X-Signature = HMAC-SHA256, hex lowercase, over `${ts}+${nonce}+${encryptedBody}`.
20
+ * The HMAC key is the **ASCII string** of the key material (NOT hex-decoded).
21
+ *
22
+ * @param keyAscii presetKey (bootstrap) or sharedKey hex string (regular requests)
23
+ */
24
+ const xSignature = (keyAscii, ts, nonce, encryptedBody) => {
25
+ const parts = encryptedBody !== undefined ? [ts, nonce, encryptedBody] : [ts, nonce];
26
+ return (0, crypto_1.createHmac)("sha256", Buffer.from(keyAscii, "utf8")).update(parts.join("+")).digest("hex");
27
+ };
28
+ exports.xSignature = xSignature;
29
+ /** Random 32-hex client-generated X-Key-Ident (one per cluster identity). */
30
+ const generateKeyIdent = () => (0, crypto_1.randomBytes)(16).toString("hex");
31
+ exports.generateKeyIdent = generateKeyIdent;
32
+ /**
33
+ * Encrypt a payload AES-128-CBC/PKCS7 under the preset key, output `base64(IV ++ ciphertext)`.
34
+ * The AES key is `bytes.fromhex(presetKey)` (16 bytes); a fresh random IV is prepended.
35
+ * Used to wrap the client's EC public key in the key/exchange request body.
36
+ */
37
+ const presetEncrypt = (plaintext, presetKeyHex = exports.MEGA_PRESET_KEY) => {
38
+ const key = Buffer.from(presetKeyHex, "hex");
39
+ const iv = (0, crypto_1.randomBytes)(16);
40
+ const cipher = (0, crypto_1.createCipheriv)("aes-128-cbc", key, iv);
41
+ const ct = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
42
+ return Buffer.concat([iv, ct]).toString("base64");
43
+ };
44
+ exports.presetEncrypt = presetEncrypt;
45
+ /** Inverse of {@link presetEncrypt}: decode `base64(IV ++ ciphertext)` → plaintext. */
46
+ const presetDecrypt = (b64, presetKeyHex = exports.MEGA_PRESET_KEY) => {
47
+ const blob = Buffer.from(b64, "base64");
48
+ const key = Buffer.from(presetKeyHex, "hex");
49
+ const decipher = (0, crypto_1.createDecipheriv)("aes-128-cbc", key, blob.subarray(0, 16));
50
+ return Buffer.concat([decipher.update(blob.subarray(16)), decipher.final()]).toString("utf8");
51
+ };
52
+ exports.presetDecrypt = presetDecrypt;
53
+ /**
54
+ * Build the key/exchange request material.
55
+ *
56
+ * The exchange is ECIES-bootstrapped: the client's ephemeral EC public key is wrapped
57
+ * with the static `presetKey`. The SESSION sharedKey is NOT derivable yet — it is
58
+ * `ECDH(clientPriv, server_public_key)` where server_public_key comes back in the
59
+ * response (see {@link finalizeKeyExchange}). We keep the ECDH object so the caller can
60
+ * finish the derivation once the server replies.
61
+ *
62
+ * @returns the ECDH object (holds the client private key), the wrapped client_public_key
63
+ * body value, the client public key hex, and a fresh client-generated keyIdent.
64
+ */
65
+ const buildKeyExchange = () => {
66
+ const ecdh = (0, crypto_1.createECDH)(CURVE);
67
+ ecdh.generateKeys();
68
+ const clientPubHex = ecdh.getPublicKey("hex");
69
+ const clientPublicKeyBody = (0, exports.presetEncrypt)(clientPubHex);
70
+ return { ecdh, clientPublicKeyBody, clientPublicKey: clientPubHex, keyIdent: (0, exports.generateKeyIdent)() };
71
+ };
72
+ exports.buildKeyExchange = buildKeyExchange;
73
+ /**
74
+ * Finalize the handshake: derive the session sharedKey from our ECDH private key and the
75
+ * server's public key returned (preset-encrypted) in the key/exchange response.
76
+ *
77
+ * @param ecdh the ECDH object from {@link buildKeyExchange} (holds clientPriv)
78
+ * @param serverPublicKeyEnc base64 `server_public_key` from the response (preset-wrapped)
79
+ * @param keyIdent the client keyIdent used for this cluster
80
+ * @param clientPublicKey our public key hex (for reference)
81
+ */
82
+ const finalizeKeyExchange = (ecdh, serverPublicKeyEnc, keyIdent, clientPublicKey) => {
83
+ const serverPubHex = (0, exports.presetDecrypt)(serverPublicKeyEnc);
84
+ if (!/^04[0-9a-f]{128}$/i.test(serverPubHex)) {
85
+ throw new Error("key/exchange: unexpected server public key format");
86
+ }
87
+ let sharedKey;
88
+ try {
89
+ sharedKey = ecdh.computeSecret(Buffer.from(serverPubHex, "hex")).toString("hex");
90
+ }
91
+ catch (err) {
92
+ throw new Error(`key/exchange: ECDH computeSecret failed (${err.message})`);
93
+ }
94
+ return { keyIdent, sharedKey, clientPublicKey };
95
+ };
96
+ exports.finalizeKeyExchange = finalizeKeyExchange;
97
+ /**
98
+ * Per-request key material derived from the handshake sharedKey.
99
+ *
100
+ * The sharedKey is the ECDH X-coordinate as a 64-char hex string. For regular requests:
101
+ * - **HMAC/signature key** = the first 32 hex CHARS of sharedKey, used as an ASCII string.
102
+ * - **AES body key** = `bytes.fromhex(sharedKey[:32])` → 16 bytes (AES-128); iv = key[:16].
103
+ *
104
+ * NOTE: only the first 32 hex chars (16 bytes) of the 64-char sharedKey are used.
105
+ */
106
+ const sharedKeySigningKey = (sharedKeyHex) => sharedKeyHex.slice(0, 32);
107
+ exports.sharedKeySigningKey = sharedKeySigningKey;
108
+ /** AES key buffer for body encryption: bytes.fromhex(sharedKey[:32]) = 16 bytes (AES-128). */
109
+ const sharedKeyToAesKey = (sharedKeyHex) => Buffer.from(sharedKeyHex.slice(0, 32), "hex");
110
+ exports.sharedKeyToAesKey = sharedKeyToAesKey;
111
+ /**
112
+ * Encrypt a regular (post-handshake) request/response body: AES-128-CBC/PKCS7 with a fresh
113
+ * RANDOM IV, output `base64(IV ++ ciphertext)` — same envelope as the key/exchange body.
114
+ */
115
+ const megaEncryptBody = (plaintext, aesKey) => {
116
+ const iv = (0, crypto_1.randomBytes)(16);
117
+ const cipher = (0, crypto_1.createCipheriv)("aes-128-cbc", aesKey, iv);
118
+ const ct = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
119
+ return Buffer.concat([iv, ct]).toString("base64");
120
+ };
121
+ exports.megaEncryptBody = megaEncryptBody;
122
+ /** Inverse of {@link megaEncryptBody}: decode `base64(IV ++ ciphertext)` → plaintext. */
123
+ const megaDecryptBody = (b64, aesKey) => {
124
+ const blob = Buffer.from(b64, "base64");
125
+ const decipher = (0, crypto_1.createDecipheriv)("aes-128-cbc", aesKey, blob.subarray(0, 16));
126
+ return Buffer.concat([decipher.update(blob.subarray(16)), decipher.final()]).toString("utf8");
127
+ };
128
+ exports.megaDecryptBody = megaDecryptBody;
129
+ //# sourceMappingURL=megaCrypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"megaCrypto.js","sourceRoot":"","sources":["../../src/http/megaCrypto.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import { MegaIdentity } from "./megaCrypto";
2
+ export interface MegaResult {
3
+ code: number;
4
+ msg: string;
5
+ data?: unknown;
6
+ trace_id?: string;
7
+ }
8
+ /** Picture-captcha challenge returned by `passport/generate/captcha`. `item` is a base64 image. */
9
+ export interface MegaCaptcha {
10
+ captcha_id: string;
11
+ item: string;
12
+ }
13
+ /** Caller's captcha answer, passed back into the login call. */
14
+ export interface MegaCaptchaAnswer {
15
+ captchaId: string;
16
+ answer: string;
17
+ }
18
+ /** Raw `devicemanage/get_user_mqtt_info` payload (AWS IoT mutual-TLS credentials). */
19
+ export interface MegaUserMqttInfo {
20
+ endpoint_addr: string;
21
+ certificate_pem: string;
22
+ private_key: string;
23
+ aws_root_ca1_pem: string;
24
+ thing_name: string;
25
+ certificate_id: string;
26
+ user_id: string;
27
+ app_name: string;
28
+ }
29
+ /**
30
+ * Everything needed to open the v6 AWS IoT (mutual-TLS) MQTT connection, assembled so a consumer
31
+ * never has to reach into MegaHTTPApi internals. Topics use `PN`/`SN` placeholders to fill per
32
+ * device (`cmd/eufy_security/PN/SN/res`, …) — see SecurityMqttConstant in the v6 app.
33
+ */
34
+ export interface MegaMqttConnectConfig {
35
+ endpoint: string;
36
+ port: number;
37
+ clientId: string;
38
+ thingName: string;
39
+ userId: string;
40
+ certificatePem: string;
41
+ privateKey: string;
42
+ awsRootCaPem: string;
43
+ topics: {
44
+ subCmd: string;
45
+ stateInfo: string;
46
+ pubCmd: string;
47
+ };
48
+ }
49
+ export interface MegaApiOptions {
50
+ /** Region/AB code, e.g. "fr", "us". Drives estimate_domain. */
51
+ ab: string;
52
+ /** os-type — MUST be "android" for the identity to route events via FCM. */
53
+ osType?: "android" | "iOS";
54
+ appName?: string;
55
+ appVersion?: string;
56
+ osVersion?: string;
57
+ phoneModel?: string;
58
+ /** Stable per-install device id. Seed it from the existing persisted openudid so the v6 client
59
+ * presents the same device as the legacy path instead of a fresh id each run. */
60
+ openudid?: string;
61
+ /** Min delay between requests in ms (WAF-friendly). Default 3000. */
62
+ minRequestIntervalMs?: number;
63
+ }
64
+ /**
65
+ * Serializable session for resume-without-relogin (see {@link MegaHTTPApi.exportSession}).
66
+ *
67
+ * Field names intentionally mirror the legacy `EufySecurityPersistentData` / `HTTPApiPersistentData`
68
+ * conventions (`openudid`, `cloud_token`, `cloud_token_expiration`, `login_hash`, `user_id`) so this
69
+ * can slot into the existing persistence layer. `login_hash = md5(user:pass)` lets the consumer
70
+ * invalidate the cached session when credentials change — exactly like HTTPApi does.
71
+ */
72
+ export interface MegaSession {
73
+ ab: string;
74
+ openudid: string;
75
+ login_hash?: string;
76
+ cloud_token?: string;
77
+ cloud_token_expiration?: number;
78
+ user_id?: string;
79
+ domains?: Record<string, string>;
80
+ megaDomain?: string;
81
+ /** Per-cluster ECDH identities (keyIdent + sharedKey + clientPublicKey). */
82
+ identities?: Record<string, MegaIdentity>;
83
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=megaInterfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"megaInterfaces.js","sourceRoot":"","sources":["../../src/http/megaInterfaces.ts"],"names":[],"mappings":""}
@@ -0,0 +1,103 @@
1
+ import { HTTPApi } from "./api";
2
+ import { MegaHTTPApi } from "./megaApi";
3
+ import type { HTTPApiPersistentData, LoginOptions } from "./interfaces";
4
+ import type { EufySecurityConfig, EufySecurityPersistentData } from "../interfaces";
5
+ /**
6
+ * Everything specific to the transitional v6 "eufy_mega" backend lives in this single file so it can
7
+ * be removed in one block once a native v6 data layer (the new library) takes over.
8
+ *
9
+ * {@link MegaTransition} is the connect coordinator: v6-first login, legacy as best-effort
10
+ * afterwards, the app-ready signal fired exactly once at the end. It owns all the v6 state (mega
11
+ * client, pending challenge, serialisation) and talks to {@link EufySecurity} only through the
12
+ * narrow {@link MegaTransitionHost} surface, so neither file leaks the other's internals.
13
+ *
14
+ * For now v6 is used only for login + FCM push registration: a migrated account logs in there and
15
+ * receives events over its push channel, while the data layer keeps using the legacy transport. The
16
+ * data endpoints differ on v6 (signed/encrypted, different paths/bodies) and belong in the new lib,
17
+ * so we deliberately do NOT route legacy endpoints through mega here.
18
+ *
19
+ * Nothing here modifies {@link MegaHTTPApi}: this layer only consumes its public API.
20
+ */
21
+ /** The result of one v6 login attempt. */
22
+ export type MegaLoginResult = "ok" | "tfa_required" | "captcha_required" | "locked" | "failed";
23
+ /** Which backend a submitted 2FA code / captcha must be routed to. */
24
+ export type ChallengeSource = "mega" | "legacy";
25
+ /**
26
+ * The narrow surface {@link MegaTransition} needs from {@link EufySecurity}. It is satisfied with a
27
+ * small closure object (not `this`) so neither side has to expose private members nor import the
28
+ * other — keeping the transition layer self-contained and removable.
29
+ */
30
+ export interface MegaTransitionHost {
31
+ readonly config: EufySecurityConfig;
32
+ readonly persistentData: EufySecurityPersistentData;
33
+ /** The live (legacy) transport, set once by {@link MegaTransition.createTransport}. */
34
+ readonly api: HTTPApi;
35
+ writePersistentData(): void;
36
+ /** Re-emit the 2FA prompt to the consumer (ws / plugin). */
37
+ emitTfaRequest(): void;
38
+ /** Re-emit the captcha prompt to the consumer (ws / plugin). */
39
+ emitCaptchaRequest(id: string, captcha: string): void;
40
+ /** The original upstream `connect()` (login + trust device), unchanged. */
41
+ legacyConnect(options?: LoginOptions): Promise<void>;
42
+ /** Signal the app as connected (refresh + push + mqtt). Fired once at the end of the sequence. */
43
+ onAPIConnect(): Promise<void>;
44
+ onConnectionError(error: Error): void;
45
+ }
46
+ /**
47
+ * Coordinates the v6-first login sequence. The v6 "eufy_mega" backend is the primary login (it
48
+ * carries push and is where the account is heading); the legacy login runs afterwards as
49
+ * best-effort and never blocks. Each backend has its OWN 2FA email + captcha; whichever asks
50
+ * records itself in {@link pendingChallenge} so the code/captcha from the next connect() is routed
51
+ * to the backend that asked for it. The app-ready signal fires ONCE, at the very end, and only if a
52
+ * login succeeded.
53
+ */
54
+ export declare class MegaTransition {
55
+ private readonly host;
56
+ private megaApi?;
57
+ /**
58
+ * Which backend a submitted 2FA code / captcha must be routed to. Set when WE emit the challenge,
59
+ * so the next connect({verifyCode|captcha}) goes to the backend that asked for it — no guessing.
60
+ * `undefined` = no challenge outstanding (start a fresh sequence).
61
+ */
62
+ private pendingChallenge?;
63
+ /** Whether the v6 login succeeded this sequence (gates signalling the app as connected). */
64
+ private megaLoggedIn;
65
+ /** Serialises connect(): concurrent calls await the in-flight one instead of racing the sequence. */
66
+ private connectInProgress?;
67
+ constructor(host: MegaTransitionHost);
68
+ /** Record that the LEGACY login asked for a code/captcha (called from the host's api-event hooks). */
69
+ recordLegacyChallenge(): void;
70
+ /**
71
+ * Build the live transport. Today this is just the upstream legacy {@link HTTPApi}; the v6 mega
72
+ * client is created lazily on demand (login / push) via {@link getMegaApi}. Kept as a single
73
+ * factory so the transport can be swapped here if v6 ever needs to drive data requests too.
74
+ */
75
+ createTransport(persistentHttpApi: HTTPApiPersistentData | undefined): Promise<HTTPApi>;
76
+ /**
77
+ * Lazily create (and restore) the v6 mega client. The persisted session (token ~30 days) is
78
+ * reused so normal startups need no extra login/2FA; it is dropped if the credentials changed.
79
+ */
80
+ getMegaApi(): Promise<MegaHTTPApi>;
81
+ /**
82
+ * Register the FCM token on the v6 backend, best-effort. No-ops with a log when there is no valid
83
+ * v6 session yet (not-yet-migrated account); a v6 failure is swallowed so legacy push is unaffected.
84
+ */
85
+ registerMegaPushToken(token: string): Promise<boolean>;
86
+ /**
87
+ * Authenticate against the v6 backend.
88
+ * 1. first call -> on `26052` triggers the email code and returns "tfa_required"; on a captcha
89
+ * challenge it emits "captcha request" and returns "captcha_required".
90
+ * 2. with a code/captcha -> completes login; the session is persisted (token ~30 days) so later
91
+ * startups reuse it with no relogin/2FA.
92
+ *
93
+ * Backend-enforced lockout (too many incorrect / max login limit) is surfaced as "locked" so the
94
+ * caller stops retrying instead of deepening the lockout.
95
+ */
96
+ loginMega(verifyCode?: string, captcha?: {
97
+ captchaId: string;
98
+ answer: string;
99
+ }): Promise<MegaLoginResult>;
100
+ /** Serialised connect(): concurrent callers await the in-flight run instead of racing it. */
101
+ connect(options?: LoginOptions): Promise<void>;
102
+ private runConnect;
103
+ }
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MegaTransition = void 0;
4
+ const api_1 = require("./api");
5
+ const megaApi_1 = require("./megaApi");
6
+ const logging_1 = require("../logging");
7
+ const types_1 = require("./types");
8
+ const error_1 = require("../error");
9
+ const utils_1 = require("../utils");
10
+ /**
11
+ * Coordinates the v6-first login sequence. The v6 "eufy_mega" backend is the primary login (it
12
+ * carries push and is where the account is heading); the legacy login runs afterwards as
13
+ * best-effort and never blocks. Each backend has its OWN 2FA email + captcha; whichever asks
14
+ * records itself in {@link pendingChallenge} so the code/captcha from the next connect() is routed
15
+ * to the backend that asked for it. The app-ready signal fires ONCE, at the very end, and only if a
16
+ * login succeeded.
17
+ */
18
+ class MegaTransition {
19
+ host;
20
+ megaApi;
21
+ /**
22
+ * Which backend a submitted 2FA code / captcha must be routed to. Set when WE emit the challenge,
23
+ * so the next connect({verifyCode|captcha}) goes to the backend that asked for it — no guessing.
24
+ * `undefined` = no challenge outstanding (start a fresh sequence).
25
+ */
26
+ pendingChallenge;
27
+ /** Whether the v6 login succeeded this sequence (gates signalling the app as connected). */
28
+ megaLoggedIn = false;
29
+ /** Serialises connect(): concurrent calls await the in-flight one instead of racing the sequence. */
30
+ connectInProgress;
31
+ constructor(host) {
32
+ this.host = host;
33
+ }
34
+ /** Record that the LEGACY login asked for a code/captcha (called from the host's api-event hooks). */
35
+ recordLegacyChallenge() {
36
+ this.pendingChallenge = "legacy";
37
+ }
38
+ /**
39
+ * Build the live transport. Today this is just the upstream legacy {@link HTTPApi}; the v6 mega
40
+ * client is created lazily on demand (login / push) via {@link getMegaApi}. Kept as a single
41
+ * factory so the transport can be swapped here if v6 ever needs to drive data requests too.
42
+ */
43
+ async createTransport(persistentHttpApi) {
44
+ return api_1.HTTPApi.initialize(this.host.config.country, this.host.config.username, this.host.config.password, persistentHttpApi);
45
+ }
46
+ /**
47
+ * Lazily create (and restore) the v6 mega client. The persisted session (token ~30 days) is
48
+ * reused so normal startups need no extra login/2FA; it is dropped if the credentials changed.
49
+ */
50
+ async getMegaApi() {
51
+ if (!this.megaApi) {
52
+ this.megaApi = new megaApi_1.MegaHTTPApi({
53
+ ab: this.host.config.country ?? "US",
54
+ osType: "android",
55
+ phoneModel: this.host.config.trustedDeviceName,
56
+ openudid: this.host.persistentData.openudid || undefined,
57
+ });
58
+ await this.megaApi.init();
59
+ const saved = this.host.persistentData.megaApi;
60
+ if (saved) {
61
+ const currentHash = (0, megaApi_1.megaLoginHash)(this.host.config.username, this.host.config.password, this.host.persistentData.openudid);
62
+ if (saved.login_hash && saved.login_hash !== currentHash) {
63
+ logging_1.rootMainLogger.debug("v6: credentials changed since last login, ignoring stored mega session");
64
+ }
65
+ else {
66
+ this.megaApi.restoreSession(saved);
67
+ }
68
+ }
69
+ }
70
+ return this.megaApi;
71
+ }
72
+ /**
73
+ * Register the FCM token on the v6 backend, best-effort. No-ops with a log when there is no valid
74
+ * v6 session yet (not-yet-migrated account); a v6 failure is swallowed so legacy push is unaffected.
75
+ */
76
+ async registerMegaPushToken(token) {
77
+ try {
78
+ const mega = await this.getMegaApi();
79
+ if (!mega.hasValidSession()) {
80
+ logging_1.rootMainLogger.debug("v6 push: no valid mega session yet, skipping register (legacy still active)");
81
+ return false;
82
+ }
83
+ const result = await mega.registerPushToken(token);
84
+ if (result.code === 0) {
85
+ logging_1.rootMainLogger.info("v6 push: FCM token registered on the eufy_mega backend");
86
+ return true;
87
+ }
88
+ logging_1.rootMainLogger.warn("v6 push: register_push_token returned a non-zero code", {
89
+ code: result.code,
90
+ msg: result.msg,
91
+ });
92
+ return false;
93
+ }
94
+ catch (err) {
95
+ logging_1.rootMainLogger.warn("v6 push: register failed (legacy push unaffected)", { error: (0, utils_1.getError)((0, error_1.ensureError)(err)) });
96
+ return false;
97
+ }
98
+ }
99
+ /**
100
+ * Authenticate against the v6 backend.
101
+ * 1. first call -> on `26052` triggers the email code and returns "tfa_required"; on a captcha
102
+ * challenge it emits "captcha request" and returns "captcha_required".
103
+ * 2. with a code/captcha -> completes login; the session is persisted (token ~30 days) so later
104
+ * startups reuse it with no relogin/2FA.
105
+ *
106
+ * Backend-enforced lockout (too many incorrect / max login limit) is surfaced as "locked" so the
107
+ * caller stops retrying instead of deepening the lockout.
108
+ */
109
+ async loginMega(verifyCode, captcha) {
110
+ try {
111
+ const mega = await this.getMegaApi();
112
+ if (mega.hasValidSession() && !verifyCode && !captcha)
113
+ return "ok";
114
+ await mega.estimateDomain();
115
+ await mega.keyExchange(mega.clusterHost("openapi"));
116
+ const result = await mega.login(this.host.config.username, this.host.config.password, verifyCode, captcha);
117
+ if (result.code === types_1.ResponseErrorCode.CODE_NEED_VERIFY_CODE) {
118
+ await mega.sendVerifyCode();
119
+ this.pendingChallenge = "mega";
120
+ this.host.emitTfaRequest();
121
+ logging_1.rootMainLogger.info("v6 login: email 2FA required — call loginMega(code) with the received code");
122
+ return "tfa_required";
123
+ }
124
+ if (result.code === types_1.ResponseErrorCode.LOGIN_NEED_CAPTCHA ||
125
+ result.code === types_1.ResponseErrorCode.LOGIN_CAPTCHA_ERROR) {
126
+ const c = await mega.generateCaptcha();
127
+ this.pendingChallenge = "mega";
128
+ this.host.emitCaptchaRequest(c.captcha_id, c.item);
129
+ logging_1.rootMainLogger.info("v6 login: captcha required — call loginMega(undefined, {captchaId, answer})");
130
+ return "captcha_required";
131
+ }
132
+ if (result.code === types_1.ResponseErrorCode.CODE_PASSWORD_TOO_MANY_INCORRECT ||
133
+ result.code === types_1.ResponseErrorCode.CODE_PASSWORD_WRONG_FIVE_TIMES ||
134
+ result.code === types_1.ResponseErrorCode.CODE_MAX_LOGIN_LIMIT) {
135
+ logging_1.rootMainLogger.warn("v6 login temporarily locked by the backend — stop retrying", {
136
+ code: result.code,
137
+ msg: result.msg,
138
+ });
139
+ return "locked";
140
+ }
141
+ if (result.code !== 0) {
142
+ logging_1.rootMainLogger.warn("v6 login failed", { code: result.code, msg: result.msg });
143
+ return "failed";
144
+ }
145
+ this.host.persistentData.megaApi = mega.exportSession((0, megaApi_1.megaLoginHash)(this.host.config.username, this.host.config.password, this.host.persistentData.openudid));
146
+ this.host.writePersistentData();
147
+ logging_1.rootMainLogger.info("v6 login: success, mega session persisted");
148
+ return "ok";
149
+ }
150
+ catch (err) {
151
+ logging_1.rootMainLogger.error("v6 login error", { error: (0, utils_1.getError)((0, error_1.ensureError)(err)) });
152
+ return "failed";
153
+ }
154
+ }
155
+ /** Serialised connect(): concurrent callers await the in-flight run instead of racing it. */
156
+ connect(options) {
157
+ if (this.connectInProgress)
158
+ return this.connectInProgress;
159
+ this.connectInProgress = this.runConnect(options).finally(() => {
160
+ this.connectInProgress = undefined;
161
+ });
162
+ return this.connectInProgress;
163
+ }
164
+ async runConnect(options) {
165
+ const megaCaptcha = options?.captcha
166
+ ? { captchaId: options.captcha.captchaId, answer: options.captcha.captchaCode }
167
+ : undefined;
168
+ // PHASE 1 — v6 first. Run it unless a challenge is currently outstanding for the LEGACY side.
169
+ if (this.pendingChallenge !== "legacy") {
170
+ const megaResult = await this.loginMega(options?.verifyCode, megaCaptcha);
171
+ if (megaResult === "tfa_required" || megaResult === "captcha_required") {
172
+ // loginMega already recorded pendingChallenge="mega" and prompted the consumer.
173
+ return;
174
+ }
175
+ this.megaLoggedIn = megaResult === "ok";
176
+ this.pendingChallenge = undefined;
177
+ }
178
+ // PHASE 2 — legacy afterwards, best-effort. A code/captcha just used by mega is not valid here;
179
+ // the legacy login emits its OWN tfa/captcha event (which records pendingChallenge="legacy" via
180
+ // the host) and we wait for the next connect(). If legacy has been decommissioned, its login
181
+ // simply fails and we carry on with v6 only.
182
+ if (!this.host.api.isConnected()) {
183
+ const legacyOptions = this.pendingChallenge === "legacy"
184
+ ? options
185
+ : { ...options, verifyCode: undefined, captcha: undefined };
186
+ this.pendingChallenge = undefined;
187
+ await this.host.legacyConnect(legacyOptions);
188
+ // legacyConnect may have recorded pendingChallenge="legacy" via the host's api-event hooks.
189
+ if (this.pendingChallenge === "legacy" && !this.host.api.isConnected())
190
+ return;
191
+ }
192
+ // PHASE 3 — both backends settled. Signal the app ONCE, only if a login actually succeeded.
193
+ if (this.megaLoggedIn || this.host.api.isConnected()) {
194
+ await this.host.onAPIConnect();
195
+ }
196
+ else {
197
+ logging_1.rootMainLogger.warn("connect: neither v6 nor legacy login succeeded — not signalling connected");
198
+ this.host.onConnectionError(new Error("Login failed on both backends"));
199
+ }
200
+ }
201
+ }
202
+ exports.MegaTransition = MegaTransition;
203
+ //# sourceMappingURL=megaTransition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"megaTransition.js","sourceRoot":"","sources":["../../src/http/megaTransition.ts"],"names":[],"mappings":"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"}
@@ -383,6 +383,7 @@ export declare class Station extends TypedEmitter<StationEvents> {
383
383
  getLockStatus(): void;
384
384
  private onSequenceError;
385
385
  private onHubNotifyUpdate;
386
+ private onPushNotification;
386
387
  updateUsername(device: Device, username: string, passwordId: string): void;
387
388
  setOpenMethod(device: Device, value: number): void;
388
389
  setMotionActivatedPrompt(device: Device, value: boolean): void;
@@ -81,6 +81,7 @@ class Station extends tiny_typed_emitter_1.TypedEmitter {
81
81
  this.p2pSession.on("storage info hb3", (channel, storageInfo) => this.onStorageInfoHB3(channel, storageInfo));
82
82
  this.p2pSession.on("sequence error", (channel, command, sequence, serialnumber) => this.onSequenceError(channel, command, sequence, serialnumber));
83
83
  this.p2pSession.on("hub notify update", () => this.onHubNotifyUpdate());
84
+ this.p2pSession.on("push notification", (message) => this.onPushNotification(message));
84
85
  }
85
86
  initializeState() {
86
87
  this.update(this.rawStation);
@@ -15609,6 +15610,9 @@ class Station extends tiny_typed_emitter_1.TypedEmitter {
15609
15610
  onHubNotifyUpdate() {
15610
15611
  this.emit("hub notify update", this);
15611
15612
  }
15613
+ onPushNotification(message) {
15614
+ this.emit("push notification", this, message);
15615
+ }
15612
15616
  updateUsername(device, username, passwordId) {
15613
15617
  const commandData = {
15614
15618
  name: types_1.CommandName.DeviceUpdateUsername,