การพัฒนาหน้า SSOLogin ด้วยโปรโตคอล OpenID
ภาพรวม
OpenId เป็นโปรโตคอลที่ใช้ในการยืนยันตัวบุคคล โดยองค์ประกอบของ OpenID นั้นมีด้วยกัน 2 ส่วน ได้แก่ เว็บผู้ให้บริการยืนยันตัวบุคคล (OpenID Provider)
และเว็บผู้รับบริการ (OpenID Relying Party) ซึ่งขั้นตอนการทำงานมีดังนี้
- เว็บผู้รับบริการส่งคำร้องขอยืนยันตัวบุคคล(OpenID Request) ที่มีรายละเอียดของข้อมูลผู้ใช้ที่ต้องการ(Attribute) ไปยังผู้ให้บริการยืนยันตัวบุคคล
- เว็บผู้ให้บริการยืนยันตัวบุคคลแสดงหน้า Login ให้ผู้ใช้ทำการ Login
- เว็บผู้ให้บริการยืนยันตัวบุคคลส่งข้อมูลผู้ใช้ที่ Login ตาม Attribute ที่เว็บผู้รับบริการร้องขอ
- เว็บผู้รับบริการนำข้อมูลที่ได้ไปทำการ Login ให้ผู้ใช้ในเว็บของตน
ในปัจจุบันทาง สพร. ได้พัฒนาระบบยืนยันตัวบุคคลสำหรับเจ้าหน้าที่รัฐ (Government ID) ซึ่งทำหน้าที่เป็น OpenID Provider เพื่อให้ระบบบริการสำหรับเจ้าหน้าที่รัฐ (e-Service for Government Officer)
ซึ่งในที่นี่เป็น OpenID Relying Party สามารถนำข้อมูลการยืนยันตัวตนนี้ ไปอนุญาตให้ผู้ใช้ที่ยีนยันตัวตนแล้วสามารถเข้าใช้ระบบบริการสำหรับเจ้าหน้าที่รัฐ (e-Service for Government Officer) ได้
รายละเอียดเพิ่มเติมสามารถดูได้ที่ OpenID.net
OpenID Request
ทางสพร. ได้ใช้มาตรฐานการรับ OpenID Request และ ส่ง OpenID Response ตามเอกสาร OpenID Authentication 2.0 - Final
และใช้มาตรฐานการรับส่งข้อมูลผู้ใช้แบบ Attribute Exchange ตามเอกสาร OpenID Attribute Exchange 1.0 - Final
โดยทางสพร. ได้เพิ่ม Attribute บางตัวจาก Attribute มาตรฐาน เรียกว่า "Attribute Alias" เพื่อให้ใช้งานได้ครอบคุมขึ้น ซึ่งมี Parameter และ Attribute ที่ใช้ในการทำ OpenID Request ดังนี้
http://localhost:1807/EGA.EGA_CAS.Security.eAuthen/server.aspx
?openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select
&openid.identity=http://specs.openid.net/auth/2.0/identifier_select
&openid.assoc_handle=nixR!IAAAAB56GYx_ecAUdoR3qhyvqAi9MZxO-Ahl00nqoeUcrtnQQQAAAAGNVREK0g4S
_Zb8IgSlb96sYGjE4AYbskGBPXGmwQ3yrBvH2CmwGL6bIW_6m2Ibh0cN5Zt5rl0hhM4hvHj6HDlZ
&openid.return_to=http://localhost:4856/loginProgrammatic.aspx?dnoa.userSuppliedIdentifier=
http%3A%2F%2Flocalhost%3A1807%2FEGA.EGA_CAS.Security.eAuthen%2F
&openid.realm=http://localhost:4856/
&openid.mode=checkid_setup
&openid.ns=http://specs.openid.net/auth/2.0
&openid.ns.alias3=http://openid.net/srv/ax/1.0
&openid.alias3.required=alias1,alias2,alias3,alias4
&openid.alias3.mode=fetch_request
&openid.alias3.type.alias1=http://axschema.org/contact/email
&openid.alias3.count.alias1=1
&openid.alias3.type.alias2=http://axschema.org/namePerson
&openid.alias3.count.alias2=1
&openid.alias3.type.alias3=http://axschema.org/namePerson/friendly
&openid.alias3.count.alias3=1
|
Parameter |
Description |
Basic OpenId parameter : |
openid.claimed_id |
(ไม่จำเป็นต้องมี) เป็น ID ของ OpenID ของผู้ใช้ ถ้าไม่ทราบให้กำหนดค่าเป็น “http://specs.openid.net/auth/2.0/identifier_select”
|
openid.identity |
(ไม่จำเป็นต้องมี) เป็น ID ของ OpenID ของผู้ใช้บนระบบยืนยันตัวบุคคลกลาง ถ้าไม่ทราบให้กำหนดค่าเป็น “http://specs.openid.net/auth/2.0/identifier_select”
|
openid.assoc_handle |
(ไม่จำเป็นต้องมี) เป็นค่าที่กำหนดขึ้นเพื่อให้ระบบยืนยันตัวบุคคลกลางใช้ในการ Sign OpenID Response โดยถ้าค่านี้ไม่ถูกกำหนด ระบบบริการสำหรับเจ้าหน้าที่รัฐต้องทำการตรวจสอบ
OpenID Response กับระบบยืนยันตัวบุคคลกลางอีกครั้ง
|
openid.return_to |
(ไม่จำเป็นต้องมี) เป็น URL ที่ระบบยืนยันตัวบุคคลกลางจะส่งผู้ใช้กลับมาหลังจากผู้ใช้งานทำการลงชื่อเข้าใช้แล้ว
|
openid.realm |
(ไม่จำเป็นต้องมี) เป็นค่าที่แจ้งให้ระบบยืนยันตัวบุคคลกลางทราบว่าผู้ใช้ทำการ Login เข้าระบบบริการสำหรับเจ้าหน้าที่รัฐใด ต้องกำหนดถ้าไม่ได้กำหนด openid.return_to
|
openid.mode |
(ต้องมี) เป็นการบอกให้ OpenId Provider รู้ว่า OpenId Provider นี้สามารถติดต่อกับผู้ใช้ได้หรือไม่ โดยมีค่าดังนี้
- “checked_immediate” (ไม่ให้ติดต่อกับผู้ใช้)
- “checked_setup”(ให้ติดต่อกับผู้ใช้ได้)
|
openid.ns |
(ต้องมี) เป็นการบอก verison ของ OpenId Request โดยระบบยืนยันตัวบุคคลกลางใช้งานรองรับ OpenID 2.0 ระบบบริการสำหรับเจ้าหน้าที่รัฐจึงควรใช้ Relying Party
ไลบรารี่ที่รองรับ OpenID 2.0 แล้วใส่ค่าเป็น “http://specs.openid.net/auth/2.0”
|
Attribute Exchange Extension : |
openid.ns.<extension_alias> |
(ต้องมี) เป็นการบอกระบบยืนยันตัวบุคคลกลางให้ส่งค่าคืนมาในรูปแบบของ Attribute Exchange (ระบบยืนยันตัวบุคคลกลาง สนับสนุนการส่งค่าคืนในรูปแบบ Attribute Exchange เท่านั้น)
ค่านี้ต้องถูกตั้งเป็น “http://openid.net/srv/ax/1.0”
|
openid.<extension_alias>.required |
(ต้องมี) เป็นการบอกระบบยืนยันตัวบุคคลกลางว่าระบบบริการสำหรับเจ้าหน้าที่รัฐต้องการ attribute อะไรคืนไปบ้าง โดยทุก attribute ต้องมีค่าจริงเพื่อให้ใช้งานได้ครอบคลุม
|
openid.<extension_alias>.mode |
(ต้องมี) เป็นค่าบังคับเพื่อใช้งาน Attribute Exchange ค่านี้ต้องถูกตั้งเป็น “fetch_request”
|
openid.<extension_alias>.type.<attribute_alias> |
(ต้องมี) เป็นการบอกระบบยืนยันตัวบุคคลกลางว่าระบบบริการสำหรับเจ้าหน้าที่รัฐมีการส่งชื่อ attribute อะไรบ้างโดยชื่อ attribute_alias ต้องถูกกำหนดใน openid.<extension_alias>.required
ถ้าระบบบริการสำหรับเจ้าหน้าที่รัฐต้องการ attribute นั้น
|
Attribute |
ค่าที่ส่งคืน |
http://axschema.org/contact/email |
e-mail ของผู้ใช้ |
http://axschema.org/namePerson |
ชื่อ-นามสกุล ของผู้ใช้ |
http://axschema.org/namePerson/friendly |
User name ในระบบ e-portal ของผู้ใช้ |
http://www.egov.go.th/2012/identifier/citizenid |
เลขประจำตัวประชาชน |
http://www.egov.go.th/2012/identifier/juristicId |
เลขนิติบุคคล |
http://www.egov.go.th/2012/identifier/passportnumber |
เลขที่หนังสือเดินทาง |
http://www.egov.go.th/2012/identifier/usertype |
ประเภทของผู้ใช้ ในระบบ e-portal
- ค่า 1 : บุคคลธรรมดา
- ค่า 2 : นิติบุคคล
- ค่า 3 : ชาวต่างชาติ
- ค่า 4 : เจ้าหน้าที่ของรัฐ
|
http://www.egov.go.th/2012/identifier/identity |
รหัสยืนยัน โดยรูปแบบขึ้นอยู่กับประเภทของผู้ใช้ดังนี้
- บุคคลธรรมดา : เลขประจำตัวประชาชน
- นิติบุคคล : เลขนิติบุคคล
- ชาวต่างชาติ : เลขที่หนังสือเดินทาง
- เจ้าหน้าที่ของรัฐ : เลขประจำตัวประชาชน
|
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
using DotNetOpenAuth.OpenId.RelyingParty;
using OpenIdRelyingPartyWebForms;
public class SSOLogin : System.Web.UI.Page
{
private const string OpenIdProviderURL = "https://testopenid.dga.or.th/";
private static OpenIdRelyingParty relyingParty = new OpenIdRelyingParty();
static SSOLogin()
{
// Configure the RP to only allow assertions from our trusted OP endpoint.
EndpointSelector ep = new EndpointSelector(EndpointFilter);
relyingParty.EndpointFilter = ep;
}
protected static bool EndpointFilter(IProviderEndpoint ipep)
{
return ipep.Uri.AbsoluteUri == OpenIdProviderURL + "server.aspx";
}
protected void Page_Load(object sender, EventArgs e)
{
//ทำการสร้าง url ของหน้าที่ต้องการให้ส่งกลับ โดยในที่นี้ให้ส่งกลับมายังหน้าเดิม
string[] segments = Request.Url.Segments;
string pathToPage = "";
for(int i=0; i<segments.Length-1; i++) {
pathToPage += segments[i];
}
UriBuilder returnToBuilder = new UriBuilder(Request.Url);
returnToBuilder.Path = pathToPage+"SSOLogin.aspx";
returnToBuilder.Query = null;
returnToBuilder.Fragment = null;
Uri returnTo = returnToBuilder.Uri;
returnToBuilder.Path = "/";
//บันทึก Url ลงใน Realm
Realm realm = returnToBuilder.Uri;
try
{
IAuthenticationRequest request = relyingParty.CreateRequest(
OpenIdProviderURL, realm, returnTo);
// Attributes ที่ต้องการร้องขอจาก OpenID Provider
// โดยใช้หลักการ AttributeExcahnge (AX)
var ax = new FetchRequest();
ax.Attributes.Add(new AttributeRequest(WellKnownAttributes.Contact.Email, true));
ax.Attributes.Add(new AttributeRequest(WellKnownAttributes.Name.FullName, true));
ax.Attributes.Add(new AttributeRequest(WellKnownAttributes.Name.Alias, true));
request.AddExtension(ax);
//ส่ง Request ไปยัง OpenID Provider
request.RedirectToProvider();
}
catch (Exception ex)
{
Response.Write("ไม่พบ OpenID end point สาเหตุอาจมาจากการที่ท่านระบุ url ผิด หรือ ท่านไม่ได้ทำการเพิ่ม
SSL Certificate ไปยัง Trusted Root Store บนเครื่อง Server");
}
}
}
|
OpenID Response
http://localhost:4856/loginProgrammatic.aspx
?dnoa.userSuppliedIdentifier=http://localhost:1807/EGA.EGA_CAS.Security.eAuthen/
&openid.claimed_id=http://localhost:1807/EGA.EGA_CAS.Security.eAuthen/user.aspx/Administrator
&openid.identity=http://localhost:1807/EGA.EGA_CAS.Security.eAuthen/user.aspx/Administrator
&openid.sig=P6+IZf5yVHeOTnngzycP7vw8BRLJiBHF4ZSOm1Mauxg=
&openid.signed=claimed_id,identity,assoc_handle,op_endpoint,return_to,response_nonce,ns.alias3
,alias3.mode,alias3.type.alias1,alias3.value.alias1,alias3.type.alias2,alias3.value.alias2
,alias3.type.alias3,alias3.value.alias3,alias3.type.alias4,alias3.value.alias4
&openid.assoc_handle=nixR!IAAAAB56GYx_ecAUdoR3qhyvqAi9MZxO-Ahl00nqoeUcrtnQQQAAAAGNVREK0g4S
_Zb8IgSlb96sYGjE4AYbskGBPXGmwQ3yrBvH2CmwGL6bIW_6m2Ibh0cN5Zt5rl0hhM4hvHj6HDlZ
&openid.op_endpoint=http://localhost:1807/EGA.EGA_CAS.Security.eAuthen/server.aspx
&openid.return_to=http://localhost:4856/loginProgrammatic.aspx?dnoa.userSuppliedIdentifier
=http%3A%2F%2Flocalhost%3A1807%2FEGA.EGA_CAS.Security.eAuthen%2F
&openid.response_nonce=2012-05-15T10:45:54ZrrKt7tDX
&openid.mode=id_res
&openid.ns=http://specs.openid.net/auth/2.0
&openid.ns.alias3=http://openid.net/srv/ax/1.0
&openid.alias3.mode=fetch_response
&openid.alias3.type.alias1=http://axschema.org/contact/email
&openid.alias3.value.alias1=test@gits.net.th
&openid.alias3.type.alias2=http://axschema.org/namePerson
&openid.alias3.value.alias2=Administrator eGov-Portal
&openid.alias3.type.alias3=http://axschema.org/namePerson/friendly
&openid.alias3.value.alias3=administrator
&openid.alias3.type.alias4=http://www.egov.go.th/2012/identifier/citizenid
&openid.alias3.value.alias4=
|
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
using DotNetOpenAuth.OpenId.RelyingParty;
using OpenIdRelyingPartyWebForms;
public class SSOLogin : System.Web.UI.Page
{
private const string OpenIdProviderURL = "https://testopenid.dga.or.th/";
private static OpenIdRelyingParty relyingParty = new OpenIdRelyingParty();
static SSOLogin()
{
// Configure the RP to only allow assertions from our trusted OP endpoint.
EndpointSelector ep = new EndpointSelector(EndpointFilter);
relyingParty.EndpointFilter = ep;
}
protected static bool EndpointFilter(IProviderEndpoint ipep)
{
return ipep.Uri.AbsoluteUri == OpenIdProviderURL + "server.aspx";
}
protected void Page_Load(object sender, EventArgs e)
{
try
{
IAuthenticationResponse response = relyingParty.GetResponse();
switch (response.Status)
{
case AuthenticationStatus.Canceled:
// ผู้ใช้ยกเลิก
break;
case AuthenticationStatus.Failed:
// ผู้ใช้ Authenticate ไม่ผ่าน
break;
case AuthenticationStatus.Authenticated:
// ทำการดึงข้อมูลที่ได้รับมาจาก OpenID Provider ออกมา
FetchResponse fetchResponse = response.GetExtension<FetchResponse>();
State.FetchResponse = fetchResponse;
string claimed = response.FriendlyIdentifierForDisplay;
string userName = State.FetchResponse.Attributes[WellKnownAttributes
.Name.Alias].Values[0];
string fullName = State.FetchResponse.Attributes[WellKnownAttributes
.Name.FullName].Values[0];
string email = State.FetchResponse.Attributes[WellKnownAttributes
.Contact.Email].Values[0];
string identifier = State.FetchResponse
.Attributes["http://www.egov.go.th/2012/identifier/citizenid"].Values[0];
string userType = State.FetchResponse
.Attributes["http://www.egov.go.th/2012/identifier/usertype"].Values[0];
UserMappingAndAuthorization(claimed, userName, fullName, email, identifier, userType);
break;
default:
break;
}
}
catch (Exception ex)
{
}
}
}
|
|