How LINEJS Authentication and Token Refresh Work: A Complete Technical Guide
LINEJS handles authentication by storing a short-lived authToken and long-lived refreshToken, automatically detecting expiration via MUST_REFRESH_V3_TOKEN errors and seamlessly refreshing tokens without user intervention.
The LINEJS library (evex-dev/linejs) provides a robust authentication system for the LINE messaging API. Understanding how LINEJS manages authentication and token refresh is essential for building reliable bots and clients that maintain persistent connections without manual re-authentication.
Understanding the LINEJS Authentication Model
LINEJS implements a dual-token architecture that separates short-term access from long-term session persistence.
Token Storage Architecture
The client maintains two critical pieces of authentication data:
authToken– Stored inBaseClient.authTokenand exposed viaClient.authToken. This short-lived LINE access token is sent as thex-line-accessheader in every HTTP request.refreshToken– Persisted in the client's storage layer under the key"refreshToken". This long-lived token enables the acquisition of newauthTokenvalues when the current one expires.
Both tokens are emitted through the update:authtoken event, allowing applications to react to authentication state changes.
Event-Driven Token Updates
The authentication system uses an event-based pattern defined in packages/linejs/base/core/utils/events.ts. When tokens change, the client emits events that consuming applications can listen to for persistence or UI updates.
Initial Login and Token Acquisition
The login logic resides in packages/linejs/base/login/mod.ts, which handles multiple authentication methods.
Password-Based Authentication
The withPassword method executes the following sequence after successful credential validation:
- Calls
client.auth.refreshto obtain initial tokens - Extracts the
accessTokenandrefreshTokenfrom the response - Stores the
authTokenin memory and emits theupdate:authtokenevent - Persists the
refreshTokento storage for future use
// Simplified flow from login/mod.ts (lines 140-155)
this.client.authToken = tokenInfo[1]; // access token
this.client.emit("update:authtoken", tokenInfo[1]);
await this.client.storage.set("refreshToken", tokenInfo[2]); // refresh token
QR Code and Direct Token Login
QR-code login follows an identical token storage path once the QR flow completes. Direct auth-token login bypasses server contact and immediately emits the supplied token via the update:authtoken event.
All login methods converge on Login.login(), which calls this.ready() to fetch the user profile and emit the "ready" event.
Automatic Token Refresh Mechanism
LINEJS implements transparent token refresh that requires no manual intervention from consuming applications.
Detecting Token Expiration
The RequestClient in packages/linejs/base/request/mod.ts monitors server responses for the specific error code MUST_REFRESH_V3_TOKEN:
// From request/mod.ts (lines 24-28)
const isRefresh = Boolean(
res.data.e &&
res.data.e.code === "MUST_REFRESH_V3_TOKEN" &&
await this.client.storage.get("refreshToken"),
);
When this error occurs and a refreshToken exists in storage, the client triggers automatic refresh.
The Refresh Token Flow
AuthService.tryRefreshToken() in packages/linejs/base/service/auth/mod.ts handles the exchange:
// From auth/mod.ts (lines 20-34)
public async tryRefreshToken() {
const refreshToken = await this.client.storage.get("refreshToken");
if (typeof refreshToken === "string") {
const RATR = await this.refresh({ request: { refreshToken } });
this.client.authToken = RATR.accessToken;
this.client.emit("update:authtoken", RATR.accessToken);
await this.client.storage.set(
"expire",
(RATR.tokenIssueTimeEpochSec as number) +
(RATR.durationUntilRefreshInSec as number) as number,
);
} else {
throw new InternalError("RefreshError", "refreshToken not found");
}
}
This method calls the LINE endpoint /EXT/auth/tokenrefresh/v1, updates the in-memory authToken, emits the change event, and stores the new expiration timestamp.
Request Retry Logic
After successful refresh, RequestClient.requestCore() automatically retries the original request with the new token:
// From request/mod.ts (lines 51-62)
if (isRefresh && !isReRequest) {
await this.client.auth.tryRefreshToken();
return this.requestCore(
path, value, methodName, protocolType,
appendHeaders, overrideMethod, parse, true,
);
}
The true flag passed as the final argument marks this as a re-request, preventing infinite refresh loops.
Implementing Token Event Listeners
Applications should listen to the update:authtoken event to persist tokens or update UI state:
client.on("update:authtoken", (newToken) => {
// Update UI, store in secure storage, etc.
console.log("Access token refreshed:", newToken);
});
The end event signals complete session termination when requests fail with NOT_AUTHORIZED_DEVICE, indicating the refresh token itself is invalid and manual re-authentication is required.
Summary
- Dual-token architecture: LINEJS stores a short-lived
authTokenfor requests and a long-livedrefreshTokenfor renewal. - Automatic detection: The client monitors for
MUST_REFRESH_V3_TOKENerrors inpackages/linejs/base/request/mod.ts. - Seamless refresh:
AuthService.tryRefreshToken()inpackages/linejs/base/service/auth/mod.tsexchanges the refresh token and retries failed requests automatically. - Event-driven updates: The
update:authtokenevent notifies applications of token changes for persistence or UI updates.
Frequently Asked Questions
How does LINEJS detect when an access token expires?
LINEJS detects expired tokens by monitoring HTTP responses for the specific error code MUST_REFRESH_V3_TOKEN. When RequestClient in packages/linejs/base/request/mod.ts receives this error and finds a stored refreshToken, it automatically triggers the refresh flow without throwing an error to the calling code.
What happens if the refresh token is invalid or missing?
If the refresh token is missing or invalid, AuthService.tryRefreshToken() throws an InternalError with the code "RefreshError". Additionally, if a request fails with NOT_AUTHORIZED_DEVICE, the client emits the "end" event, signaling that the session has terminated and the user must re-authenticate manually.
Can I manually trigger a token refresh in LINEJS?
Yes, you can manually trigger a refresh by calling client.auth.tryRefreshToken(). This method is public and available in packages/linejs/base/service/auth/mod.ts. It retrieves the stored refresh token, calls the LINE /EXT/auth/tokenrefresh/v1 endpoint, and updates the client's authToken while emitting the update:authtoken event.
How should I persist authentication tokens between application restarts?
Listen to the update:authtoken event to save the access token, and ensure your storage backend persists the refreshToken (stored under the key "refreshToken"). When initializing the client, populate the storage with the saved refresh token so that AuthService.tryRefreshToken() can function if the access token expires during the session.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →