LINE webhook api มีสิ่งขั้นตอนนึงที่ต้องทำคือการ validate signature เพื่อยืนยันว่า request ที่เข้ามานั้นมาจาก Official Account ของเราจริงๆ ซึ่งถ้าใช้ภาษาที่ official LINE support แล้วละก็จะมีจะ function ให้ใช้งานอย่าง line-bot-sdk-go ที่มี ParseRequest(*http.Request) ให้ใช้งานก็จะ validate signature จาก request header ให้อัตโนมัติ
ถ้าลองลอกการทำงานของ validate signature เพื่อนำมาทำ webhook ใน Elixir น่าจะได้ประมาณนี้
ขั้นตอนแรกสร้าง hash ด้วย HMAC-SHA256 โดยใช้ key เป็น channel secret และ data เป็น request body ที่ LINE ส่งเข้ามา function จะได้หน้าตาประมาณนี้
defp sign_signature(channel_secret, body) do
:crypto.hmac(:sha256, channel_secret, body)
end
จากนั้น encode ด้วย base64 จะได้ signature ที่ LINE ส่งมาทาง request header ซึ่งใช้ pipe operator pipe ตัว hash ที่เราสร้างเข้าไป Base.encode เลยจะง่ายกว่า
defp sign_signature(channel_secret, body) do
:crypto.hmac(:sha256, channel_secret, body) |> Base.encode64()
end
จากนั้นเอา signature ที่ได้ไป compare กับ signature จาก request header
def validate(signature, body, channel_secret) do
validate(signature, sign_signature(channel_secret, body))
end
defp validate(signature, signature), do: :ok
defp validate(_, _), do: :invalid_signature
ผมสร้าง validate/3 เพื่อรับ signature จาก request header แล้วส่งเข้าไปที่ validate/2 เพื่อใช้ pattern matching ดูว่าเป็น signature เดียวกันก็จะตอบ :ok
ไปเลยส่วน case อื่นก็ถือว่า :invalid_signature
ไป
ปล. จริงๆ จะใช้ case <> do ... end
ก็ได้ไม่ว่ากัน เพราะความตั้งใจผมคือต้องการแยกเคสให้อ่านง่าย
ผมลองสร้างตัวอย่างโดย port มาจาก echo_bot ที่อยู่ใน line-bot-sdk-go ที่นี่ลองดูหรือ discuss กันได้ครับ :)
ข้อควรระวัง
LINE จะ sign signature โดยรวม space ใน request body กรณีนี้จะเจอตอนที่ LINE ส่ง body แบบ indent JSON มาให้ซึ่งหากเราใช้ Plug.Parsers แล้วมาดูจาก conn.body_params อาจจะผิดได้ (เคสนี้ผมเจอตอน verify webhook หาอยู่พักนึงถึงเข้าใจ)
Top comments (0)