Authentication & login¶
Every OkLine request is signed with an X-Hmac header, so Node.js 18+ must be
on your PATH (OkLine runs a bundled WASM module through a tiny Node bridge to
compute the signature — see architecture). You also need
Python 3.9+.
There are three ways to get an authenticated OkLine:
- Reuse a saved session — log in once, then load the same file forever (this also restores your E2EE keys).
- QR-code login — scan a code with the LINE app, no password.
- E-mail + password — the classic RSA credential flow.
🔒 Tokens are credentials. An access token (and a saved session file) grants full access to your LINE account. Never commit, log, paste or share them. OkLine redacts them by default in recorded output, and
tokens.jsonis matched by the project.gitignore.
1. Reuse a saved session (recommended)¶
The easiest, most reliable pattern: log in once, save a session file, then load it on every later run. The session file keeps your access token, refresh token, certificate, mid — and the unwrapped E2EE (Letter Sealing) keychain — so encrypted messages keep working across runs without re-scanning a QR code.
Save a session after logging in¶
from okline import OkLine
from okline.qrterm import print_qr
api = OkLine()
api.qr_login(on_qr=print_qr, on_pin=lambda pin: print("Confirm this PIN on your phone:", pin))
api.save_tokens("tokens.json") # writes credentials + E2EE keychain
api.qr_login(...) (on the OkLine object) drives the QR flow and loads
your E2EE keys for this session, so a following save_tokens() persists them.
Reuse it next time — instant, no scan¶
from okline import OkLine
api = OkLine.from_tokens_file("tokens.json") # restores tokens AND E2EE keys
print(api.get_profile())
print("E2EE ready:", api.e2ee.is_ready()) # True — encrypted chats work
That is all you need. Prefer this over hand-writing JSON — save_tokens /
from_tokens_file use the right field names and carry the E2EE keychain, which a
manual json.dump of {accessToken, refreshToken} cannot.
When a client is built with
from_tokens_file, OkLine auto-saves the file whenever the access token is refreshed (see Token refresh), so the stored session stays valid.
The CLI does exactly this for you: okline login logs in once and saves
./tokens.json; every other command reuses it automatically. See CLI.
2. QR-code login¶
Scan a QR with the LINE app on your phone — no password needed. OkLine renders the QR as ASCII right in your terminal and drives the whole flow (for a step-by-step walkthrough with troubleshooting, see the dedicated QR login guide):
from okline import OkLine
from okline.qrterm import print_qr
api = OkLine()
result = api.qr_login(
on_qr=lambda url: print_qr(url), # draw the QR (scan it)
on_pin=lambda pin: print("PIN:", pin), # show the PIN to confirm
wait_seconds=180, # how long to wait for you
)
print("logged in:", bool(result.access_token))
api.save_tokens("tokens.json") # so you don't have to scan again next time
What happens under the hood:
createSession → createQrCode → (generate a Curve25519 key inside the WASM)
→ show QR = callbackUrl + ?secret=<pubkey>&e2eeVersion=1
→ checkQrCodeVerified (you scan) → verifyCertificate / PIN flow
→ checkPinCodeVerified (you confirm the PIN) → qrCodeLoginV2 → tokens
The ?secret=…&e2eeVersion=1 on the QR URL is mandatory — without it the LINE
app shows "an error occurred" after scanning. OkLine generates the Curve25519
keypair inside the real ltsm.wasm and appends it automatically, so you only
render the URL on_qr hands you.
Light-background terminal? Pass print_qr(url, invert=True). QR garbled on
Windows? Run chcp 65001 first. For an inline graphical QR, install the extra:
pip install "okline[qr]".
Skip the PIN on later logins¶
qr_login returns a certificate. Save it and pass it next time to skip the PIN
step (verifyCertificate succeeds for a known device):
result.certificateis also written into your session file bysave_tokens, so loading withfrom_tokens_filecarries it for you.
3. E-mail + password (RSA)¶
This mirrors the extension exactly: it fetches an RSA key, encrypts your
credentials and calls loginV2.
from okline import OkLine
api = OkLine()
result = api.auth.email_login("me@example.com", "secret", with_e2ee=False)
if result.success:
print("access token:", result.access_token[:12], "…")
print(api.get_profile())
elif result.type == 3: # REQUIRE_DEVICE_CONFIRM
print("Confirm this PIN on your phone:", result.pin_code)
else:
print("needs verification:", result.type, result.display_message)
email_login returns a LoginResult with fields: .success, .type
(LoginResultType), .access_token, .refresh_token, .certificate, .mid,
.pin_code, .verifier, .display_message, .raw. On success the tokens are
adopted into the client automatically, so you can call API methods right away.
with_e2ee=True (the default) negotiates Letter Sealing (E2EE). The RSA password
blob is built and encrypted for you in
okline/crypto.py.
After a successful login you can persist the session the same way:
Token refresh¶
Automatic: if a refresh_token is set, a 401 from the server triggers a token
refresh and the original request is retried transparently. When the client was
built with from_tokens_file, the refreshed token is also written back to the
file.
You can also refresh manually:
To build a client directly from tokens you already hold (auto-refresh enabled as soon as a refresh token is present):
Proactive renewal schedule (auto_refresh_schedule)¶
Every login and token-refresh response carries a tokenV3IssueResult telling
the client when the token should be renewed
(tokenIssueTimeEpochSec + durationUntilRefreshInSec). The extension arms a
setTimeout(renewToken, ...) on every issuance (its tT class); OkLine ports
that as an opt-in background timer:
api = OkLine.from_tokens_file("tokens.json", auto_refresh_schedule=True)
# or OkLine(..., auto_refresh_schedule=True)
With the flag set, a daemon threading.Timer is armed after every login and
every refresh (minus up to ~1 s of jitter, matching the extension), silently
renews the token when it fires, re-arms itself from the new schedule, and —
when the client came from a session file — saves the refreshed token back to
it. A failed silent renewal only logs a warning: the old token keeps working
until the reactive 119/401 refresh path fires. If an SSE operation stream is
active, it is reconnected with the fresh token, exactly like the extension.
The schedule persists in the session file (camelCase keys), so
from_tokens_file(..., auto_refresh_schedule=True) re-arms the timer without
a fresh login. Cancel it with api.cancel_refresh_schedule() (also called by
api.close()).
Refresh retries (10202) and kickout (10201)¶
refresh_access_token() follows the server-provided refreshApiRetryPolicy
(recorded from the last login/refresh): if the refresh endpoint answers with
gateway code 10202 (AUTH_RETRY_REQUIRED), the call is retried with the
policy's jittered exponential backoff (initialDelayInMillis * multiplier^n,
each sleep jittered by jitterRate, capped at maxDelayInMillis). Note the
backoff sleeps are blocking — budget up to maxDelayInMillis per retry
sequence. Gateway code 10201 (AUTH_INVALID_REQUEST) is a hard kickout:
it raises LineAuthError and you must log in again. Without a stored policy
the refresh is a single attempt (pre-2.9 behaviour).
Logout¶
Invalidate the current session on the server:
From the CLI, okline logout does this and deletes the local tokens.json.
You can also remove the device from LINE app → Settings → Account → logged-in
devices.
See also: Getting started · Messaging · Receiving events / bots · CLI.