Cofacts LIFF redesign

Implementation

LIFF 實作為一 SPA:

LIFF <> Chatbot server communication

結合 liff.js 的 server 資料(user id)與 chatbot server 資料 (nonce in URL param) 來驗證使用者資料。

  1. In user’s redis session, generate a nonce for current session and change nonce as new session starts (issueAt changes)
  2. User clicks LIFF URL with URL path and nonce param, LIFF pops up
  3. LIFF invokes GraphQL APIs with userId in liff.init() response and the nonce in AJAX headers and get required data / perform mutation
  4. When user perform action that brings themselves back to chatbot, LIFF will invoke liff.sendMessage with special pattern (Readable prefix, or flex message (???)) –> 因為要有 text message 才能 reply

[!CAUTION] nonce 打算用 JWT 取代掉。JWT 每次產生 LIFF 按鈕時會蓋一個新的,裡面有:

  • exp: 每次產生 LIFF 按鈕的時候產生,擋住太久以前的 LIFF,控制 JWT leak 的 impact
  • sessionId: server 可跟當下 redis context.data.sessionId 比對是否屬於同一個 session
  • sub: user Id,API 將以此判斷是哪一位使用者。LIFF 會拿 sub 與 liff.getProfile() 結果比對。

LIFF UI

LIFF 互動 & state diagram

https://docs.google.com/drawings/d/1sSzI0PSggkA3PPP99Nl18H4zMO4lk-2y5s7dGRNJwAE/edit

https://g0v.hackmd.io/6f12uznTQjCDRAKRyPOSxw?both#LINE-bot-%E4%BA%92%E5%8B%95%E4%BF%AE%E6%94%B9

LIFF API

[!CAUTION] 想改成 Authorization: bearer <JWT>

type Query {
  # Current user's chatbot context
  context: Context
  
  # TODO: Implement after database is established
  readArticleIds(skip: Int, limit: Int): [String]
}

type Context {
  state: String
  issuedAt: Int
  data: StateData
}

type StateData {
  searchedText: String
  selectedArticleId: String
  selectedArticleText: String
  selectedReplyId: String
}

type Mutation {
  # Submits searchedText in context, then store articleId to context.
  # Returns created article ID.
  submitArticle: ID
  
  # Submit empty reply request to the articleId in context.
  submitReplyRequest: boolean
  
  voteReply(vote: FeedbackVote): boolean  
  
  # TODO: Implement after database is established
  setViewed(articleId: String!): boolean
}

enum FeedbackVote {
  UPVOTE
  DOWNVOTE
}

Project folder restructure

現在有點亂,加上 LIFF & chatbot 共用的 file 之後,可能會更亂。 因此做下面的改動:

Setup on LINE side

LIFF 2.0 w/ LINE login-capable channels

Design alternatives - 沒採用的設計

LIFF as server-rendered pages

Pros:

Cons:

CSRF: 不是用來防偽造身份打 api 的 只能防跨網域 nonce = 暫時性密碼是正解

Deploy LIFF elsewhere

現況就是 LIFF 在 github pages, chatbot 在 Heorku。

優點:打開 LIFF 不佔 chatbot 資源(但 API 還是會) 缺點:

CRA instead of Svelte

Pros

Cons

During development, use CRA’s built-in web server + proxy

一樣是要同時開 CRA 與 chatbot server,CRA 也有內建 proxy,由 CRA webpack-dev-server 的 express 來主導。

但這樣一來,/ 也會是 CRA host 的 index.html,而且這樣的行為與 production 下,server & LIFF 的主客關係相反,讓開發變得有些複雜。