Let’s Hack LINE LIFF SDK

Watchanon Numnam
3 min readOct 6, 2021

Disclaimer: บทความทั้งหมดนี้เกิดจากการลอง Revert engineer และมโนเอาเองล้วนๆครับ ไม่มีส่วนไหนที่ได้รับรู้มาจากการ Implement จริงของ Line เลย ใช้เพื่อเป็น Reference เพื่อให้ผมลองพัฒนาระบบแบบเดียวกับ LIFF ขึ้นมาเพียงเท่านั้น หากมีข้อผิดพลาดตรงไหนสามารถแนะนำได้เลยครับ

ในบทความนี้จะลองมาแกะการทำงานของ LIFF SDK กันว่ามันมีการทำงานคร่าวๆยังไงบ้าง แล้วมันเกี่ยวข้องยังไงกับตัว Open API ที่เคยกล่าวถึงมา

จากบทความเก่าที่เคยเล่าเรื่อง OAuth แบบต่างๆ ส่วน จริงๆแล้ว LIFF พยายามทำตัวเป็น OAuth2 ฉlient ให้เราสามารถได้ ID Token และ AccessToken และยังช่วยเรื่องการเรียก Open API ต่างๆของไลน์

และก่อนจะมาของเขียน Javascript SDK เราต้องเรียนรู้เรื่อง การบิ้ว Javascript Library กันนิดหน่อยด้วย Webpack

แล้ว LIFF มันทำงานคร่าวๆยังไง

LIFF เองรองรับทั้งการทำงานแบบ External Browser และ Internal Browser ของ Line App โดยการทำงานต่างๆจะต่างกันนิดหน่อยในจังหวะ Get Access Token และการเก็บข้อมูลใน Session Storage หรือ Local Storage

การทำงานใน Internal Browser (Line Browser)

in line app

ขั้นตอนการทำงานใน Internal Browser เริ่มจากการที่ Mobile App register deeplink เอาไว้ ในกรณีของ Line จะใช้ https://liff.line.me/${liffId}

เมื่อมีการกด Deeplink เข้ามาก่อนที่จะ Navigate ไปเปิด Internal Browser จะทำการไปขอ Token ( Access Token หรือ ID Token) กับ API ของตัว Mobile App ก่อน จริงๆแล้วในขั้นตอนนี้ Line App มี User Session อยู่แล้ว จึงน่าจะไปขอให้ Line Mobile App API ไปขอ Token จาก Authorization Server (access.line.me) และส่งกลับมาให้

ขั้นตอนทั้งหมดนี้เกิดขึ้นภายใน API ของ Line App เลยดาว่า Line Mobile App API น่าจะไปขอ Authorization Server แบบเป็น Implicit type เพราะวิ่งผ่าน Internal Network

ด้วยการส่ง LiffId ที่ได้จาก Deeplink และ User Session ของ Line App ทำให้ Authorization Server ทำการสร้าง Token ของ User คนนั้นๆได้ โดยมี Scope ตามที่ LiffId น้ันๆเคย Register ไว้ (โดยอาจจะมีการพร้อมแสดงหน้าจอ Consent ก่อนที่จะ Issue Token กลับมาให้)

โดยจะส่งค่า Access Token มาพร้อมกับ URL Endpoint ของ LIFF ID ด้วยการใช้ Url fragment โดยมี Access Token ต่อท้ายมากับ URL Endpoint http://partner.com#access_token=xxxx จากนั้น Line App จะทำการเปิด Internal Browser โดย Internal Browser จะทำการเพิ่ม “Line/${version} Line” ต่อท้ายเข้าไปใน User-Agent

หลังจากนั้น Javascript SDK (LIFF SDK) ที่ฝั่งอยู่ที่ Client Website จะทำหน้าที่ต่อโดยการเช็คว่าเป็นการรันใน isInClient() หรือไม่ ด้วยการเช็ค User-Agent ที่เปิดเวปไซต์เข้ามา หาก User-Agent มีคำว่า Line อยู่ด้วยก็แสดงว่ากำลังเปิดใน Internal Browser อยู่

จากนั้น SDK จะทำการแกะค่า Parameter ต่างๆเช่น Access Token จาก URL fragment มาเก็บไว้ใน Session Storage

ในส่วนของฟังก์ชั่นเช็คว่ามีการล็อกอินอยู่แล้วไหม isLoggedIn() มันคือการเช็คว่ามี Access Token เก็บอยู่ใน Local Storage หรือ Session Storage หรือไม่ หากมีก็จะแสดงว่าเราล็อกอินแล้ว และสามารถนำไปใช้งานเรียกฟังก็ชั่นของ Open API อื่นๆได้ แต่หากไม่มี ก็แสดงว่าเรายังไม่ได้ล็อกอินหรือล็อกอินหมดอายุไปนั่นเอง

เพียงแค่นี้ ถ้าเราทำการเรียกใช้ฟังก์ชั่นต่างๆที่ต้องการ Access Token เช่น getProfile() โดยเมื่อเราทำการเรียก Javascript SDK ก็จะทำการอ่านค่า Access Token จาก Local Storage หรือ Session Storage แล้วส่งไปใน Authorization Header ทำให้เราสามารถใช้งาน Open API ได้อย่างสบายๆ

การทำงานของ Internal Browser ของ Line ทุกครั่งที่เราเปิด LIFF App ระบบจะ Issue Access Token และเก็บใน Session Storage หากเราปิดละเปิดใหม่ก็จะ Issue ขึ้นมาใหม่

การทำงานใน External Browser

external browser

ในส่วนของการเปิดจาก External Browser จะต่างกับ Internal Browser ในช่วงการ Issue Token ขึ้นมา เนื่องจากใน Internal Browser นั้นตัว Native Application จะทำการส่ง Token มาให้ แต่สำหรับ External Browser เมื่อผู้ใช้เปิด https://lift.line.me/${liffId} จะทำการ Redirect ไปหา URL Endpoint ของ Partner

จากนั้นในผู้พัฒนาจะทำการสั่ง lift.init({liftId: XXXX}) พร้อมกับส่ง liftId ไปให้ LIFF SDK ทำการ Initialize Context เมื่อเรียกร้อยแล้ว เราก็จะสามารถเรียกใช้งานฟังชั่นต่อๆไปได้ (บางฟังก์ชั่นไม่จำเป็นต้อง init ก่อน)

เมื่อผู้พัฒนาสั่ง liff.login() LIFF SDK จะทำการ Request Authorization code ไปยัง Authorization Server (http://access.life.me) เพื่อขอ Authorize ตาม Authorization Code with PKCE Flow

จากนั้น Authorization Server จะส่ง Authorization Code กลับมาใน Query string และ Redirect กลับมา จากนั้น SDK จะทำการอ่านค่า Code จาก Query string ที่ได้หลังจากการ Redirect ส่งไป Request token ต่อไปให้เลยโดนอัตโนมัติ และเมื่อสิ้นสุดกระบวนการ Authorization Server จะ Redirect กลับมาพร้อมส่ง AccessToken มาใน Query string https://partner.com?access_token=xxxx

จากนั้น Javascript SDK จะทำการอ่านค่า Query string เพื่อนำค่า Access Token ไปเก็บใน Local Storage พร้อมกับสร้าง Cookie เก็บค่า Key อะไรก็ได้สักตัว และทำการ Set Expire ของ Cookie ไว้เวลาเดียวกับที่ Access Token หมดอายุ

ทำไมต้องเก็บ Cookie เช่นนี้ ขอย้อนกลับไปนิดนึง ในขั้นตอนการ initContext หลังจากที่เราเรียก liff.init() LIFF SDK จะพยายามไปอ่านค่า Key ที่เราเก็บไว้ใน Cookie หากว่าเจอค่าก็แสดงว่า Access Token เรานั้นยังไม่หมดอายุ แต่หากไม่เจอค่าใน Cookie ก็จะแสดงว่า Access Token ที่เราได้เก็บไว้ใน Local Storage นั้นควรจะหมดอายุไปแล้ว จากนั้น LIFT SDK ก็จะไปไปลบ Access Token ใน Local Storage ทิ้ง ดังนั้นเมื่อหลังจาก initContext เรียบร้อยแล้ว เราไปเรียก isLoggedIn() เราก็จะได้ค่า false นั่นเอง

หลังจากเราได้ Access Token แล้วเราก็สามารถใช้งานต่างๆของ Open API ได้อย่างปกติ เหมือนกันทั้ง Internal Browser และ External Browser

ทั้งหมดนี้เป็นเพียงแค่ส่วนหนึ่งที่ LIFF SDK นั้นทำให้ครับ

--

--