// app/api/line/callback/route.ts
import { cookies } from 'next/headers'
import { NextResponse, type NextRequest } from 'next/server'
export async function GET(req: NextRequest) {
const code = req.nextUrl.searchParams.get('code')
const state = req.nextUrl.searchParams.get('state')
const jar = await cookies()
if (!code || !state || state !== jar.get('line_state')?.value) {
return NextResponse.redirect(new URL('/login?error=state', req.url))
}
// แลก code เป็น token
const tokenRes = await fetch('https://api.line.me/oauth2/v2.1/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
redirect_uri: `${process.env.SITE_URL}/api/line/callback`,
client_id: process.env.LINE_CLIENT_ID!,
client_secret: process.env.LINE_CLIENT_SECRET!,
}),
})
const token = await tokenRes.json()
// ให้ LINE ตรวจ ID token แทนการถอดรหัสเอง
const verifyRes = await fetch('https://api.line.me/oauth2/v2.1/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
id_token: token.id_token,
client_id: process.env.LINE_CLIENT_ID!,
nonce: jar.get('line_nonce')?.value ?? '',
}),
})
if (!verifyRes.ok) {
return NextResponse.redirect(new URL('/login?error=verify', req.url))
}
const profile = await verifyRes.json() // sub = LINE user ID, name, picture
// TODO: หา/สร้างผู้ใช้ในฐานข้อมูลด้วย profile.sub แล้วสร้าง session ของระบบเรา
jar.delete('line_state')
jar.delete('line_nonce')
return NextResponse.redirect(new URL('/account', req.url))
}
state กันการปลอม request ข้ามเว็บ ส่วน nonce กันการเอา ID token เก่ามาใช้ซ้ำ อย่าตัดสองตัวนี้ออกแม้โค้ดจะทำงานได้โดยไม่มีมัน ส่วนการสร้าง session ให้ใช้ระบบที่มีอยู่ เช่น cookie แบบ httpOnly ที่เก็บ session ID
ให้ผู้ใช้เพิ่มเพื่อน LINE OA ตอนล็อกอิน
นี่คือเหตุผลที่ธุรกิจไทยหลายเจ้าเลือกทำ LINE Login เพราะได้ทั้งบัญชีผู้ใช้และเพื่อนใน LINE OA ในขั้นตอนเดียว ทำได้สองขั้น
1.ใน LINE Developers Console เปิด channel LINE Login ที่แท็บ Basic settings แล้วผูก Linked LINE Official Account กับ OA ของร้าน
2.ใส่ bot_prompt ใน URL อนุญาต ตาม เอกสารการผูก LINE OA ถ้าใช้ normal จะมีตัวเลือกเพิ่มเพื่อนอยู่ในหน้ายินยอม ถ้าใช้ aggressive จะเปิดหน้าจอแยกให้เพิ่มเพื่อนหลังหน้ายินยอม
ตอน LINE ส่งผู้ใช้กลับมาที่ callback จะมีพารามิเตอร์ friendship_status_changed บอกว่าสถานะเพื่อนเปลี่ยนไหม ใช้เก็บสถิติว่าได้เพื่อนใหม่จากหน้าล็อกอินกี่คนได้
ใช้ LINE user ID ผิดตัว LINE user ID (sub) ต่างกันในแต่ละ Provider ถ้ามีหลาย channel เช่น LINE Login กับ LIFF ให้สร้างไว้ใน Provider เดียวกัน user ID จะได้ตรงกัน
ผูกบัญชีด้วยอีเมล ผู้ใช้บางคนไม่ได้ตั้งอีเมลใน LINE หรือไม่ยอมให้สิทธิ์อีเมล ให้ใช้ sub เป็นตัวระบุผู้ใช้หลัก ส่วนอีเมลเก็บไว้เป็นข้อมูลเสริม
การสร้าง LINE Login channel และให้ผู้ใช้ล็อกอินไม่มีค่าบริการจาก LINE ค่าใช้จ่ายที่อาจเกิดคือฝั่ง LINE OA ถ้าส่งข้อความหาผู้ใช้เกินโควตาของแพ็กเกจ OA
ในการใช้งานทั่วไปไม่ได้ ข้อมูลที่ได้คือ user ID ชื่อที่แสดง รูปโปรไฟล์ และอีเมลถ้าได้รับสิทธิ์ ถ้าต้องใช้เบอร์โทร ให้ถามผู้ใช้เพิ่มหลังล็อกอิน
LINE Login ใช้ให้คนล็อกอินเว็บด้วยบัญชี LINE ส่วน LIFF คือการเปิดเว็บแอปข้างในแอป LINE ซึ่งดึงข้อมูลผู้ใช้ได้โดยไม่ต้องผ่านหน้าล็อกอิน ถ้าลูกค้าส่วนใหญ่เข้าเว็บจากลิงก์ในแชต LINE OA, LIFF อาจเหมาะกว่า