2026-05-29 17:53:25 +08:00
2026-05-29 17:53:25 +08:00
2026-05-29 17:53:25 +08:00
2026-05-29 17:53:25 +08:00
2026-05-29 17:53:25 +08:00
2026-05-29 17:53:25 +08:00

Netflix MSL Universal Handshake

A cross-platform MSL (Message Security Layer) authentication toolkit for Netflix.
Implements a unified handshake layer that works across Android, iOS, Smart TV, Web, and MGK — all driven from a single entry point with shared credential management.


Table of Contents


Requirements

Python >= 3.9
requests
pycryptodome  (Cryptodome)
pywidevine
jsonpickle

Install all dependencies:

pip install requests pycryptodome pywidevine jsonpickle

Project Structure

.
├── main.py                  # Entry point — platform router
├── config.ini               # Credentials (EMAIL / PASSWORD)
├── devices/
│   ├── l3.wvd   # L3 Android/iOS WVD
│   └── l1.wvd          # L1 TV WVD
│   └── KpeKph          MGK KpeKph platform: base64 encryption + HMAC keys (comma-separated)
│   └── ESNID           # MGK platform: model-group identity string
├── modules/
│   ├── __init__.py
│   ├── config.py            # config.ini loader
│   ├── msl_android.py       # MSL_ANDROID class
│   ├── msl_ios.py           # MSL_IOS class
│   ├── msl_tv.py            # MSL_TV class
│   ├── msl_web.py           # MSL_WEB class
│   └── msl_mgk.py           # MSL_MGK class (Model Group Key)                   

Configuration

Edit config.ini before running anything:

[NETFLIX]
EMAIL    = your@email.com
PASSWORD = yourpassword

Credentials are read once at startup and passed internally to every platform function. They are never accepted as command-line arguments.


Devices (WVD Files)

Two Widevine Device (.wvd) files are included in the devices/ folder:

File Security Level Used by
l3.wvd L3 Android, iOS
l1.wvd L1 TV, TV OTP

The correct WVD is selected automatically for each platform. The --wvd flag lets you override with a custom device file if needed.


Platforms

Android

Emulates a Samsung Galaxy Z Flip3 (SM-F711N) running Android 15.

MSL flow:

  1. Bootstrap HTTP session → obtain nfvdid cookie via appboot
  2. Widevine key exchange (MSL handshake) using L3 WVD
  3. VerifyLoginMslRequest — submits email + password via MSL
  4. Decrypts the response header to extract useridtoken

Output: netflix_auth_tokens.json, netflix_auth_useridtoken.json, netflix_auth_cookies.json


iOS

Emulates an iPhone 15 Pro Max running iOS 18.

MSL flow:

  1. Bootstrap HTTP session → obtain nfvdid via appboot
  2. Widevine key exchange using L3 WVD
  3. MembershipStatus GraphQL probe (anonymous)
  4. CLCSScreenUpdate — submits email + password via MSL to ios.prod.cloud.netflix.com/graphql
  5. Decrypts the response header to extract useridtoken

Output: netflix_auth_cookies.json


TV (email/password)

Emulates an NVIDIA SHIELD Android TV (2019).

MSL flow:

  1. Obtain nfvdid from the Android TV config endpoint
  2. Bootstrap AUI + pre-login pathEvaluator
  3. Widevine key exchange using L1 WVD → mintCookies
  4. CLCS session initiation (clcsLegacyMoneyballInitiateSession)
  5. Multi-step sign-in flow:
    • Navigate welcome landing → web sign-in → email → password path
    • Submit credentials via clcsScreenUpdate
  6. Post-login PBO config + token refresh (getPartnerToken, ping)
  7. Save cookies including NetflixId, SecureNetflixId, gsid

Output: netflix_cookies.json, useridtoken.json, msl_debug_trace.json, password_login_response.json


TV OTP (pairing code)

Same device profile as TV, but authenticates via a one-time pairing code instead of email/password.

MSL flow: 13. Same as TV up through mintCookies 4. CLCS session initiation 5. Navigate to webSignIn mode → extract an 8-digit TV code 6. Display the code and poll https://www.netflix.com/tv2 until the user activates it from a browser 7. Send continueAction to complete sign-in

Interactive: you must visit https://www.netflix.com/tv2 in a browser and enter the displayed code to proceed.

Output: netflix_cookies.json, useridtoken.json


Web

Emulates Chrome 146 on Windows 10.

MSL flow:

  1. Bootstrap anonymous browser session (netflix.comnetflix.com/login)
  2. MembershipStatus GraphQL probe
  3. Extract clcsSessionId and renditionId from the login page HTML
  4. CLCSScreenUpdate — submit email + password directly to PASSWORD_LOGIN screen
  5. Optional CLCSSendFeedback if the response contains a feedback payload
  6. Open /browse to finalize the authenticated session
  7. CLCSInterstitialProfileGate probe
  8. ALE provision via MSL (aleProvision handshake)

Output: netflix_auth_cookies.json


MGK (Model Group Key)

Uses the MGK / AUTHENTICATED_DH MSL key-exchange scheme, authenticating with a pre-provisioned KpeKph key pair rather than a Widevine device.

Requires two sidecar files:

File Content
KpeKph Base64 AES-128 encryption key + Base64 HMAC-SHA256 key, comma-separated
ESNID Model-group identity string (the MGK sender ESN)

These files are discovered automatically in the working directory, any subdirectory, or via environment variables:

export MSL_KPEKPH_PATH=/path/to/KpeKph
export MSL_ESNID_PATH=/path/to/ESNID

Or pass --kpekph on the command line.

MSL flow:

  1. Load KpeKph → derive wrapping key
  2. Generate a DH keypair; build AUTHENTICATED_DH key-request with mechanism=MGK
  3. Perform handshake → derive session encryption + HMAC keys from shared secret
  4. Send EMAIL_PASSWORD user-auth message → receive useridtoken

Output: useridtoken_mgk.json, netflix_auth_cookies_mgk.json


Usage

# Android
python main.py --platform android

# iOS
python main.py --platform ios

# TV (email/password)
python main.py --platform tv

# TV OTP (pairing code — interactive)
python main.py --platform tv_otp

# Web (Chrome emulation)
python main.py --platform web

# MGK (Model Group Key)
python main.py --platform mgk

# MGK with explicit KpeKph path
python main.py --platform mgk --kpekph /path/to/KpeKph

# Override WVD device for Android/iOS/TV/TV-OTP
python main.py --platform android --wvd /path/to/device.wvd

# Force a fresh MSL key exchange (ignore cached keys)
python main.py --platform tv --new-msl

# Disable TLS verification (not recommended)
python main.py --platform web --no-verify

# Pass a reCAPTCHA token for web login
python main.py --platform web --recaptcha-token <token>

All flags

Flag Applies to Description
--platform / -p all Required. android, ios, tv, tv_otp, web, mgk
--wvd android, ios, tv, tv_otp Path to .wvd Widevine device file (optional override)
--kpekph mgk Path to KpeKph key file
--new-msl all Force a fresh MSL handshake, ignoring any cached keys
--no-verify all Disable TLS certificate verification
--recaptcha-token web reCAPTCHA v2 response token

Output Files

Each platform writes its results to the same directory as main.py:

File Platform(s) Contents
netflix_auth_cookies.json android, ios, web Session cookies (NetflixId, SecureNetflixId, etc.)
netflix_auth_tokens.json android Full MSL header data including useridtoken
netflix_auth_useridtoken.json android Extracted useridtoken only
netflix_cookies.json tv, tv_otp Filtered important cookies
useridtoken.json tv, tv_otp MSL useridtoken
msl_debug_trace.json tv Decrypted MSL payload trace log
password_login_response.json tv Raw CLCS credential-submit response
useridtoken_mgk.json mgk MSL useridtoken from MGK flow
netflix_auth_cookies_mgk.json mgk Session cookies from MGK flow
msl_keys_cache_android.json android Cached MSL session keys
msl_keys_cache_ios.json ios Cached MSL session keys
msl_keys_cache.json tv, tv_otp Cached MSL session keys
msl_keys_cache_web.json web Cached MSL session keys
msl_keys_cache_mgk.json mgk Cached MSL session keys

MSL key caches are reused across runs to avoid a full handshake every time. They expire automatically when the master token has fewer than 10 hours remaining.


Credits: Hugoved Big thanks to Hugov for the foundational work on MSL (Message Security Layer) reverse engineering, and the original pywidevine implementation that made this unified handshake toolkit possible. This project builds upon years of community research into Netflix's authentication protocols.

S
Description
Netflix MSL Handshake for All Platforms
Readme
234 KiB
Languages
Python 100%