Skip to content

Instantly share code, notes, and snippets.

@clins1994
Last active March 16, 2026 12:41
Show Gist options
  • Select an option

  • Save clins1994/d7af75d96695d21db31532631fe5aa59 to your computer and use it in GitHub Desktop.

Select an option

Save clins1994/d7af75d96695d21db31532631fe5aa59 to your computer and use it in GitHub Desktop.

LINE Chrome Net-Export Report

This report summarizes the relevant request and response flow from:

  • docs/line-letter-sealing-off.json
  • docs/line-letter-sealing-on-to-off.json

The source files are Chrome net-export captures, not HAR files.

Caveats

  • Chrome net-export preserves request headers, response headers, response bodies, and upload sizes.
  • The request body is not exposed at the high-level URL_REQUEST / UPLOAD_DATA_STREAM_* layer in a directly usable form.
  • The request body is recoverable at a lower layer by correlating HTTP2_SESSION_SEND_HEADERS with SSL_SOCKET_BYTES_SENT HTTP/2 DATA frames.
  • This report now uses those recovered request bodies where available.
  • Some request fields are still application-level encrypted blobs even after extraction, most notably loginV2.password.
  • Secrets are redacted in this report:
    • access tokens
    • cookies
    • HMACs
    • verifier values
    • QR auth session IDs

Common Header Patterns

Most requests in both traces share these headers:

:authority: line-chrome-gw.line-apps.com
:scheme: https
accept: application/json, text/plain, */*
accept-language: en-US
content-type: application/json
origin: chrome-extension://ophjlpahpchlmihnnnihgmmeilfjmjjc
user-agent: Mozilla/5.0 (...) Chrome/146.0.0.0 Safari/537.36
x-lal: en_US
x-line-chrome-version: 3.7.2
x-hmac: <redacted>

Special cases:

  • QR poll and permit-notice requests add x-line-session-id and x-lst.
  • Authenticated Talk requests add:
x-line-access: <redacted jwt>
cookie: lct=<redacted jwt>

docs/line-letter-sealing-off.json

This capture is mostly a login flow for a Letter Sealing off account, followed by logout. It also contains two incidental post-login group text sends before logout.

Step 1: Initial loginV2 attempt fails

Request:

POST /api/talk/thrift/Talk/AuthService/loginV2
content-length: 549

Request payload:

[
  {
    "type": 2,
    "identityProvider": 1,
    "identifier": "15327",
    "password": "<app-encrypted blob>",
    "keepLoggedIn": false,
    "accessLocation": "",
    "systemName": "Chrome",
    "certificate": "",
    "verifier": "",
    "secret": "<secret>",
    "e2eeVersion": 1,
    "modelName": ""
  }
]

Response:

{
  "code": 10051,
  "message": "RESPONSE_ERROR",
  "data": {
    "name": "TalkException",
    "message": "TalkException",
    "code": 89,
    "reason": "not supported",
    "parameterMap": null
  }
}

Step 2: Retry loginV2 returns verifier and PIN

Request:

POST /api/talk/thrift/Talk/AuthService/loginV2
content-length: 505

Request payload:

[
  {
    "type": 0,
    "identityProvider": 1,
    "identifier": "15343",
    "password": "<app-encrypted blob>",
    "keepLoggedIn": false,
    "accessLocation": "",
    "systemName": "Chrome",
    "certificate": "",
    "verifier": "",
    "secret": "",
    "e2eeVersion": 1,
    "modelName": ""
  }
]

Response:

{
  "code": 0,
  "message": "OK",
  "data": {
    "verifier": "<redacted verifier>",
    "pinCode": "2423",
    "type": 3,
    "lastPrimaryBindTime": "0"
  }
}

Step 3: Long-poll with the verifier

Request:

GET /api/talk/long-polling/JQ
x-line-session-id: <redacted verifier>
x-lst: 180000

Response:

{
  "code": 0,
  "message": "OK",
  "data": {
    "result": {
      "verifier": "<redacted verifier>",
      "authPhase": "QRCODE_VERIFIED",
      "userAuthLogEventListNotValid": true
    },
    "timestamp": "1773625290467"
  }
}

Step 4: Final loginV2 completes login and issues tokens

Request:

POST /api/talk/thrift/Talk/AuthService/loginV2
content-length: 232

Request payload:

[
  {
    "type": 1,
    "identityProvider": 1,
    "identifier": "",
    "password": "",
    "keepLoggedIn": false,
    "accessLocation": "",
    "systemName": "Chrome",
    "certificate": "",
    "verifier": "<redacted verifier>",
    "secret": "",
    "e2eeVersion": 1,
    "modelName": ""
  }
]

Response headers of note:

set-cookie: lct=<redacted jwt>; path=/; samesite=none; secure; httponly

Response body shape:

{
  "code": 0,
  "message": "OK",
  "data": {
    "certificate": "<redacted certificate>",
    "type": 1,
    "lastPrimaryBindTime": "1483535856056",
    "tokenV3IssueResult": {
      "accessToken": "<redacted jwt>",
      "refreshToken": "<redacted refresh token>",
      "durationUntilRefreshInSec": "...",
      "loginSessionId": "...",
      "tokenIssueTimeEpochSec": "..."
    },
    "mid": "<logged-in user MID>"
  }
}

Step 5: Incidental post-login sends present in this trace

These are not part of the login itself, but they are in the file.

Request 1:

POST /api/talk/thrift/Talk/TalkService/sendMessage
content-length: 272
x-line-access: <redacted jwt>
cookie: lct=<redacted jwt>

Request payload:

[
  662642008,
  {
    "from": "<logged-in user MID>",
    "to": "<group MID>",
    "toType": 2,
    "id": "local-662642008",
    "createdTime": "1773625306000",
    "sessionId": 0,
    "text": "テスト",
    "contentType": 0,
    "contentMetadata": {},
    "hasContent": false
  }
]

Response:

{
  "code": 0,
  "message": "OK",
  "data": {
    "from": "<logged-in user MID>",
    "toType": 2,
    "id": "605311889450205372",
    "createdTime": "1773625305497",
    "deliveredTime": "0",
    "hasContent": false,
    "contentType": 0,
    "sessionId": 0,
    "reactions": []
  }
}

Request 2:

POST /api/talk/thrift/Talk/TalkService/sendMessage
content-length: 272
x-line-access: <redacted jwt>
cookie: lct=<redacted jwt>

Request payload:

[
  662642009,
  {
    "from": "<logged-in user MID>",
    "to": "<second group MID>",
    "toType": 2,
    "id": "local-662642009",
    "createdTime": "1773625310000",
    "sessionId": 0,
    "text": "テスト",
    "contentType": 0,
    "contentMetadata": {},
    "hasContent": false
  }
]

Response:

{
  "code": 0,
  "message": "OK",
  "data": {
    "from": "<logged-in user MID>",
    "toType": 2,
    "id": "605311896362418782",
    "createdTime": "1773625309725",
    "deliveredTime": "0",
    "hasContent": false,
    "contentType": 0,
    "sessionId": 0,
    "reactions": []
  }
}

Important note:

  • sendMessage responses do not include the message text.
  • The actual text appears in the /api/operation/receive event stream.

Step 6: Logout

Request:

POST /api/talk/thrift/Talk/AuthService/logoutV2
content-length: 2
x-line-access: <redacted jwt>
cookie: lct=<redacted jwt>

Request payload:

[]

Response:

{
  "code": 0,
  "message": "OK"
}

Response headers of note:

set-cookie: lct=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; samesite=none; secure; httponly

Step 7: Fresh QR login screen starts immediately after logout

Observed requests:

POST /api/talk/thrift/LoginQrCode/SecondaryQrCodeLoginService/createSession
content-length: 4

Request payload:

[{}]

Response:

{
  "code": 0,
  "message": "OK",
  "data": {
    "authSessionId": "<redacted auth session id>"
  }
}

Then:

POST /api/talk/thrift/LoginQrCode/SecondaryQrCodeLoginService/createQrCode
content-length: 88

Request payload:

[
  {
    "authSessionId": "<redacted auth session id>"
  }
]

Response:

{
  "code": 0,
  "message": "OK",
  "data": {
    "callbackUrl": "https://line.me/R/au/lgn/sq/<redacted auth session id>",
    "longPollingMaxCount": 2,
    "longPollingIntervalSec": 150
  }
}

Then:

POST /api/talk/thrift/LoginQrCode/SecondaryQrCodeLoginPermitNoticeService/checkQrCodeVerified
content-length: 88
x-line-session-id: <redacted auth session id>
x-lst: 150000

Request payload:

[
  {
    "authSessionId": "<redacted auth session id>"
  }
]

The net-export does not include a usable response body for this request.

/api/operation/receive events in this trace

Request:

GET /api/operation/receive?localRev=947933&version=3.7.2&lastPartialFullSyncs=%7B%7D&language=en_US

Observed SSE events of interest:

event: ping
data: null

event: connInfoRevision
data: 396

id: 947934
data: {"revision":"947934","createdTime":"1773625292915","type":40,"reqSeq":65421321,"param1":"<group MID>","param2":"605311845963661626","param3":"0"}

id: 947935
data: {"revision":"947935","createdTime":"1773625299390","type":25,"reqSeq":-1,"param1":"0","message":{"from":"<logged-in user MID>","to":"<group MID>","toType":2,"id":"605311879182811626","createdTime":"1773625299388","text":"\\u306a\\u306b\\u3092\\u3057\\u3066\\u3044\\u308b\\u306e","hasContent":false,"contentType":0,"contentMetadata":{},"sessionId":0,"reactions":[]}}

id: 947936
data: {"revision":"947936","createdTime":"1773625299460","type":55,"reqSeq":0,"param1":"<group MID>","param2":"<logged-in user MID>","param3":"605311879182811626"}

id: 947937
data: {"revision":"947937","createdTime":"1773625305500","type":25,"reqSeq":662642008,"param1":"0","message":{"from":"<logged-in user MID>","to":"<group MID>","toType":2,"id":"605311889450205372","createdTime":"1773625305497","text":"\\u30c6\\u30b9\\u30c8","hasContent":false,"contentType":0,"contentMetadata":{},"sessionId":0,"reactions":[]}}

id: 947938
data: {"revision":"947938","createdTime":"1773625305601","type":55,"reqSeq":0,"param1":"<group MID>","param2":"<logged-in user MID>","param3":"605311889450205372"}

id: 947939
data: {"revision":"947939","createdTime":"1773625309727","type":25,"reqSeq":662642009,"param1":"0","message":{"from":"<logged-in user MID>","to":"<second group MID>","toType":2,"id":"605311896362418782","createdTime":"1773625309725","text":"\\u30c6\\u30b9\\u30c8","hasContent":false,"contentType":0,"contentMetadata":{},"sessionId":0,"reactions":[]}}

docs/line-letter-sealing-on-to-off.json

This capture contains:

  1. login with a Letter Sealing on account
  2. direct message to a Letter Sealing off user
  3. group message into a mixed group
  4. logout

Step 0: Old session is invalid, then logout

Observed request:

POST /api/talk/thrift/Talk/TalkService/getProfile
content-length: 3

Request payload:

[2]

Observed response:

{
  "code": 10051,
  "message": "RESPONSE_ERROR",
  "data": {
    "name": "TalkException",
    "message": "TalkException",
    "code": 1,
    "reason": "Authentication Failed.",
    "parameterMap": null
  }
}

Chrome then calls:

POST /api/talk/thrift/Talk/AuthService/logoutV2
content-length: 2

Response:

{
  "code": 0,
  "message": "OK"
}

Step 1: Create QR auth session

Request:

POST /api/talk/thrift/LoginQrCode/SecondaryQrCodeLoginService/createSession
content-length: 4

Request payload:

[{}]

Response:

{
  "code": 0,
  "message": "OK",
  "data": {
    "authSessionId": "<redacted auth session id>"
  }
}

Step 2: Create QR code

Request:

POST /api/talk/thrift/LoginQrCode/SecondaryQrCodeLoginService/createQrCode
content-length: 88

Request payload:

[
  {
    "authSessionId": "<redacted auth session id>"
  }
]

Response:

{
  "code": 0,
  "message": "OK",
  "data": {
    "callbackUrl": "https://line.me/R/au/lgn/sq/<redacted auth session id>",
    "longPollingMaxCount": 2,
    "longPollingIntervalSec": 150
  }
}

Step 3: Permit notice check

Request:

POST /api/talk/thrift/LoginQrCode/SecondaryQrCodeLoginPermitNoticeService/checkQrCodeVerified
content-length: 88
x-line-session-id: <redacted auth session id>
x-lst: 150000

Request payload:

[
  {
    "authSessionId": "<redacted auth session id>"
  }
]

Response:

  • Not available in usable form in the net-export.

Step 4: getRSAKeyInfo then final authenticated loginV2

Observed precursor request:

POST /api/talk/thrift/Talk/TalkService/getRSAKeyInfo
content-length: 3

Request payload:

[1]

Response returns the RSA key material used to construct the encrypted loginV2.password field.

Request:

POST /api/talk/thrift/Talk/AuthService/loginV2
content-length: 569

Request payload:

[
  {
    "type": 0,
    "identityProvider": 1,
    "identifier": "15349",
    "password": "<app-encrypted blob>",
    "keepLoggedIn": false,
    "accessLocation": "",
    "systemName": "Chrome",
    "certificate": "<certificate from prior login>",
    "verifier": "",
    "secret": "",
    "e2eeVersion": 1,
    "modelName": ""
  }
]

Response headers of note:

set-cookie: lct=<redacted jwt>; path=/; samesite=none; secure; httponly

Response body shape:

{
  "code": 0,
  "message": "OK",
  "data": {
    "type": 1,
    "lastPrimaryBindTime": "1534608800980",
    "tokenV3IssueResult": {
      "accessToken": "<redacted jwt>",
      "refreshToken": "<redacted refresh token>",
      "durationUntilRefreshInSec": "...",
      "loginSessionId": "...",
      "tokenIssueTimeEpochSec": "..."
    },
    "mid": "<LSON user MID>"
  }
}

Step 5: Direct LSON -> LSOFF message probe

Request:

POST /api/talk/thrift/Talk/TalkService/negotiateE2EEPublicKey
content-length: 48
x-line-access: <redacted jwt>
cookie: lct=<redacted jwt>

Request payload:

[
  "<LSOFF user MID>"
]

Response:

{
  "code": 0,
  "message": "OK",
  "data": {
    "allowedTypes": [],
    "specVersion": -1
  }
}

Interpretation:

  • Chrome asked whether an E2EE public key was usable for the LSOFF peer.
  • The server did not provide a usable E2EE mode here.

Step 6: Direct sendMessage still succeeds

Request:

POST /api/talk/thrift/Talk/TalkService/sendMessage
content-length: 274
x-line-access: <redacted jwt>
cookie: lct=<redacted jwt>

Request payload:

[
  1524162637,
  {
    "from": "<LSON user MID>",
    "to": "<LSOFF user MID>",
    "toType": 0,
    "id": "local-1524162637",
    "createdTime": "1773638909000",
    "sessionId": 0,
    "text": "テスト",
    "contentType": 0,
    "contentMetadata": {},
    "hasContent": false
  }
]

Response:

{
  "code": 0,
  "message": "OK",
  "data": {
    "from": "<LSON user MID>",
    "toType": 0,
    "id": "605334711279812711",
    "createdTime": "1773638908366",
    "deliveredTime": "0",
    "hasContent": false,
    "contentType": 0,
    "sessionId": 0,
    "reactions": []
  }
}

Step 7: Mixed group E2EE key lookup fails

First request:

POST /api/talk/thrift/Talk/TalkService/getLastE2EEGroupSharedKey
content-length: 50
x-line-access: <redacted jwt>
cookie: lct=<redacted jwt>

Request payload:

[
  1,
  "<mixed group MID>"
]

Response:

{
  "code": 10051,
  "message": "RESPONSE_ERROR",
  "data": {
    "name": "TalkException",
    "message": "TalkException",
    "code": 5,
    "reason": "not found",
    "parameterMap": null
  }
}

Second request:

POST /api/talk/thrift/Talk/TalkService/getLastE2EEPublicKeys
content-length: 48
x-line-access: <redacted jwt>
cookie: lct=<redacted jwt>

Request payload:

[
  "<mixed group MID>"
]

Response:

{
  "code": 10051,
  "message": "RESPONSE_ERROR",
  "data": {
    "name": "TalkException",
    "message": "TalkException",
    "code": 98,
    "reason": "member settings off",
    "parameterMap": {}
  }
}

Interpretation:

  • The group shared key is not available.
  • At least one member has settings that prevent usable E2EE key retrieval.

Step 8: Group sendMessage still succeeds

Request:

POST /api/talk/thrift/Talk/TalkService/sendMessage
content-length: 274
x-line-access: <redacted jwt>
cookie: lct=<redacted jwt>

Request payload:

[
  1524162638,
  {
    "from": "<LSON user MID>",
    "to": "<mixed group MID>",
    "toType": 2,
    "id": "local-1524162638",
    "createdTime": "1773638915000",
    "sessionId": 0,
    "text": "テスト",
    "contentType": 0,
    "contentMetadata": {},
    "hasContent": false
  }
]

Response:

{
  "code": 0,
  "message": "OK",
  "data": {
    "from": "<LSON user MID>",
    "toType": 2,
    "id": "605334721933082899",
    "createdTime": "1773638914873",
    "deliveredTime": "0",
    "hasContent": false,
    "contentType": 0,
    "sessionId": 0,
    "reactions": []
  }
}

Step 9: Logout

Request:

POST /api/talk/thrift/Talk/AuthService/logoutV2
content-length: 2
x-line-access: <redacted jwt>
cookie: lct=<redacted jwt>

Request payload:

[]

Response:

{
  "code": 0,
  "message": "OK"
}

/api/operation/receive events in this trace

Request:

GET /api/operation/receive?localRev=949684&version=3.7.2&lastPartialFullSyncs=%7B%7D&language=en_US

Observed SSE events of interest:

event: ping
data: null

event: connInfoRevision
data: 396

id: 949685
data: {"revision":"949685","createdTime":"1773638908368","type":25,"reqSeq":1524162637,"param1":"0","message":{"from":"<LSON user MID>","to":"<LSOFF user MID>","toType":0,"id":"605334711279812711","createdTime":"1773638908366","text":"\\u30c6\\u30b9\\u30c8","hasContent":false,"contentType":0,"contentMetadata":{},"sessionId":0,"reactions":[]}}

id: 949686
data: {"revision":"949686","createdTime":"1773638914875","type":25,"reqSeq":1524162638,"param1":"0","message":{"from":"<LSON user MID>","to":"<mixed group MID>","toType":2,"id":"605334721933082899","createdTime":"1773638914873","text":"\\u30c6\\u30b9\\u30c8","hasContent":false,"contentType":0,"contentMetadata":{},"sessionId":0,"reactions":[]}}

Important note:

  • These /receive events are the strongest confirmation that Chrome sent plain text successfully in both cases.
  • The sendMessage responses only confirm acceptance and message IDs; the /receive stream is where the actual text body appears.

Practical Takeaway

The Letter Sealing on trace shows this exact sequence:

  1. Try E2EE-related lookup for the direct peer.
  2. Get no usable E2EE mode.
  3. Send plain sendMessage anyway.
  4. For the mixed group, try group-key lookup.
  5. Get not found and member settings off.
  6. Send plain sendMessage anyway.
  7. Confirm success through /api/operation/receive.

That is the main protocol behavior this capture demonstrates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment