CodeForge
← All posts
Security·August 19, 2026·5 min read

Don't trust the client: verifying LINE LIFF logins server-side

Building auth for a LINE Mini App means the login token arrives from inside a platform you don't control. Here's the verification step that's easy to skip and shouldn't be.

LINE Mini Apps (LIFF — LINE Front-end Framework) run inside the LINE app, and the platform hands your front end an ID token identifying the logged-in LINE user. It's tempting to treat that token as good enough on its own: the LIFF SDK already confirms the user is logged in, so why not just read the profile it gives you client-side and call it authentication?

Because the client is not a trust boundary. Anything the browser or the LIFF WebView can read, a motivated user can also forge, replay, or tamper with before it reaches your server. On a recent AI tarot-reading app built as a LINE Mini App, this wasn't a hypothetical — it's the exact seam between "the platform vouches for this user" and "our server has to independently agree."

Verify the token where the client can't interfere

The fix is straightforward in principle and easy to skip under deadline pressure: on every login, the server takes the ID token the client hands it and calls LINE's own verification endpoint directly, server-to-server, before issuing anything of its own. If LINE says the token is invalid, expired, or issued for a different app, the request is rejected before a session is ever created. The client's claim about who it is never gets trusted on its word — it gets independently confirmed against the platform that issued it.

This is a small amount of code and an extra network round-trip, which is exactly why it's the kind of check that's easy to defer "for now" and never come back to. But it's the one check that actually separates "a user is logged into LINE" from "our backend has confirmed this session belongs to that specific LINE user."

Sessions: sign them, don't just store them

Once the token checks out, the app issues its own session — an HMAC-SHA256-signed, httpOnly, secure cookie. Signing it means the server can detect tampering without a database round-trip on every request: recompute the signature, compare it to what came in, and if they don't match byte-for-byte, reject it. That comparison is done in constant time specifically to avoid leaking information through response-time differences — a standard defense against timing attacks on signature checks, and one of those details that costs nothing to get right the first time and is annoying to retrofit later.

httpOnly keeps the cookie out of reach of any JavaScript running in the page, which matters more than usual inside a WebView you don't fully control the surrounding environment of. secure ensures it's never sent over a plain HTTP connection.

The general lesson

Any time authentication involves a third-party identity platform — LINE, Google, an internal SSO — the integration usually looks trustworthy from the client side because the SDK does real work and the UI genuinely reflects a logged-in state. The question worth asking on every one of these integrations is narrower: what, specifically, does our server independently verify, and what are we simply taking the client's word for? On this project, the answer to the second half used to be "the ID token's contents," and closing that gap was a half-day fix that mattered more than most of the feature work around it.

Ready to build something?

We take on a limited number of projects per quarter. Let's talk before the spots fill up.