

(บทความนี้ เขียนเพื่อ .NET CF - Windows Mobile แต่ว่าประยุกต์ใช้กับ .NET ธรรมดาได้ครับ เพราะ .NET CF เป็นแค่ Subset ของ .NET ธรรมดาเองนะ ส่วน Silverlight ยังไม่ได้ลองครับ)
ตะกี้ผมแทบจะกระโดดชนหลังคาได้เลยครับ ตอนที่ Debug เจอว่าทำไมเจ้า OAuth ของผม มันไม่ยอมคุยกันรู้เรื่องกับ Twitter ซะที ผมเลยคิดว่า ต้องมีคนปวดหัวกับมันอีกเยอะแน่นอน แต่ไม่ต้องมานั่งปวดหัวเหมือนผมแล้วครับ มาดูดีกว่า ว่าผมทำสำเร็จได้อย่างไร
ผมขอออกตัวก่อนว่า ที่ทำได้นี่ ใช้ดวงไปเกินครึ่ง เพราะ
- HttpUtility.UrlEncode ทำงานไม่เหมือนกันเลยซํกกะราย ทั้งของ Mono, ของ Microsoft (System.Web.HttpUtility) และที่ผมดาวน์โหลดมา สุดท้ายคือ ผมต้องไป Fix เองตามมีตามเกิด โดยอ้างอิงจาก http://www.blooberry.com/indexdot/html/topics/urlencoding.htm โดยของ Microsoft ไม่สามารถแยกแยะระหว่าง เครื่องหมาย + และ Space ได้ (ออกมากลายเป็น Space ทั้งคู่) ของ Mono Encode ออกมา ดันได้ตัวเล็ก ซึ่งไม่ตรงมาตรฐาน (เช่น %2B มันดันได้ %2b) ส่วนของที่ผมโหลดมา Space กลายเป็น + และ + ก็ยังได้ + มั่วไปหมด
- Protocol OAuth ซับซ้อนเกินไป และผมกำลังรู้สึกว่า Twitter เอง ก็ไม่ได้ Implement ตามเป๊ะๆ ง่ายๆ เลยคือ ตอน Gen Signature บางครั้งใช้ oauth_token_secret บางครั้ง ไม่ใช้ บางครั้งส่งเป็น Query String ได้ บางครั้ง ไม่ได้ และก็ไม่ได้ระบุไว้ชัดเจนใน Doc ทำให้ต้องงมอยู่นานมาก ต้องขอบคุณ www.codingthewheel.com/archives/codingthetweet มาก ที่เป็นโปรแกรมอ้างอิงให้ ไม่งั้นแย่แน่ๆ ผมต้องเอา Output มันมา Compare กับของผมด้วย Winmerge เลยทีเดียว

- ผมเพิ่งตื่นจากการฟุบไป 6 ชั่วโมง ด้วย ทิฟฟี่ 2 เม็ด
เอาละ เข้าเรื่องๆ
ทำไมต้อง OAuth
เนื่องจากว่า Twitter เพิ่งจะเปลี่ยน Policy ว่า ถ้าจะให้ชื่อโปรแกรมเราขึ้นอยู่ใน Tweet แบบนี้ เราจะต้องใช้ Protocol OAuth เท่านั้น ในการส่ง Tweet

ถ้าใช้ API อย่างเดียว แล้วใส่ Username/Password เจ้า Twitter ก็จะคิดว่าโปรแกรมเราเป็นแค่บอท แบบนี้

ดังนั้น ขั้นแรกเลย เพื่อให้โปรแกรมเรามีชื่อขึ้นใน Twitter เราก็ต้องไปลงทะเบียนก่อน ตามลิงค์ http://twitter.com/oauth_clients ใส่ข้อมูลให้ครบถ้วน เลือก ตามนี้ด้วยละครับ (ขั้นตอนที่ผมแสดง เป็นของ Client นะครับ Browser ใช้ไม่ได้)

แล้วเราก็จะได้ ConsumerKey กับ Consumer Secret มา เก็บมันไว้ให้ดี (และที่น่าเคืองคือ ถ้า Twitter Support PLAINTEXT signature ผ่าน HTTPS ทุกอย่างจะง่ายกว่านี้!!!!)

เมื่อได้ Key มาแล้ว ก็เตรียมปวดหัวกันต่อได้เลย
รู้จักกับ OAuth และพาราเซตามอล
OAuth ก็คือ Protocol ในการแลกเปลี่ยน User Identity แบบหนึ่ง ซึ่งใช้ระบบ Token กล่าวคือ ถ้าเราจะทำอะไร ในฐานะ User ได้ เราก็ต้องมี Token ที่ยืนยันว่าเราได้รับสิทธิ์จากผู้ใชเสียก่อน ซึ่ง Diagram ด้านล่างนี้ จะอธิบายการทำงานคร่าวๆ แต่อ่านไม่รู้เรื่องหรอกครับ ผ่านมันไปก่อนเถอะ (ทำได้แล้ว กลับมาอ่าน ถึงจะเข้าใจ) 

จาก Diagram สรุปว่า ในขั้นแรก เราจะต้อง Request ไปที่ http://api.twitter.com/oauth/request_token (คลิ๊กเพื่ออ่าน Doc) เพื่อเอา Request Token สำหรับติดต่อกับ Twitter มา โดย Twitter ก็จะตอบมาประมาณนี้
oauth_token=R8bl4GSvcIXeP1C5h1MLVktXbuzdqnlrRR98p53Ps&oauth_token_secret=f6kL0dFrirWgYBxfCT57JdsLftVLgHUO3qOKCOI50&oauth_callback_confirmed=true
(ไม่ต้องจดของผมไปนะครับ มันใช้ไม่ได้ เพราะว่ามัน Gen ใหม่ ทุก Request ครับ)
ทีนี้ถ้าเราจะขอละ ต้องทำยังไง?
ขอให้แน่ใจว่า มี Requirement ครบตามนี้ก่อนครับ:
- เราจะต้องมี Consumer Key, Consumer Secret ก่อน
- เราจะต้องสามารถ คำนวณ Hash แบบ HMAC-SHA1 ได้ (ใน .NET CF ไม่มี แต่ผม โพสโค๊ดเอาไว้ให้แล้ว)
- เราจะต้องคำนวณ Tick แบบ Unix เป็น ซึ่ง Code ไม่ยากครับ ตามนี้ (จริงๆ มันเรียกว่า Unix Timestamp)

- และท้ายที่สุด เราต้องหาวิธี Generate String แบบ Random ขึ้นมา และต้องไม่ซ้ำกันด้วย ง่ายที่สุด ใช้ Guid.NewGuid().ToString() เนี่ยละครับ
สำหรับการขอ เราจะต้องสร้าง Signature เพื่อเอาไว้ป้องกัน Man-in-the-middle attack จะได้ไม่มีใครสามารถมาแอบแก้ข้อมูลเรากลางทางได้ เพราะเราจำเป็นต้องส่งข้อมูลแบบ Clear-text (ทำไมไม่ใช้ HTTPS??!?!?) โดยสร้าง Signature นั้น เราจะต้องสร้างสิ่งที่เรียกส่า Signature Base String ขึ้นมา โดยการเอาคำหลายๆ คำ มาต่อกันด้วยเครื่องหมาย & ตาม Spec ของ OAuth ระบุไว้ดังนี้ครับ
- คำว่า GET หรือ POST ตามแต่ Request ของเรา
- Url ที่เรา Request โดยไม่มี Query String (กล่าวคือ ถ้าใช้ http://myhost/default.aspx?id=5 ใช้แค่ http://myhost/default.aspx) และทำการ UrlEncode เจ้า Url นี่ด้วย เช่น จากตัวอย่าง จะได้ http%3A%2F%2Fmyhost%2Fdefault.aspx
- Query String ทั้งหมด ที่เราใช้ และผ่านการ Normalize แล้ว พร้อม UrlEncode มันด้วย
Normalize???
Normalize ในทีนี้คือ การเอา Query String มาเรียงตามลำดับอักษร เป็นตัวๆ ไป เช่น
z=5&status=this is a test&a=1&b=2&c=3
ก็ต้องจัดการเปลี่ยนมันเป็น (สังเกตว่า มันเรียงจาก a->z)
a=1&b=2&c=3&status=this is a test&z=5
นอกจากนี้ Value ของแต่ละตัว จะต้องทำการ UrlEncode ให้เรียบร้อยก่อนด้วย นั่นก็คือ การให้ Value ผ่าน HttpUtility.UrlEncode ก่อนเอาไปต่อกัน ซึ่งจากตัวอย่างเมื่อกี้ จะเห็นว่ามีช่องว่างใน Parameter status การ UrlEncode ค่าของมัน เราก็จะได้ว่า
a=1&b=2&c=3&status=this%20is%20a%20test&z=5
ยัง ยังไม่จบ! หลังจาก UrlEncode แต่ละ Value แล้ว เราจะต้องเอา QueryString ทั้งหมด มา UrlEncode อีกรอบ ก็จะได้ว่า
a%3D1%26b%3D2%26c%3D3%26status%3Dthis%20is%20a%20test%26z%3D5
งงอ่ะดี๊ มันจะทำไปทำไมฟระ!!!
สรุปว่า ถ้าทำตามตัวอย่าง เราจะได้ Signature Base String ประมาณนี้
GET&http%3A%2F%2Fmyhost%2Fdefault.aspx&a%3D1%26b%3D2%26c%3D3%26status%3Dthis%20is%20a%20test%26z%3D5
เดี๋ยว แล้ว Key อะไรไม่ใช้เหรอ?
ใช้สิครับ นอกจาก Query String ที่เราจะส่งแล้ว จะต้องมี Query String ของ OAuth พวกนีด้วย
- oauth_consumer_key ให้ค่าเป็น Consumer Key ของเรา เช่น oauth_consumer_key=abcd124561234sfqsdf
- oauth_signature_method=HMAC-SHA1
- oauth_nonce ให้ค่าเป็น String Random ที่ไม่ซ้ำกัน เช่น oauth_none=abcd1234
- oauth_timestamp ให้ค่าเป็น Unix Timestamp ของเวลาปัจจุบัน เช่น oauth_timestamp=12700156
- oauth_version=1.0
ซึ่งเมื่ออกมาเต็มๆ แล้ว Signature Base String ของเรา มันจะประมาณนี้
GET&http%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_consumer_key%3D<Consumer Key>%26oauth_nonce%3D9753966%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1270332486%26oauth_version%3D1.0
เมื่อได้ Signature Base String แล้ว ทำการ Sign มัน ด้วย Consumer Secret ที่เรามี ตามด้วยเครื่องหมาย & และ Token Secret แต่ตอนนี้เรายังไม่มี Token Scret ก็ปล่อยว่างไปก่อน คำสั่งจะประมาณนี้

และ hashKey จะมีค่าลักษณะนี้:
ZdvrXXTTZZVJidRX7kQ&
สังเกตว่า หลังจาก Sign เสร็จ ต้องแปลงเป็น Base64 ด้วย ไม่ได้เอา String มาดุ้นๆ
หมดยังเนี่ย!?!?!?
เมื่อมี Signature แล้ว เราก็มา Build URL สำหรับ Request Token ดังนี้
- http://api.twitter.com/oauth/request_token
- ? (เครื่องหมาย ?)
- Query String ตั้งแต่ oauth_consumer_key ถึง oauth_version ตามหัวข้อ เดี๋ยว แล้ว Key อะไรไม่ใช้เหรอ?
- &oauth_signature= Signature ที่เราคำนวณได้ และเก็บอยู่ในตัวแปร sig เมื่อกี้นี้ ต้องเข้า UrlEncode 1 ทีด้วย (myUrl += &oauth_signature= + HttpUtility.UrlEncode(sig) )
ตัวอย่าง:
http://api.twitter.com/oauth/request_token?oauth_consumer_key=&oauth_nonce=9a6a1697-4e0a-4bb0-a793-92623a414667&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1270337030&oauth_version=1.0&oauth_signature=1huEQZk39hqRlG0Pt4JaUtpozfY%3D
เมื่อ Request ไป เราก็จะได้คำตอบกลับมา ประมาณนี้
oauth_token=R8bl4GSvcIXeP1C5h1MLVktXbuzdqnlrRR98p53Ps&oauth_token_secret=f6kL0dFrirWgYBxfCT57JdsLftVLgHUO3qOKCOI50&oauth_callback_confirmed=true
ก็เก็บ oauth_token และ oauth_token_secret ไว้ใช้ครับ เอาละ หยิบพารามากิน 1 เม็ด 
โอเค ได้ Token มาละ ยังไงต่อ?
หลังจากได้ Token มาแล้ว เราจะอยู่ที่ Step B-C ในภาพการทำงานของ OAuth

นั่นก็คือ เราจะต้องสั่งเปิดหน้า Web เพื่อให้ User กด Allow แล้วเราจะได้ PIN มา ตามรูปด้านล่างนี้


เราก็ต้อง Buid URL ขึ้นมาอีกแล้วละ ซึ่ง Url นี้ คล้ายกับขั้นตอน Request_Token โดยสิ่งที่เปลี่ยนไป คือ
- Query String ที่ใช้ในการคำนวณ Hash ให้รวม oauth_token ไปด้วย นั่นคือ Signature Base String จะเป็นประมาณนี้
GET&http%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_consumer_key%3D**********%26oauth_nonce%3D9753966%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1270332486%26oauth_token%3DR8bl4GSvcIXeP1C5h1MLVktXbuzdqnlrRR98p53Ps%26oauth_version%3D1.0
ตัวอักษรสีม่วง ก็คือค่าที่ Twitter Response มาในขั้นตอน Request_Token (อย่าลืมว่า เราต้องเรียงลำดับของ Query String ด้วยนะ)
Url จะประกอบไปด้วย
- http://twitter.com/oauth/authorize
- ? (เครื่องหมาย ?)
- Query String ตั้งแต่ oauth_consumer_key ถึง oauth_version ตามหัวข้อ เดี๋ยว แล้ว Key อะไรไม่ใช้เหรอ? และ oauth_token=R8bl4GSvcIXeP1C5h1MLVktXbuzdqnlrRR98p53Ps
- &oauth_signature= Signature ที่เราคำนวณได้ (คนละ Signature กันกับครั้งแรกแล้ว เพราะ Timestamp, nonce เปลี่ยน และมี oauth_token เพิ่มเข้ามา Signature Base String)
ถ้าทำถูกต้อง ก็จะพบกับหน้า Deny/Allow ตามตัวอย่าง ซึ่งในขั้นตอนนี้ ผมพบว่า คุณจะสร้าง Signature โดยใช้ oauth_token_secret หรือไม่ ก็ได้ Twitter ก็ยังคงให้หน้า Deny/Allow มาตามปกติ อันขัดกับหลักการของ OAuth (หรือว่าผมเข้าใจผิด?) แต่เพื่อความถูกต้อง ก็ควรจะใช้ค่า Consumer Scret และ Oauth_token_secret ทั้งคู่ในการสร้าง Signature
ซึ่ง hashKey จากโค๊ด

จะมีค่าลักษณะนี้ (Consumer Secret ตามด้วย & ตามด้วย oauth_token_secret)
ZdvrXXTTZZVJidRX7kQ&1ZwNGTfFUnKaabu7cicQiRyWRFFXmT09c3BnQ5Ww
หลังจาก User ได้ PIN มาแล้ว เราก็ต้องให้ User กรอก PIN ใส่โปรแกรมของเรา ซึ่งเราไม่จำเป็นต้องบันทึก PIN นี้ไว้ครับ เพราะว่า เราจะใช้ Access Token ไม่ได้ใช้ PIN เราใช้ PIN เพื่อแลก Access Token เท่านั้น
เอา Request Token, PIN ไปแลก Access Token

เหนื่อยไหมครับ? ยังครับ ยังไม่จบ เราต้องเอา PIN ที่ได้ ไปแลก Access Token มาอีกที โดยการสร้าง Url อีกแล้วครับ ตรงนี้แหละ ที่เล่นเอาผมซะงงไปเลย
Url จะประกอบไปด้วย
- http://twitter.com/oauth/access_token
- ? (เครื่องหมาย ?)
- Query String ตั้งแต่ oauth_consumer_key ถึง oauth_version ตามหัวข้อ เดี๋ยว แล้ว Key อะไรไม่ใช้เหรอ? และ oauth_verifier=PIN
- &oauth_signature= Signature ที่เราคำนวณได้ (คนละ Signature กันกับครั้งแรกแล้ว เพราะ Timestamp, nonce เปลี่ยน และมี oauth_verifier เพิ่มเข้ามาใน Signature Base String
ในขั้นตอนนี้ การ Sign ไม่ต้องใช้ oauth_token_secret!!!!
นั่นละครับ ถ้าใส่ลงไป มันจะ 401 มา แล้วไม่บอกเหตุผลด้วย มึนจริงๆ สรุปก็คือ การ Sign ใช้แค่ Consumer Secret อย่างเดียว ตามด้วยเครื่องหมาย & ดังนี้ (ไม่ต้องมีสีม่วงนั่นเอง)
ZdvrXXTTZZVJidRX7kQ&1ZwNGTfFUnKaabu7cicQiRyWRFFXmT09c3BnQ5Ww
และอย่าลืมว่า Signature Base String นั้นมี oauth_verifier รวมอยู่ด้วยครับ นั่นก็คือ Signature Base String มันจะมีค่าลักษณะนี้
GET&http%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_consumer_key%3D**********%26oauth_nonce%3D9753966%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1270332486%26oauth_token%3DR8bl4GSvcIXeP1C5h1MLVktXbuzdqnlrRR98p53Ps%26oauth_verifier=PIN%26oauth_version%3D1.0
เมื่อ Request สำเร็จ เราก็จะได้ Access Token และ Access Token Secret สำหรับใช้ในการส่งค่า และคำนวณ Signature สำหรับ Request ของเราในครั้งต่อๆ ไป ตราบนานเท่านั้น ดังนั้นให้ทำการเซฟค่าของทั้งสองตัวนี้ ลงไฟล์ไว้เลยครับ ครั้งหน้า ถ้าเปิดโปรแกรมขึ้นมาใหม่ ใช้เจ้า Access Token เป็น oauth_token และ Consumer Secret ร่วมกับ Access Token Secret ในการ คำนวน Signature ได้เลย
การส่ง Tweet เข้า Twitter
เอาละ คิดว่าคงจะ Authen ผ่านกันทุกคน ถ้าทำตามจนถึงขึ้นตอนนี้ คุณจะมี
- Consumer Key
- Consumer Secret
- Access Token
- Access Token Secret
เรียบร้อยแล้ว ซึ่งเราจะใช้ตัวแปรทั้ง 4 ตัวนี้ ในการ Request ทุกครั้ง ซึ่ง Request ที่จะต้องใช้ OAuth ก็ได้แก่ Request ที่มีการแก้ไขค่าต่างๆ ของ Twitter ทั้งหลาย เช่น ส่ง Tweet, Follow/Unfollow, Delete, Retweet หรืออะไรก็ตามที่เป็นการ เขียน นั่นเอง
เพื่อเป็นตัวอย่าง ผมจะแสดงขั้นตอนการส่ง Tweet ให้อ่านกัน มันจะมีขั้นตอนดังนี้ครับ
- รับค่า Tweet จาก ผู้ใช้ และทำการ UrlEncode มันซะ เช่น this is a test เมื่อ Url Encode จะได้ว่า this%20is%20a%20test
- สร้าง Signature Base String ดังนี้
POST&http%3A%2F%2Ftwitter.com%2Fstatuses%2Fupdate.xml&oauth_consumer_key%3D**********%26oauth_nonce%3D9753966%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1270332486%26oauth_token%3DR8bl4GSvcIXeP1C5h1MLVktXbuzdqnlrRR98p53Ps%26oauth_version%3D1.0%26status=this%20is%20a%20test
- สร้าง Signature จาก Signature Base String โดยใช้ Consumer Secret และ Access Token Secret เป็น hashKey ดังนี้
ZdvrXXTTZZVJidRX7kQ&1ZwNGTfFUnKaabu7cicQiRyWRFFXmT09c3BnQ5Ww
- เตรียมข้อมูลดังต่อไปนี้ให้พร้อม
- oauth_consumer_key ให้ค่าเป็น Consumer Key ของเรา เช่น oauth_consumer_key=abcd124561234sfqsdf
- oauth_signature_method=HMAC-SHA1
- oauth_nonce ให้ค่าเป็น String Random ที่ไม่ซ้ำกัน เช่น oauth_none=abcd1234
- oauth_timestamp ให้ค่าเป็น Unix Timestamp ของเวลาปัจจุบัน เช่น oauth_timestamp=12700156
- oauth_version=1.0
- oauth_signature= Signature ที่เราคำนวณได้
- สร้าง HttpWebRequest เพื่อส่งข้อมูลแบบ URLEncoded ดังนี้
หมายเหตุ: - ถ้าทำงานสำเร็จ Twitter จะตอบมาเป็น Format ที่เราระบุไว้ ดูรายละเอียดเพิ่มเติมได้จาก http://apiwiki.twitter.com/Twitter-REST-API-Method:-statuses update
เข้าใจดีหรือเปล่า?
ถึงตรงนี้ ผมเชื่อว่า หลายๆ ท่านน่าจะสามารถติดต่อกับ Twitter ได้แล้ว (และรีบๆ ทำโปรแกรม Twitter ออกมาเลย ผมเบื่อ PocketTwit เต็มทน) ถ้ายังไม่เก็ท เดี๋ยวผมจะเอา Framework ของผมขึ้น CodePlex เร็วๆ นี้ ซึ่งจะมีส่วนติดต่อกับ Twitter และ Facebook อยู่ด้วย รอติดตามชมครับ
OAuth Anti-Headache บทความแก้ปวดหัวการติดต่อกับ Twitter แบบใช้ OAuth (.NET Compact Framework)
(บทความนี้ เขียนเพื่อ .NET CF - Windows Mobile แต่ว่าประยุกต์ใช้กับ .NET ธรรมดาได้ครับ เพราะ .NET CF เป็นแค่ Subset ของ .NET ธรรมดาเองนะ ส่วน Silverlight ยังไม่ได้ลองครับ)
ตะกี้ผมแทบจะกระโดดชนหลังคาได้เลยครับ ตอนที่ Debug เจอว่าทำไมเจ้า OAuth ของผม มันไม่ยอมคุยกันรู้เรื่องกับ Twitter ซะที ผมเลยคิดว่า ต้องมีคนปวดหัวกับมันอีกเยอะแน่นอน แต่ไม่ต้องมานั่งปวดหัวเหมือนผมแล้วครับ มาดูดีกว่า ว่าผมทำสำเร็จได้อย่างไร
ผมขอออกตัวก่อนว่า ที่ทำได้นี่ ใช้ดวงไปเกินครึ่ง เพราะ
เอาละ เข้าเรื่องๆ
ทำไมต้อง OAuth
เนื่องจากว่า Twitter เพิ่งจะเปลี่ยน Policy ว่า ถ้าจะให้ชื่อโปรแกรมเราขึ้นอยู่ใน Tweet แบบนี้ เราจะต้องใช้ Protocol OAuth เท่านั้น ในการส่ง Tweet
ถ้าใช้ API อย่างเดียว แล้วใส่ Username/Password เจ้า Twitter ก็จะคิดว่าโปรแกรมเราเป็นแค่บอท แบบนี้

ดังนั้น ขั้นแรกเลย เพื่อให้โปรแกรมเรามีชื่อขึ้นใน Twitter เราก็ต้องไปลงทะเบียนก่อน ตามลิงค์ http://twitter.com/oauth_clients ใส่ข้อมูลให้ครบถ้วน เลือก ตามนี้ด้วยละครับ (ขั้นตอนที่ผมแสดง เป็นของ Client นะครับ Browser ใช้ไม่ได้)
แล้วเราก็จะได้ ConsumerKey กับ Consumer Secret มา เก็บมันไว้ให้ดี (และที่น่าเคืองคือ ถ้า Twitter Support PLAINTEXT signature ผ่าน HTTPS ทุกอย่างจะง่ายกว่านี้!!!!)
เมื่อได้ Key มาแล้ว ก็เตรียมปวดหัวกันต่อได้เลย
รู้จักกับ OAuth และพาราเซตามอล
OAuth ก็คือ Protocol ในการแลกเปลี่ยน User Identity แบบหนึ่ง ซึ่งใช้ระบบ Token กล่าวคือ ถ้าเราจะทำอะไร ในฐานะ User ได้ เราก็ต้องมี Token ที่ยืนยันว่าเราได้รับสิทธิ์จากผู้ใชเสียก่อน ซึ่ง Diagram ด้านล่างนี้ จะอธิบายการทำงานคร่าวๆ แต่อ่านไม่รู้เรื่องหรอกครับ ผ่านมันไปก่อนเถอะ (ทำได้แล้ว กลับมาอ่าน ถึงจะเข้าใจ)
จาก Diagram สรุปว่า ในขั้นแรก เราจะต้อง Request ไปที่ http://api.twitter.com/oauth/request_token (คลิ๊กเพื่ออ่าน Doc) เพื่อเอา Request Token สำหรับติดต่อกับ Twitter มา โดย Twitter ก็จะตอบมาประมาณนี้
oauth_token=R8bl4GSvcIXeP1C5h1MLVktXbuzdqnlrRR98p53Ps&oauth_token_secret=f6kL0dFrirWgYBxfCT57JdsLftVLgHUO3qOKCOI50&oauth_callback_confirmed=true
(ไม่ต้องจดของผมไปนะครับ มันใช้ไม่ได้ เพราะว่ามัน Gen ใหม่ ทุก Request ครับ)
ทีนี้ถ้าเราจะขอละ ต้องทำยังไง?
ขอให้แน่ใจว่า มี Requirement ครบตามนี้ก่อนครับ:
สำหรับการขอ เราจะต้องสร้าง Signature เพื่อเอาไว้ป้องกัน Man-in-the-middle attack จะได้ไม่มีใครสามารถมาแอบแก้ข้อมูลเรากลางทางได้ เพราะเราจำเป็นต้องส่งข้อมูลแบบ Clear-text (ทำไมไม่ใช้ HTTPS??!?!?) โดยสร้าง Signature นั้น เราจะต้องสร้างสิ่งที่เรียกส่า Signature Base String ขึ้นมา โดยการเอาคำหลายๆ คำ มาต่อกันด้วยเครื่องหมาย & ตาม Spec ของ OAuth ระบุไว้ดังนี้ครับ
Normalize???
Normalize ในทีนี้คือ การเอา Query String มาเรียงตามลำดับอักษร เป็นตัวๆ ไป เช่น
z=5&status=this is a test&a=1&b=2&c=3
ก็ต้องจัดการเปลี่ยนมันเป็น (สังเกตว่า มันเรียงจาก a->z)
a=1&b=2&c=3&status=this is a test&z=5
นอกจากนี้ Value ของแต่ละตัว จะต้องทำการ UrlEncode ให้เรียบร้อยก่อนด้วย นั่นก็คือ การให้ Value ผ่าน HttpUtility.UrlEncode ก่อนเอาไปต่อกัน ซึ่งจากตัวอย่างเมื่อกี้ จะเห็นว่ามีช่องว่างใน Parameter status การ UrlEncode ค่าของมัน เราก็จะได้ว่า
a=1&b=2&c=3&status=this%20is%20a%20test&z=5
ยัง ยังไม่จบ! หลังจาก UrlEncode แต่ละ Value แล้ว เราจะต้องเอา QueryString ทั้งหมด มา UrlEncode อีกรอบ ก็จะได้ว่า
a%3D1%26b%3D2%26c%3D3%26status%3Dthis%20is%20a%20test%26z%3D5
งงอ่ะดี๊ มันจะทำไปทำไมฟระ!!!
สรุปว่า ถ้าทำตามตัวอย่าง เราจะได้ Signature Base String ประมาณนี้
GET&http%3A%2F%2Fmyhost%2Fdefault.aspx&a%3D1%26b%3D2%26c%3D3%26status%3Dthis%20is%20a%20test%26z%3D5
เดี๋ยว แล้ว Key อะไรไม่ใช้เหรอ?
ใช้สิครับ นอกจาก Query String ที่เราจะส่งแล้ว จะต้องมี Query String ของ OAuth พวกนีด้วย
ซึ่งเมื่ออกมาเต็มๆ แล้ว Signature Base String ของเรา มันจะประมาณนี้
GET&http%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_consumer_key%3D<Consumer Key>%26oauth_nonce%3D9753966%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1270332486%26oauth_version%3D1.0
เมื่อได้ Signature Base String แล้ว ทำการ Sign มัน ด้วย Consumer Secret ที่เรามี ตามด้วยเครื่องหมาย & และ Token Secret แต่ตอนนี้เรายังไม่มี Token Scret ก็ปล่อยว่างไปก่อน คำสั่งจะประมาณนี้
และ hashKey จะมีค่าลักษณะนี้:
ZdvrXXTTZZVJidRX7kQ&
สังเกตว่า หลังจาก Sign เสร็จ ต้องแปลงเป็น Base64 ด้วย ไม่ได้เอา String มาดุ้นๆ
หมดยังเนี่ย!?!?!?
เมื่อมี Signature แล้ว เราก็มา Build URL สำหรับ Request Token ดังนี้
ตัวอย่าง:
http://api.twitter.com/oauth/request_token?oauth_consumer_key= &oauth_nonce=9a6a1697-4e0a-4bb0-a793-92623a414667&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1270337030&oauth_version=1.0&oauth_signature=1huEQZk39hqRlG0Pt4JaUtpozfY%3D
เมื่อ Request ไป เราก็จะได้คำตอบกลับมา ประมาณนี้
oauth_token=R8bl4GSvcIXeP1C5h1MLVktXbuzdqnlrRR98p53Ps&oauth_token_secret=f6kL0dFrirWgYBxfCT57JdsLftVLgHUO3qOKCOI50&oauth_callback_confirmed=true
ก็เก็บ oauth_token และ oauth_token_secret ไว้ใช้ครับ เอาละ หยิบพารามากิน 1 เม็ด
โอเค ได้ Token มาละ ยังไงต่อ?
หลังจากได้ Token มาแล้ว เราจะอยู่ที่ Step B-C ในภาพการทำงานของ OAuth
นั่นก็คือ เราจะต้องสั่งเปิดหน้า Web เพื่อให้ User กด Allow แล้วเราจะได้ PIN มา ตามรูปด้านล่างนี้
เราก็ต้อง Buid URL ขึ้นมาอีกแล้วละ ซึ่ง Url นี้ คล้ายกับขั้นตอน Request_Token โดยสิ่งที่เปลี่ยนไป คือ
GET&http%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_consumer_key%3D**********%26oauth_nonce%3D9753966%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1270332486%26oauth_token%3DR8bl4GSvcIXeP1C5h1MLVktXbuzdqnlrRR98p53Ps%26oauth_version%3D1.0
ตัวอักษรสีม่วง ก็คือค่าที่ Twitter Response มาในขั้นตอน Request_Token (อย่าลืมว่า เราต้องเรียงลำดับของ Query String ด้วยนะ)
Url จะประกอบไปด้วย
ถ้าทำถูกต้อง ก็จะพบกับหน้า Deny/Allow ตามตัวอย่าง ซึ่งในขั้นตอนนี้ ผมพบว่า คุณจะสร้าง Signature โดยใช้ oauth_token_secret หรือไม่ ก็ได้ Twitter ก็ยังคงให้หน้า Deny/Allow มาตามปกติ อันขัดกับหลักการของ OAuth (หรือว่าผมเข้าใจผิด?) แต่เพื่อความถูกต้อง ก็ควรจะใช้ค่า Consumer Scret และ Oauth_token_secret ทั้งคู่ในการสร้าง Signature
ซึ่ง hashKey จากโค๊ด
จะมีค่าลักษณะนี้ (Consumer Secret ตามด้วย & ตามด้วย oauth_token_secret)
ZdvrXXTTZZVJidRX7kQ&1ZwNGTfFUnKaabu7cicQiRyWRFFXmT09c3BnQ5Ww
หลังจาก User ได้ PIN มาแล้ว เราก็ต้องให้ User กรอก PIN ใส่โปรแกรมของเรา ซึ่งเราไม่จำเป็นต้องบันทึก PIN นี้ไว้ครับ เพราะว่า เราจะใช้ Access Token ไม่ได้ใช้ PIN เราใช้ PIN เพื่อแลก Access Token เท่านั้น
เอา Request Token, PIN ไปแลก Access Token
เหนื่อยไหมครับ? ยังครับ ยังไม่จบ เราต้องเอา PIN ที่ได้ ไปแลก Access Token มาอีกที โดยการสร้าง Url อีกแล้วครับ ตรงนี้แหละ ที่เล่นเอาผมซะงงไปเลย
Url จะประกอบไปด้วย
ในขั้นตอนนี้ การ Sign ไม่ต้องใช้ oauth_token_secret!!!!
นั่นละครับ ถ้าใส่ลงไป มันจะ 401 มา แล้วไม่บอกเหตุผลด้วย มึนจริงๆ สรุปก็คือ การ Sign ใช้แค่ Consumer Secret อย่างเดียว ตามด้วยเครื่องหมาย & ดังนี้ (ไม่ต้องมีสีม่วงนั่นเอง)
ZdvrXXTTZZVJidRX7kQ&
1ZwNGTfFUnKaabu7cicQiRyWRFFXmT09c3BnQ5Wwและอย่าลืมว่า Signature Base String นั้นมี oauth_verifier รวมอยู่ด้วยครับ นั่นก็คือ Signature Base String มันจะมีค่าลักษณะนี้
GET&http%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_consumer_key%3D**********%26oauth_nonce%3D9753966%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1270332486%26oauth_token%3DR8bl4GSvcIXeP1C5h1MLVktXbuzdqnlrRR98p53Ps%26oauth_verifier=PIN%26oauth_version%3D1.0
เมื่อ Request สำเร็จ เราก็จะได้ Access Token และ Access Token Secret สำหรับใช้ในการส่งค่า และคำนวณ Signature สำหรับ Request ของเราในครั้งต่อๆ ไป ตราบนานเท่านั้น ดังนั้นให้ทำการเซฟค่าของทั้งสองตัวนี้ ลงไฟล์ไว้เลยครับ ครั้งหน้า ถ้าเปิดโปรแกรมขึ้นมาใหม่ ใช้เจ้า Access Token เป็น oauth_token และ Consumer Secret ร่วมกับ Access Token Secret ในการ คำนวน Signature ได้เลย
การส่ง Tweet เข้า Twitter
เอาละ คิดว่าคงจะ Authen ผ่านกันทุกคน ถ้าทำตามจนถึงขึ้นตอนนี้ คุณจะมี
เรียบร้อยแล้ว ซึ่งเราจะใช้ตัวแปรทั้ง 4 ตัวนี้ ในการ Request ทุกครั้ง ซึ่ง Request ที่จะต้องใช้ OAuth ก็ได้แก่ Request ที่มีการแก้ไขค่าต่างๆ ของ Twitter ทั้งหลาย เช่น ส่ง Tweet, Follow/Unfollow, Delete, Retweet หรืออะไรก็ตามที่เป็นการ เขียน นั่นเอง
เพื่อเป็นตัวอย่าง ผมจะแสดงขั้นตอนการส่ง Tweet ให้อ่านกัน มันจะมีขั้นตอนดังนี้ครับ
POST&http%3A%2F%2Ftwitter.com%2Fstatuses%2Fupdate.xml&oauth_consumer_key%3D**********%26oauth_nonce%3D9753966%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1270332486%26oauth_token%3DR8bl4GSvcIXeP1C5h1MLVktXbuzdqnlrRR98p53Ps%26oauth_version%3D1.0%26status=this%20is%20a%20test
ZdvrXXTTZZVJidRX7kQ&1ZwNGTfFUnKaabu7cicQiRyWRFFXmT09c3BnQ5Ww
หมายเหตุ:
เข้าใจดีหรือเปล่า?
ถึงตรงนี้ ผมเชื่อว่า หลายๆ ท่านน่าจะสามารถติดต่อกับ Twitter ได้แล้ว (และรีบๆ ทำโปรแกรม Twitter ออกมาเลย ผมเบื่อ PocketTwit เต็มทน) ถ้ายังไม่เก็ท เดี๋ยวผมจะเอา Framework ของผมขึ้น CodePlex เร็วๆ นี้ ซึ่งจะมีส่วนติดต่อกับ Twitter และ Facebook อยู่ด้วย รอติดตามชมครับ