mirror of
https://github.com/qtmleap/revkit.git
synced 2026-09-25 12:31:51 +02:00
327 lines
12 KiB
Markdown
327 lines
12 KiB
Markdown
# Netflix Android 認証フロー: API 詳細
|
|
|
|
> [auth_flow_android.md](auth_flow_android.md) のフロー概要と合わせて参照。
|
|
|
|
---
|
|
|
|
## 1. Phase 1: リクエスト送信
|
|
|
|
### 1.1 Appboot
|
|
|
|
デバイス登録と `nfvdid` Cookie の発行。
|
|
|
|
**リクエスト:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| URL Path | `{BaseESN}` | デバイス Base ESN (`NFANDROID1-PRV-P-L3-...`) |
|
|
| URL Query | `keyVersion` | デバイス鍵バージョン (`1`) |
|
|
| URL Query | `suspended` | アカウント停止フラグ (`false`) |
|
|
|
|
**レスポンス:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| Header | `Content-Type` | `application/x-msl+json` |
|
|
| Header | `x-netflix-deviceidtoken` | デバイス ID トークン (324 bytes) |
|
|
| Header | `x-netflix.nfstatus` | Netflix ステータスコード (`1_1`) |
|
|
| Cookie | `nfvdid` | デバイス ID Cookie (更新) |
|
|
| Body | — | MSL JSON ペイロード |
|
|
|
|
> `x-netflix-deviceidtoken` は appboot レスポンスで発行されるが、後続の HTTP リクエストで送信される様子は観測されていない。MSL ペイロード内部またはローカル保持と推定される。
|
|
|
|
---
|
|
|
|
### 1.2 getProxyEsn (MSL)
|
|
|
|
新しい PXA ESN をサーバーから取得する。
|
|
|
|
**リクエスト:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| URL | エンドポイント | `prod.ftl` → `/nq/androidui/samurai/~9.0.0/api` |
|
|
| Body (MSL) | `url` | `/getProxyEsn` |
|
|
|
|
**レスポンス (MSL 復号後):**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| Body | `result.esn` | 新しい PXA ESN (`NFANDROID1-PXA-P-L3-...`) |
|
|
| Body | `serverTime` | サーバータイムスタンプ |
|
|
| Body | `from` | レスポンス元 (`playapi`) |
|
|
|
|
> レスポンスは `proxyEsn.response` イベントで取得。アプリは `result.esn` を `proxyEsn.onKnown` でキャッシュに保存する。
|
|
|
|
---
|
|
|
|
### 1.3 aleProvision #1 (MSL)
|
|
|
|
ALE (Application Level Encryption) のための RSA 鍵交換。
|
|
|
|
**リクエスト:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| URL | エンドポイント | `prod.ftl` → `/nq/androidui/samurai/~9.0.0/api` |
|
|
| Body (MSL) | `url` | `/aleProvision` |
|
|
| Body (MSL) | `params.provisionRequest.keyx.scheme` | `RSA-OAEP-256` — 鍵交換アルゴリズム |
|
|
| Body (MSL) | `params.provisionRequest.keyx.data.pubkey` | RSA 公開鍵 (2048-bit, Base64) |
|
|
| Body (MSL) | `params.provisionRequest.scheme` | `A128GCM` — セッション暗号化方式 |
|
|
| Body (MSL) | `params.provisionRequest.type` | `SOCKETROUTER` — プロビジョニング種別 |
|
|
| Body (MSL) | `params.netflixClientPlatform` | `androidNative` |
|
|
| Body (MSL) | `params.appVer` | `63928` |
|
|
| Body (MSL) | `params.mId` | `GOOGLPIXEL=4A==5G=S` — Model ID |
|
|
| Body (MSL) | `params.ffbc` | `phone` — Form Factor |
|
|
|
|
**レスポンス:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| Body | — | MSL 暗号化 (A128GCM セッション鍵を RSA-OAEP-256 で暗号化して返すと推定) |
|
|
|
|
---
|
|
|
|
### 1.4 RenewSSOToken (MSL → GraphQL)
|
|
|
|
SSO トークンの更新。
|
|
|
|
**リクエスト:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| URL | エンドポイント | `prod.cloud` → `/graphql` |
|
|
| Body (MSL) | `operationName` | `RenewSSOToken` |
|
|
| Body (MSL) | `variables.ssoToken` | 既存の SSO トークン (`BgiHtuvcAxL4AYwX...`) |
|
|
| Body (MSL) | `extensions.persistedQuery.id` | `a4d00303-b02d-47c9-a53f-776b6a63b001` |
|
|
| Body (MSL) | `extensions.persistedQuery.version` | `102` |
|
|
|
|
**レスポンス:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| Body | — | MSL 暗号化 (SSO トークン更新結果) |
|
|
|
|
> `ssoToken` はこの API でのみ使用される。他の API で再利用される様子は観測されていない。
|
|
|
|
---
|
|
|
|
### 1.5 CurrentCountryQuery
|
|
|
|
国情報取得と認証 Cookie の発行。Non-MSL GraphQL。
|
|
|
|
**リクエスト:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| URL | エンドポイント | `prod.ftl` → `/graphql` |
|
|
| Cookie | `nfvdid` | デバイス識別子 |
|
|
| Body | — | GraphQL JSON (平文) |
|
|
|
|
**レスポンス:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| Header | `Content-Type` | `application/json;charset=UTF-8` |
|
|
| Cookie | `NetflixId` | ユーザー認証 Cookie (新規発行) |
|
|
| Cookie | `SecureNetflixId` | セキュア認証 Cookie (HTTPS only, 新規発行) |
|
|
| Body | — | 国情報 JSON |
|
|
|
|
> 起動フローで最初に `NetflixId` / `SecureNetflixId` Cookie を発行するレスポンス。
|
|
|
|
---
|
|
|
|
### 1.6 InterstitialForProfileGate / InterstitialForLolomo (MSL → GraphQL)
|
|
|
|
プロフィール選択画面・ホーム画面のインタースティシャル (中間画面) チェック。
|
|
|
|
**リクエスト:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| URL | エンドポイント | `prod.cloud` → `/graphql` |
|
|
| Body (MSL) | `operationName` | `InterstitialForProfileGate` / `InterstitialForLolomo` |
|
|
| Body (MSL) | `variables.format` | `HTML` |
|
|
| Body (MSL) | `variables.resolutionMode` | `ANDROID_XHDPI` |
|
|
| Body (MSL) | `variables.imageFormat` | `PNG` |
|
|
| Body (MSL) | `variables.commonParameters.isConsumptionOnly` | `true` |
|
|
| Body (MSL) | `extensions.persistedQuery.id` | `18f3ae27-a0f1-45c9-88e6-c6bd39159ecb` (ProfileGate) |
|
|
| Body (MSL) | `extensions.persistedQuery.version` | `102` |
|
|
|
|
**レスポンス:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| Body | — | MSL 暗号化 (インタースティシャル表示の要否) |
|
|
|
|
---
|
|
|
|
## 2. Phase 3: 追加リクエスト
|
|
|
|
### 2.1 AccountQuery (MSL → GraphQL)
|
|
|
|
アカウント基本情報の取得。
|
|
|
|
**リクエスト:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| URL | エンドポイント | `prod.cloud` → `/graphql` |
|
|
| Body (MSL) | `operationName` | `AccountQuery` |
|
|
| Body (MSL) | `extensions.persistedQuery.id` | `4043dd89-0ed5-4d7f-ac5c-40c7ffcec7ae` |
|
|
| Body (MSL) | `extensions.persistedQuery.version` | `102` |
|
|
|
|
**レスポンス:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| Body | — | MSL 暗号化 (アカウント情報: プラン、プロフィール等) |
|
|
|
|
---
|
|
|
|
### 2.2 aleProvision #2 (MSL)
|
|
|
|
再鍵交換。#1 と同一の RSA 公開鍵を使用。
|
|
|
|
**リクエスト:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| URL | エンドポイント | `prod.ftl` → `/nq/androidui/samurai/~9.0.0/api` |
|
|
| Body (MSL) | — | aleProvision #1 と同一構造 (同一 RSA pubkey) |
|
|
|
|
**レスポンス:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| Body | — | MSL 暗号化 (新セッション鍵 + PXA ESN) |
|
|
|
|
> このレスポンスの直後に `proxyEsn.response` → `proxyEsn.onKnown` で新 PXA ESN が確定・キャッシュされる。
|
|
|
|
---
|
|
|
|
### 2.3 PromoProfileGateVideoDataQuery
|
|
|
|
プロフィール画面データの取得。Non-MSL GraphQL。
|
|
|
|
**リクエスト:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| URL | エンドポイント | `prod.ftl` → `/graphql` |
|
|
| Cookie | `nfvdid` | デバイス識別子 |
|
|
| Cookie | `NetflixId` | ユーザー認証 Cookie |
|
|
| Cookie | `SecureNetflixId` | セキュア認証 Cookie |
|
|
| Body | — | GraphQL JSON (平文) |
|
|
|
|
**レスポンス:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| Header | `Content-Type` | `application/json;charset=UTF-8` |
|
|
| Cookie | `NetflixId` | ユーザー認証 Cookie (リフレッシュ) |
|
|
| Cookie | `SecureNetflixId` | セキュア認証 Cookie (リフレッシュ) |
|
|
| Body | — | プロフィール画面データ JSON |
|
|
|
|
---
|
|
|
|
## 3. Phase 4: 遅延リクエスト
|
|
|
|
起動から約 30 秒後にバックグラウンドで送信される。
|
|
|
|
### 3.1 FetchConfigData (MSL → Samurai)
|
|
|
|
デバイス・ストリーミング設定の一括取得。
|
|
|
|
**リクエスト:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| URL | エンドポイント | `prod.ftl` → `/nq/androidui/samurai/v1/config` |
|
|
| Body (MSL) | `method` | `get` |
|
|
| Body (MSL) | `path` | `["deviceConfig"]`, `["hendrixConfig"]`, `["networkScoreConfig"]`, `["accountConfig"]` 等 |
|
|
| Body (MSL) | `appType` | `samurai` |
|
|
|
|
**レスポンス:**
|
|
|
|
| 場所 | フィールド | 説明 |
|
|
|---|---|---|
|
|
| Body | — | MSL 暗号化 (設定データ) |
|
|
|
|
---
|
|
|
|
### 3.2 AccountQuery #2 (MSL → GraphQL)
|
|
|
|
Phase 3 の AccountQuery と同一。キャッシュ更新のための再取得。
|
|
|
|
---
|
|
|
|
## 4. GraphQL Persisted Query パターン
|
|
|
|
Netflix Android は GraphQL の **Persisted Query** を使用する。クエリ本文は送信せず、事前登録済みの ID のみを指定する。
|
|
|
|
### 4.1 確認済みクエリ一覧
|
|
|
|
| operationName | persistedQuery ID | version | プロトコル | エンドポイント |
|
|
|---|---|---|---|---|
|
|
| `RenewSSOToken` | `a4d00303-b02d-47c9-a53f-776b6a63b001` | 102 | MSL | prod.cloud |
|
|
| `InterstitialForProfileGate` | `18f3ae27-a0f1-45c9-88e6-c6bd39159ecb` | 102 | MSL | prod.cloud |
|
|
| `InterstitialForLolomo` | *(未取得)* | 102 | MSL | prod.cloud |
|
|
| `AccountQuery` | `4043dd89-0ed5-4d7f-ac5c-40c7ffcec7ae` | 102 | MSL | prod.cloud |
|
|
| `CurrentCountryQuery` | *(未取得)* | — | non-MSL | prod.ftl |
|
|
| `PromoProfileGateVideoDataQuery` | *(未取得)* | — | non-MSL | prod.ftl |
|
|
|
|
### 4.2 MSL GraphQL vs Non-MSL GraphQL
|
|
|
|
| 特性 | MSL GraphQL | Non-MSL GraphQL |
|
|
|---|---|---|
|
|
| エンドポイント | `prod.cloud/graphql` | `prod.ftl/graphql` |
|
|
| ボディ暗号化 | MSL (CBOR → GZIP → JSON) | なし (平文 JSON) |
|
|
| 認証 | MSL Master Token | `NetflixId` / `SecureNetflixId` Cookie |
|
|
| レスポンス Cookie | `nfvdid` のみ | `nfvdid` + `NetflixId` + `SecureNetflixId` |
|
|
|
|
---
|
|
|
|
## 5. 完全タイムライン
|
|
|
|
| # | イベント | エンドポイント | API / 操作 | 備考 |
|
|
|---|---|---|---|---|
|
|
| 1 | `proxyEsn.forceExpired` | — | PXA ESN 期限チェック | Frida が強制失効 |
|
|
| 2 | `http.request` | appboot | `POST /appboot/{BaseESN}` | `keyVersion=1` |
|
|
| 3 | `proxyEsn.requestHeaders` | — | ヘッダー準備 | |
|
|
| 4 | `proxyEsn.request` | — | `/getProxyEsn` | |
|
|
| 5 | `msl.api` | prod.ftl | `/getProxyEsn` | MSL 平文 |
|
|
| 6 | `http.request` | prod.ftl | `samurai/~9.0.0/api` | MSL 暗号文 |
|
|
| 7 | `msl.api` | prod.ftl | `/aleProvision` #1 | RSA-OAEP-256 |
|
|
| 8 | `http.request` | prod.ftl | `samurai/~9.0.0/api` | MSL 暗号文 |
|
|
| 9 | `msl.api` | prod.cloud | `RenewSSOToken` | GraphQL Mutation |
|
|
| 10 | `http.request` | prod.cloud | `/graphql` | MSL 暗号文 |
|
|
| 11 | `http.request` | prod.ftl | `/graphql` | CurrentCountryQuery |
|
|
| 12 | `msl.api` | prod.cloud | `InterstitialForProfileGate` | |
|
|
| 13 | `msl.api` | prod.cloud | `InterstitialForLolomo` | |
|
|
| 14 | `http.request` | prod.cloud | `/graphql` | MSL 暗号文 |
|
|
| 15 | `http.request` | prod.cloud | `/graphql` | MSL 暗号文 |
|
|
| 16 | `http.response` | prod.ftl | CurrentCountryQuery | **+NetflixId [Cookie]** |
|
|
| 17 | `http.response` | prod.ftl | getProxyEsn | |
|
|
| 18 | `http.response` | prod.ftl | aleProvision #1 | |
|
|
| 19 | `msl.api` | prod.cloud | `AccountQuery` | |
|
|
| 20 | `http.request` | prod.cloud | `/graphql` | MSL 暗号文 |
|
|
| 21 | `http.response` | appboot | appboot | **+nfvdid [Cookie]** + DeviceIdToken |
|
|
| 22 | `http.response` | prod.cloud | RenewSSOToken | |
|
|
| 23 | `http.response` | prod.cloud | InterstitialForLolomo | |
|
|
| 24 | `http.response` | prod.cloud | InterstitialForProfileGate | |
|
|
| 25 | `http.response` | prod.cloud | AccountQuery | |
|
|
| 26 | `msl.api` | prod.ftl | `/aleProvision` #2 | 同一 RSA pubkey |
|
|
| 27 | `http.request` | prod.ftl | `samurai/~9.0.0/api` | MSL 暗号文 |
|
|
| 28 | `http.response` | prod.ftl | aleProvision #2 | |
|
|
| 29 | `proxyEsn.response` | — | 新 PXA ESN 受信 | |
|
|
| 30 | `proxyEsn.onKnown` | — | ESN キャッシュ保存 | `sn=8572399748193023` |
|
|
| 31 | `http.request` | prod.ftl | `/graphql` | PromoProfileGate |
|
|
| 32 | `http.response` | prod.ftl | PromoProfileGate | +NetflixId [Cookie] |
|
|
| 33 | `msl.api` | prod.ftl | FetchConfigData | Samurai config |
|
|
| 34 | `msl.api` | prod.cloud | `AccountQuery` #2 | |
|
|
| 35 | `http.request` | prod.ftl | `samurai/v1/config` | MSL 暗号文 |
|
|
| 36 | `http.request` | prod.cloud | `/graphql` | MSL 暗号文 |
|
|
| 37 | `http.response` | prod.cloud | AccountQuery #2 | |
|
|
| 38 | `http.response` | prod.ftl | FetchConfigData | |
|