มุมนักพัฒนา > พัฒนาหน้า SSORegister ด้วยโปรโตคอล OAuth

การพัฒนาหน้า SSORegiser ด้วยโปรโตคอล OAuth

ภาพรวม

OAuth เป็นโปรโตคอลที่ใช้ในการร้องขอข้อมูลจาก Remote Server ซึ่งไม่มีการจำกัดประเภทของข้อมูล สามารถเป็นได้ทั้งรูปภาพ, ไฟล์เสียง หรือข้อมูลที่เป็น Text โดยทางสพร. ได้เอาโปรโตคอลนี้มาปรับใช้ในการส่งข้อมูลส่วนตัวของผู้ใช้งานระบบยืนยันตัวบุคคลสำหรับเจ้าหน้าที่รัฐ (Government ID) กลับมาในรูปแบบของ XML ดังนั้นระบบบริการสำหรับเจ้าหน้าที่รัฐ (e-Service for Government Officer) ต้องทำการพัฒนาชุดคำสั่งหรือระบบ OAuth Consumer เพื่อใช้ในการร้องขอข้อมูลผู้ใช้จากระบบยืนยันตัวบุคคลสำหรับเจ้าหน้าที่รัฐ (Government ID) (ซึ่งทำหน้าที่เป็น OAuth Provider) ซึ่งมีขั้นตอนการทำงานดังนี้
  1. ระบบบริการสำหรับเจ้าหน้าที่รัฐร้องขอ "Request Token" จาก OAuth Provider
  2. OAuth Provider ส่ง "Request Token" กลับมา
  3. ผู้ใช้ตัดสินใจว่าจะอนุญาติให้ระบบบริการสำหรับเจ้าหน้าที่รัฐสามารถเข้าถึงข้อมูลของผู้ใช้ได้หรือไม่
  4. OAuth Provider ส่งผู้ใช้กลับไปยังระบบบริการสำหรับเจ้าหน้าที่รัฐ
  5. ระบบบริการสำหรับเจ้าหน้าที่รัฐร้องขอ “Access Token”
  6. OAuth Provider แลก “Request Token” เป็น “Access Token” และส่งกลับมา
  7. ระบบบริการสำหรับเจ้าหน้าที่รัฐใช้ “Access Token” ดึงข้อมูลของผู้ใช้

การขอ Request Token จาก OAuth Provider

ที่มา : http://oauth.net/core/1.0/

Parameter Description
1. ระบบบริการสำหรับเจ้าหน้าที่รัฐร้องขอ "Request Token" จาก OAuth Provider
oauth_consumer_key "key" ที่ทางสพร.ส่งให้
oauth_signature_method วิธีการ Sign Request (ควรใช้ค่าเป็น HMAC-SHA1)
oauth_signature ค่าที่ในการ Sign Request ตาม oauth_signature_method (โดยทั่วไป parameter นี้จะถูกตั้งค่าให้อัตโนมัติในขั้นตอนสร้าง Request ของแต่ละไลบรารี่)
oauth_timestamp เวลาที่ทำการ Request
oauth_nonce เป็นชุดของตัวหนังสือภาษาอังกฤษที่ถูกสุ่มขึ้นมาให้ไม่ซ้ำกันในแต่ละ Request ของแต่ละระบบบริการสำหรับเจ้าหน้าที่รัฐ เพื่อเอาไว้ตรวจสอบว่า Request นี้เป็น Request ที่ไม่เคยถูกใช้มาก่อน และป้องกันการโจมตีผ่าน HTTP
oauth_version เวอร์ชั่นของ OAuth
oauth_callback URL ที่จะให้ส่ง “Request Token” กลับไป
2. OAuth Provider ส่ง "Request Token" กลับมา
oauth_token “Request Token” จากระบบข้อมูลผู้ใช้
oauth_token_secret เป็นค่าที่ระบบข้อมูลผู้ใช้ส่งมาพร้อมกับ “Request Token” เพื่อใช้ในการตรวจสอบ “Request Token” โดยค่านี้จะไม่ซ้ำกันในแต่ละ “Request Token”
oauth_callback_confirmed เป็น True ถ้าได้รับการยืนยันจาก OAuth Provider
3. ผู้ใช้ตัดสินใจว่าจะอนุญาติให้ระบบบริการสำหรับเจ้าหน้าที่รัฐสามารถเข้าถึงข้อมูลของผู้ใช้ได้หรือไม่
oauth_token “Request Token” จากระบบข้อมูลผู้ใช้ (ได้มาจากขั้นตอนที่ 2)
4. OAuth Provider ส่งผู้ใช้กลับไปยังระบบบริการสำหรับเจ้าหน้าที่รัฐ
oauth_token “Request Token” จากขั้นตอน B (ในขั้นตอนนี้ “Request Token” ได้รับการอนุญาตให้ใช้งานได้จากระบบข้อมูลผู้ใช้แล้ว)
oauth_verifier เป็นค่าที่ระบบข้อมูลผู้ใช้ส่งมาพร้อมกับ “Request Token” โดยค่านี้จะมีความเชื่อมโยงกับระบบบริการสำหรับเจ้าหน้าที่รัฐ ค่านี้ถูกใช้ในขั้นตอน E เพื่อยืนยันว่าระบบบริการสำหรับเจ้าหน้าที่รัฐที่จะขอ “Access Token” นั้นเป็นระบบบริการสำหรับเจ้าหน้าที่รัฐเดียวกับที่ขอ “Request Token”
5. ระบบบริการสำหรับเจ้าหน้าที่รัฐร้องขอ “Access Token”
oauth_consumer_key “key” ที่ทางสพร.ส่งให้
oauth_token “Request Token” ในขั้นตอน D
oauth_signature_method วิธีการเข้ารหัส Request (ต้องใสค่าเป็น HMAC-SHA1)
oauth_signature ค่าที่ได้จากขั้นตอนการเข้ารหัส ตาม oauth_signature_method ค่าในขั้นตอนนี้จะไม่เหมือนค่าในขั้นตอน A (โดยทั่วไป parameter นี้จะถูกตั้งค่าให้อัตโนมัติในขั้นตอนสร้าง Request ของแต่ละไลบรารี่)
oauth_timestamp เวลาที่ทำการ Request
oauth_nonce เป็นชุดของตัวหนังสือภาษาอังกฤษที่ถูกสุ่มขึ้นมาให้ไม่ซ้ำกันในแต่ละ Request ของแต่ละระบบบริการสำหรับเจ้าหน้าที่รัฐ เพื่อเอาไว้ตรวจสอบว่า Request นี้เป็น Request ที่ไม่เคยถูกใช้มาก่อน และป้องกันการโจมตีผ่าน HTTP
oauth_version เวอร์ชั่นของ OAuth
oauth_verifier ค่าที่ได้จากระบบข้อมูลผู้ใช้ในขั้นตอน D
6. OAuth Provider แลก “Request Token” เป็น “Access Token” และส่งกลับมา
oauth_token “Access Token” ที่ได้รับจากระบบเว็บไซต์กลาง
oauth_token_secret เป็นค่าที่ระบบข้อมูลผู้ใช้ส่งมาพร้อมกับ “Access Token” เพื่อใช้ในการตรวจสอบ “Access Token” โดยค่านี้จะไม่ซ้ำกันในแต่ละ “Access Token”
7. ระบบบริการสำหรับเจ้าหน้าที่รัฐใช้ “Access Token” ดึงข้อมูลของผู้ใช้
นำ “Access Token” ที่ได้ไปเข้าถึงข้อมูลของผู้ใช้ โดยทางสพร.ได้ปรับวิธีการเข้าถึงข้อมูลเพื่อให้ได้ข้อมูลกลับมาในรูปแบบของ xml โดยผู้พัฒนาสามารถเข้าถึง xml ผ่าน URL : “http://www.egov.go.th/XmlUserInfo.aspx?AccessToken={Access Token ที่ระบบบริการสำหรับเจ้าหน้าที่รัฐได้รับ}” โดย “Access Token” มีอายุการใช้งาน 10 นาที
<? Xml version=“1.0” encoding="UTF-8"?>
<Member type="Citizen">
    <UserID>a4d8b6a0-37db-4f44-b598fb0e6550a31f</UserID>
    <UserName>TestVender3</UserName>
    <Title VerifiedLevel="Unverified"/>
    <FullName VerifiedLevel="Unverified">Vender3 Test</FullName>
    <FirstName VerifiedLevel="Unverified">Vender3</FirstName>
    <LastName VerifiedLevel="Unverified">Test</LastName>
    <DateOfBirth VerifiedLevel="Unverified"/>
    <Gender VerifiedLevel="Unverified"/>
    <Identification>
        <Code VerifiedLevel="Unverified"/>
        <IssueBy VerifiedLevel="Unverified"/>
        <IssueDate VerifiedLevel="Unverified"/>
        <ExpireDate VerifiedLevel="Unverified"/>
    </Identification>
    <Nationality VerifiedLevel="Unverified"/>
    <Occupation VerifiedLevel="Unverified"/>
    <Address>
        <HouseNumber VerifiedLevel="Unverified"/>
        <VillageName VerifiedLevel="Unverified"/>
        <Moo VerifiedLevel="Unverified"/>
        <Soi VerifiedLevel="Unverified"/>
        <Road VerifiedLevel="Unverified"/>
        <SubDistrict VerifiedLevel="Unverified"/>
        <District VerifiedLevel="Unverified"/>
        <Province VerifiedLevel="Unverified"/>
        <PostCode VerifiedLevel="Unverified"/>
        <GeoCode VerifiedLevel="Unverified"/>
    </Address>
    <ContactInfo>
        <Telephone VerifiedLevel="Unverified"/>
        <Mobilephone VerifiedLevel="Unverified"/>
        <EMail VerifiedLevel="Unverified">vender3@test.com</EMail>
    </ContactInfo>
</Member>

                                
using GITSSSO;
using System.Xml.XPath;
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; 
using DotNetOpenAuth.OpenId.RelyingParty;
using OpenIdRelyingPartyWebForms;
using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth;
using DotNetOpenAuth.ApplicationBlock;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;
using EGA.EGA_CAS.Util;
using EGA.EGA_CAS.Util.Entity;
using EGA.EGA_CAS.SSO.Library;

public class SSORegister : System.Web.UI.Page
{
    //แก้ 2 ตัวนี้ตามชื่อ e-service ต่างๆ
    private const string consumerKey = "";
    private const string consumerSecret = SecretUtil.encodeSecret("");

    #region ตรงนี้เป็น code ที่ไว้ใช้จัดการกับเรื่องการทำ OpenID นะครับ ไม่จำเป็นอย่าแก้
    
    private const string oAuthUrl = "http://testopenid.dga.or.th/OAuth.ashx";
    private const string xmlUrl = "http://www.egov.go.th/XmlUserInfo.aspx";
    
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            //OAuth
            if (Session["WcfTokenManager"] != null)
            {
                WebConsumer consumer = this.CreateConsumer();
                AuthorizedTokenResponse accessTokenMessage = consumer.ProcessUserAuthorization();
                if (accessTokenMessage != null)
                {
                    Session["WcfAccessToken"] = accessTokenMessage.AccessToken;
                    Session.Remove("WcfTokenManager");
                    string path = String.Format("{0}?AccessToken={1}", xmlUrl
                    , Server.UrlEncode(accessTokenMessage.AccessToken));
                    
                    SSOUserInfo ssoUI = new SSOUserInfo(path);

                    // ฟังก์ชั่นสำหรับนำข้อมูลที่ได้ไปใช้งาน ขึ้นอยู่กับแต่ละ Service
                    this.bindValueToPage(ssoUI);
                }
                else
                {
                    WebConsumer consumer = this.CreateConsumer();
                    UriBuilder callback = new UriBuilder(Request.Url);
                    callback.Query = null;

                    Dictionary<string, string> requestParams = new Dictionary<string, string>();
                    requestParams.Add("scope", "");

                    UserAuthorizationRequest response = consumer.PrepareRequestUserAuthorization(
                                                        callback.Uri, requestParams, null);
                    consumer.Channel.Send(response);
                }
            }
            else
            {
                WebConsumer consumer = this.CreateConsumer();
                UriBuilder callback = new UriBuilder(Request.Url);
                callback.Query = null;
                
                Dictionary<string, string> requestParams = new Dictionary<string, string>();
                requestParams.Add("scope", "");

                UserAuthorizationRequest response = consumer.PrepareRequestUserAuthorization(
                                                    callback.Uri, requestParams, null);
                consumer.Channel.Send(response);
            }

            //ของระบบเดิม
            //string userIdentifier = State.ProfileFields.Nickname;
            //int userType = this.ConvertToInt(State.ProfileFields.PostalCode);
            //SSOUserInfo ssoUI = new SSOUserInfo(userIdentifier, userType);

            //this.bindValueToPage(ssoUI);
        }
    }

    private int ConvertToInt(string input)
    {
        int result = 0;
        try
        {
            result = Convert.ToInt32(input);
        }
        catch
        {
            result = 1;
        }
        return result;
    }

    private WebConsumer CreateConsumer()
    {
        InMemoryTokenManager tokenManager = Session["WcfTokenManager"] as InMemoryTokenManager;
        if (tokenManager == null)
        {
            tokenManager = new InMemoryTokenManager(consumerKey, consumerSecret);
            Session["WcfTokenManager"] = tokenManager;
        }
        MessageReceivingEndpoint oauthEndpoint = new MessageReceivingEndpoint(
            new Uri(oAuthUrl),
            HttpDeliveryMethods.PostRequest);

        ServiceProviderDescription spd = new ServiceProviderDescription();
        spd.RequestTokenEndpoint = oauthEndpoint;
        spd.UserAuthorizationEndpoint = oauthEndpoint;
        spd.AccessTokenEndpoint = oauthEndpoint;
        spd.ProtocolVersion = DotNetOpenAuth.OAuth.ProtocolVersion.V10a;
        spd.TamperProtectionElements = new DotNetOpenAuth.Messaging
            .ITamperProtectionChannelBindingElement[] {
                new HmacSha1SigningBindingElement(),
            };

        WebConsumer consumer = new WebConsumer(spd, tokenManager);
        return consumer;
    }

    #endregion
}
                        

บทความที่เกี่ยวข้อง

1. คุณสมบัติขององค์ประกอบที่ต้องได้รับการพัฒนาในระบบ e-Service for Government Officer

2. การพัฒนาหน้า SSOLogin ด้วยโปรโตคอล OpenID


สงวนลิขสิทธิ์ พ.ศ. 2554 ตามพระราชบัญญัติลิขสิทธิ์ 2537 สำนักงานพัฒนารัฐบาลดิจิทัล (องค์การมหาชน) (สพร.)