Building a Permission Binding System Between LINE Bot and In-House ERP via Phone Number Matching
We wanted staff to check inventory via LINE — but not let just anyone access it.
That simple requirement led to what we built: a LINE × ERP permission binding system.
Background
At TechsFree, we develop and operate our own ERP for a food/produce wholesale business. Letting staff check inventory and sales figures directly via LINE Bot would make field operations much smoother — so we built a bridge between LINE Bot and the ERP backend.
The problem: LINE's userId (Uxxxxxxxx...) and ERP's internal user accounts (email + password) are completely separate. Without knowing "who is this LINE user?", we can't enforce any permission checks.
Solution: Phone Number Matching
Our ERP user table already had a phone column. When someone messages the bot, we simply ask for their phone number, look up the matching ERP user, and bind them.
-- Add LINE-related columns to users table
ALTER TABLE users ADD COLUMN line_user_id VARCHAR(64) UNIQUE NULL;
ALTER TABLE users ADD COLUMN line_display_name VARCHAR(255) NULL;
Since we use Sequelize, adding fields to User.ts means they're automatically synced at startup.
API Design
We added three endpoints:
GET /api/line/user/:line_user_id # Look up identity by LINE ID
POST /api/line/bind # Bind a LINE user to an ERP account via phone
POST /api/line/unbind # Remove the binding
The response from /api/line/user/:id looks like this:
// Not yet bound
{ "bound": false }
// Bound
{
"bound": true,
"user": {
"id": 1,
"name": "Tanaka Taro",
"role": "admin",
"permissions": [
"sales", "purchases", "inventory",
"products", "customers", "suppliers",
"payments", "reports", "staff"
]
}
}
Permission Groups
We kept roles simple — just two types:
| Role | Access |
|---|---|
| admin | Sales / Purchases / Inventory / Products / Customers / Suppliers / Payments / Reports / Staff |
| user (staff) | Inventory / Products / Purchases / Sales / Shipping |
We considered a customer role, but regular customers don't need to see ERP inventory directly — that's what the shop frontend is for. Keeping it simple matters.
Integration Flow with the Agent
The LINE Bot itself runs on a dedicated agent on a separate server. When the agent receives a LINE message, it first calls /api/line/user/:line_user_id to check bound:
-
bound: false→ Guide the user through the binding flow ("Please share your phone number") -
bound: true→ Present available features based onpermissions
The agent calls the ERP API directly (no auth token needed within the internal network).
Where We Got Stuck
TypeScript type definitions were surprisingly annoying. Adding a new column to a User model requires both the sequelize-typescript decorator and Sequelize's sync to be written correctly — otherwise you get runtime errors even when compilation succeeds.
@Column({ type: DataType.STRING(64), unique: true, allowNull: true })
line_user_id!: string | null;
Forgetting allowNull: true will kill existing record sync.
The other gotcha: LINE's channelAccessToken case sensitivity issue. A 401 kept appearing, and we spent time suspecting a typo (uppercase E vs lowercase e). The real issue was that the token itself had been invalidated — regenerating it in LINE Developers Console solved it instantly. The lesson: don't trust your eyes on log characters too much.
Takeaways
Integrating external Bot permission checks into an in-house ERP turned out to be simpler than expected.
Key points:
- Use an existing attribute (phone number) for matching — no OAuth needed
- Manage binding state on the ERP side — keep the Bot stateless
- Return permissions as a flat list — keeps Bot logic simple
"I want to connect LINE with our ERP" is a common requirement in business systems. If you already have a phone number column in your user table, this approach lets you skip building a whole auth infrastructure.
Top comments (0)