您的位置:首页 > 移动开发

Integrating OpenID in an ASP.NET MVC Application using DotNetOpenAuth

2010-09-10 14:09 585 查看
The#1requestIgotontheCodePaste.netsite-whichprovidesaquickandeasywaytopublishandsharecodesnippetspublicly-hasbeentoimplementOpenIdforauthenticationratherthanthecustomusername/passwordloginthat’sbeeninuseupuntilnow.OpenIdisacentralizedlogin/authenticationmechanismwhichhandlesauthenticationthroughoneormorecentralizedOpenIdproviders.TheseprovidersliveontheWebandareaccessedthroughaforwardingandcallbackmechanism–youloginattheprovider’ssiteandarethenreturnedtotheoriginalstartingUrlwithanauthorizedusertokenthatuniquelyidentifiesthatusertotheoriginalsite.

WhatisOpenId?

OpenIdisasinglesign-onscheme–theideaisthatyoukeepyourloginandprofileinformationinoneplacesothatyoudon’thavetologinateveryWeblocationandcreatenewusercredentialsoneachsite.Theideaofasinglesignonisn’tnewofcourse–lotsofthesethingshavebeenaroundovertheyearswiththemostrememberedprobablybeingMicrosoft’sPassport/WindowsLiveID(notthatanybodylikesWindowsLiveId).OpenIdusesasimilarconcept,butthedifferenceisthattherearemanyprovidersandsitesthatareimplementingOpenIdProviderssothatyoucanusetheirlogoncredentialsthatyouarealreadyusingtologintoothersites.ChancesarethatyoualreadyhaveasignonononeoftheOpenIdprovidersratherthanjustoneproviderthatcontrolsaccess.Youcanessentiallychoosewhereyouloginfrom.SomeofsitesthatareOpenIdprovidersnowareGoogle,Yahoo,MicrosoftWindowsLive,AOL,Facebook,Flickrandmanymanymore.Chancesareyou’resignedupononeoftheseservicesandyoucanusetheirlogonstologintoothersitesthatsupportOpenId.TherushtoOpenIdisfairlyfreshsosomeoftheseworkbetterthanothers.TheonesI’vebeenusingareMyOpenId,GoogleandYahooofwhichMyOpenIdworksbestifyouwanttoshareprofileinformation.

TobehonestIhadn’twarmeduptoOpenIduntilrecently.InfactIhadtobeconvincedbyuserrequestsontheCodePaste.netsitetotakealookatintegration.Nowthatit’sthereIthinkitisaprettysweetwaytogoalthoughIthinktheconceptislostontheaverageuseruntilOpenIdbecomesmorewidespread.Italsodidn’thelpthattheoriginalimplementationsandproviderswereprettycrappyandtheprocessonmostsitestosignupactuallywasreallyslowandclunky.ThathaschangedabitwithbetterinterfacesthatarefasterandmanymoresitesareusingOpenIdandmanymoreprovidersexist.

AnotherpainpointisthatsupportingstandardsforretrievingprofileinformationlikeSimpleRegistration(SREG)isnotwidelysupported.Providersarereturninginformationinavarietyofdifferentformatsandmostdon’treturnprofileinformationatallwhetheryouapproveornotwhichisreallylame.Youpresumablyownthedataonanyserviceyou’vegivendatatosowhythefussofsharingitifyouapproveofsharing?Anyway,don’tcountongettingfullcontactinformationfromOpenIdforawhile.Someproviderswillgivesomeinformation,otherswon’tgiveany–soyourapplicationhastobeabletohandlebothscenarios.

OpenIdDevelopment

Fordevelopmentwith.NETthereareafewlibrariesouttherethathandlethecommunicationsdetailsforyou.WhilecommunicationsarehandledusingsimpleURLsforroutingandcallbacksthedataencodedintotheseURLsmustbesecuresothattheycan’tbespoofedandthisprocessisnotexactlytrivial.

ForthesiteIchoseDotNetOpenAuth–afterdoingalittlebitofresearchitseemedtomethatthiswasthemostactivedevelopmentprojectthatisalsomostfeaturerich.DotNetOpenAuthdoesmuchmorethanOpenIditalsosupportsoAuth(anon-interactiveservicebasedcounterparttoOpenId)whichisanotherthingonmylisttoimplementfortheAPIportionofthesite.DotNetOpenAuthisfairlycomprehensiveandincludessupportspecificallyforWebapplications–bothMVCandWebFormswithcontrolsandhelpersprovidedaswellasMVCspecificactionmethodstohandleredirectionandreturnroutingwhichisanicetouch.

Thetoolmakestheactualroutingandhandlingoftheresultsfairlyeasy,buttheintegrationprocessneverthelessisnotcompletelytrivial.ThereasonforthisisthatmostapplicationswillneedtohandlebothloginsaswellasattachinganOpenIdaccounttoanexistinguserprofileofsomesortandthiscangettrickybecauseoftheHTTPGETbasedcallbackinterface.TheactualOpenIdcallsandcallbacksarequitesimple–theactualuserinterfaceandhookuproutinesthoughendeduptakingmealotlongerthanIhadexpected.

WalkingthroughOpenIdAuthentication

AlrightletstakealookandseehowthisworksontheCodePaste.netsite.AsImentionedtherearetwoparts–thebasicloginvalidationwhichisverystraightforwardandsimplesoI’llstartthere.

TheloginformdisplaysbothloginsforOpenIdandthetraditionalusernameandpassword:





Figure1–LogginginwithOpenId

IneededtoleavetheoldlogininformplacesoexistinguserscanloginandattachtheirexistingaccountstoOpenId.AlsosomefolksmayjustnotwanttogotheOpenIdroute.Sobotharesupportedonthisform.

OpenIdvalidationstartsbyprovidingaOpenIdUrl.TheUrldependsontheproviderandyoucanpickupthatUrlfromyourprovider’sOpenIdinformationpage–hereisalistofafewcommononesfromtheOpenIdsite.Ialsoprovidedacoupleoficonbuttonsforthemostlikelyculprits–MyOpenId,GoogleandYahoo.Anyothersneedtobetypedin.BetweenGoogleandYahooyouprobablyhaveanaccount.There’salsomyOpenIdwhichisoneoftheearlyproviderswhichispopular,andwithityouuseaurllikehttp://rickstrahl.myopenid.comtologon.TheYahoo(http://yahoo.com/)andGoogle(https://www.google.com/accounts/o8/id)Urlsarethesameforeverybody,sothoselinkscanactuallybeauto-fireddirectlybytheclickontheicon,whilethemyOpenIdentryrequiresenteringthenamefirstandmanuallyclickingthebutton.

OncetheLoginbuttonisclicked(orautomaticallyfired)theprocesstakesyoutotheprovider’ssite.ForexampleifyouuseGoogle,you’llseeaGoogleLoginscreenifyouaren’talreadysignedintoGoogleandyouareaskedtologin.Thepagewilltellyouthesourcesitethat’srequestingtheloginandpossiblywhichprofileinformationisrequested.Onceyouloginyouarethenredirectedhenceyoucame–backtothesameurlwhereyoucamefrombydefault.Thiscanbeoverridden,butinanMVCapplicationyou’llmostlikelywillwanttocomebacktothesameURLtoredisplaythelogin(orregistration)datawithsomeindicationthatyouarenowloggedin.

IfyouarealreadyloggedintotheOpenIdprovider(whichisusuallythecase)theloginoperationiscompletelytransparent–thepageisre-routedtotheOpenIdprovidersiteandimmediatelyreturnsbacktoyoursiteataUrlthatisspecifed.Thisusuallyhappensprettyfastthroughabunchofredirects–fromyoursitetotheOpenIdproviderandfrotheOpenIdproviderbacktoyoursite–sootherthansomeextradelaytimeyoudon’tevenseeanythinghappening.Itjustlookslikeyou’regoingdirectlytoyournextpageintheapp.

WhenthepagereturnsafteraloginitincludesencodedauthenticationinformationonthequerystringthatbecomesavailabletotherequestthathandlestheOpenIdlogin.SpecifiallyDotNetOpenAuthcanreadtheauthenticationtokenandanyprofileinformationthatwasreturnedifanywasrequestedandreturned.

Onceloggedinmyprofiledisplaythenreflectstheaccountassociationlikethis:



Figure2–LoggedinwithOpenIdandviewingtheProfile

Theapplicationstorestheuser’sopenidaspartoftheprofileandsoit’seasytodisplaythisinformation.TheOpenIdrelationshipcanbeunlinkedandtheaccountcanthenbeassignedtoanotherOpenIdorausernameandpasswordhastobeprovidedifnoOpenIdisavailable.AsImentionedtheactualOpenIdauthenticationprocessisprettysimplebutsomeoftheissuessurroundingitlikeattachinganddisconnectingaccountsfromOpenIdisalittlemorecomplicated–I’lltalkaboutthatbelow.

IfIgototheprofilepagewhentheaccountisnotassociatedwithanOpenIdthepagehasanoptiontologinandeffectivelyassociatetheaccountwiththenewOpenId:



ThesamepageisalsousedfornewregistrationswhichisusefulinordertoprovidethenameandemailactuallyfromtheOpenIdprovider(assupported–notethatgoogleandyahoodonotprovidethisdatainaformatthatDotnetOpenAuthextractsautomatically).

HowtoimplementOpenIdinanMVCApplication

PleasekeepinmindthatwhatIdescribeisjustonewayyoucandothisinthiscaseusingDotNetOpenAuthsinceitprovidesahostofdifferentwaystoimplementOpenId.TherearehighlevelhelpersandforWebFormsthereisaservercontrolthatyoucanuse.InthisexampleIhandlethisprocessmanuallysoyoucanseetheflowoftherequests.Theprocessisprettysimplealthoughthere’safairbitofcode(mostofitboilerplate)thatisinvolved.

I’llstartwiththesigninformbecausethat’ssimpler.Thisrelatestofigure1intheimagesequenceabove.TheOpenIdloginareaofthepageiswrappedintoitsownHTMLformthatkicksofftheprocessing.Thelayoutforthisblocklookslikethis:

<%using(Html.BeginForm(new{Controller="Account",Action="OpenIdLogon"}))
{%>
<fieldset>
<divclass="containercontent">
<divclass="labelheaderblock">LogonusingOpenId:</div>
<inputid="openid_identifier"name="openid_identifier"/>
<inputid="btnOpenIdLogin"type="submit"value="Login"/>
<imgid="imgOpenIdLoginProgress"src="<%=ResolveUrl("~/css/images/loading_small.gif")%>"style="display:none"/>

<divid="divOpenIdIcons">
<imgsrc="<%=ResolveUrl("~/images/openid-icon.png")%>"onclick="openIdUrl('openid')"title="myopenid.com"class="hoverbutton"/>
<imgsrc="<%=ResolveUrl("~/images/google-icon.png")%>"onclick="openIdUrl('google')"title="Google"class="hoverbutton"/>
<imgsrc="<%=ResolveUrl("~/images/yahoo-icon.png")%>"onclick="openIdUrl('yahoo')"title="Yahoo"class="hoverbutton"/>
</div>
</div>
</fieldset>
<%}%>

TheimageiconsfireasmallJavaScriptroutinethatcreatestheOpenIdurlsthatareinjectedintothetextbox:

functionopenIdUrl(site)
{
varvalue="";
varautoClick=false;

if(site=="openid"){
value="<YourAccount>.myopenid.com"
}
elseif(site=="google"){
value="https://www.google.com/accounts/o8/id";
autoClick=true;
}
elseif(site=="yahoo"){
value="http://yahoo.com/"
autoClick=true;
}

if(value){
varjText=$("#openid_identifier");
jText.val(value)
.focus();
if(autoClick)
$("#btnOpenIdLogin").trigger("click");
}
}

YoucanaddtothiswithotherURLsandiconsasyouseefit.Istuckwiththosethreebecausetheyarethemostcommonthatpeoplewillhaveaccountsonbutthatmaychange.AllothersOpenIdUrlscanbetypedinbyhand(whichisnotsosmooth).

WhentheLoginbuttonisclickedandtherequestissubmitteditfiresintoanOpenIdLogOnactionmethodofthecontroller.ThecontrollerhastoacceptbothPOSTandGEToperations.POSTisfromtheinitialbuttonclick,theGETwilloccurwhentheOpenIdproviderfiresbacktheresponseandprovidesthedataviathequerystring.

AsmentionedI’musingDotNetOpenAuthherewhichisasinglelargeDLLyoucandropintoyourBINfolder.Ithandlesalltheheavyliftingofcreatingtheredirectlinkandcrackingtheresponsequerystringextractingtheauthenticationtoken.

Theprocessinvolvestwosteps:MakingtheoriginalrequesttotheOpenIdprovider(whichredirects)andhandlingtheresponsethatcomesback.Here’sthecode(whichisprettyclosetothesamplecodeprovidedinoneofthedemos):

[AcceptVerbs(HttpVerbs.Post|HttpVerbs.Get),ValidateInput(false)]
publicActionResultOpenIdLogOn(stringreturnUrl)
{
varopenid=newOpenIdRelyingParty();
varresponse=openid.GetResponse();

if(response==null)//Initialoperation
{
//Step1-SendtherequesttotheOpenIdproviderserver
Identifierid;
if(Identifier.TryParse(Request.Form["openid_identifier"],outid))
{
try
{
varreq=openid.CreateRequest(Request.Form["openid_identifier"]);
returnreq.RedirectingResponse.AsActionResult();
}
catch(ProtocolExceptionex)
{
//displayerrorbyshowingoriginalLogOnview
this.ErrorDisplay.ShowError("Unabletoauthenticate:"+ex.Message);
returnView("Logon",this.ViewModel);
}
}
else
{
//displayerrorbyshowingoriginalLogOnview
this.ErrorDisplay.ShowError("Invalididentifier");
returnView("LogOn",this.ViewModel);
}
}
else//OpenIdredirectioncallback
{
//Step2:OpenIDProvidersendingassertionresponse
switch(response.Status)
{
caseAuthenticationStatus.Authenticated:
stringidentifier=response.ClaimedIdentifier;

//OpenIdlookupfails-Iddoesn'texistforlogin-loginfirst
if(busUser.ValidateUserOpenIdAndLoad(identifier)==null)
{
this.ErrorDisplay.HtmlEncodeMessage=false;
this.ErrorDisplay.ShowError(busUser.ErrorMessage+
"Please<ahref='"+WebUtils.ResolveUrl("~/Account/Register")+
"'>register</a>tocreateanewaccountor<ahref='"+
WebUtils.ResolveUrl("~/Account/Register")+
"'>associate</a>anexistingaccountwithyourOpenId");

returnView("LogOn",this.ViewModel);
}

//CaptureuserinformationforAuthTicket
//andissueFormsAuthtoken
UserStateuserState=newUserState()
{
Email=busUser.Entity.Email,
Name=busUser.Entity.Name,
UserId=busUser.Entity.Id,
IsAdmin=busUser.Entity.IsAdmin
};
this.IssueAuthTicket(userState,true);

if(!string.IsNullOrEmpty(returnUrl))
returnRedirect(returnUrl);

returnRedirect("~/new");

caseAuthenticationStatus.Canceled:
this.ErrorDisplay.ShowMessage("Canceledatprovider");
returnView("LogOn",this.ViewModel);
caseAuthenticationStatus.Failed:
this.ErrorDisplay.ShowError(response.Exception.Message);
returnView("LogOn",this.ViewModel);
}
}
returnnewEmptyResult();
}


ThecodeusestheOpenIdRelyingPartyclasstohandletheprocessingofthelogin.Thefirststepoccurswhenthebuttonisclickedandthere’snoproviderresponsetoprocess.Thiscode:

varreq=openid.CreateRequest(Request.Form["openid_identifier"]);
returnreq.RedirectingResponse.AsActionResult();

firesoffthetherequest.Theopenid_identifieristheOpenIdUrltheuserenteredintotheformandit’spassedtoDotNetOpenAuthtostartformulatingarequestforauthenticationatthatUrl.

Thecodeabovestartstheredirectionsequencegoingtotheproviderandbacktothecurrentpage-sinceIdidn’tspecifyanotherUrltoreturntobydefaulttherequestreturnstothecurrentpage’surl.

AsmentionedI’musingMVCsoandasyoucanseeDotNetOpenAuthconvenientlyincludesanAsActionResult()methodthatcreatesredirectresponseinaproperMVCstyleresponsewhichisverythoughtfulforconsistency.YoucanalsoretrievetheURLdirectlyandfireitonyourownalthoughIcan’tseewhyyouwould.Ifyou’reusingWebFormsorahandlerthereareothermethodsonRedirectingResponsetofiretheredirectsforyou.

NextthebrowserisredirectedtotheOpenIdproviderwhereyoueitherareaskedtologinorifyoualreadyareloggedinyouareauthenticatedandimmediatelyreturnedtothecurrentpage.WhentherequestreturnsitisnowaGEToperationandwestartbackoutatthetopofthesameControlleraction.

Thistimearound:

varresponse=openid.GetResponse();

willnotbenull–thereturnURLincludesabunchofnewencodedquerystringvalues.GetResponse()looksforthoseandbasedonthatcreatestheresponseinstanceandthecodecanbranchintothegreaterifblock.Therequestwilleitherbeauthenticated,canceledorfailed.Failurecanbeanynumberofthingssuchasinvalidlogininformation,ortimingoutorsomesortoftamperingwiththeURL.Bothcancelandfailureoperationsarehandledbysimplyredisplayingthecurrentviewwithanerrormessage.

Ifauthenticationsucceededwecanretrieveauniqueidentifierthatidentifiestheuser’sproviderchoiceandwe’reguaranteedatthispointthatthisuserwasauthenticatedthroughOpenId.Yay!

AlthoughyounowknowtheuserisauthenticatedASP.NETknowsnothingaboutthisauthenticationyet,sothenextstepistosetaFormsAuthenticationtickettoauthenticatetheusertotheapplication.ThisprocessisnodifferentthandoingamanuallogininanyotherASP.NETapplicationwithFormsAuthentication.

InCodePaste.netIstoreauserrecordintheticketsoIhavesomebasicuserinformationavailabletomyapplicationoneachhitwithouthavingtousesessionstorageorretrievingthisinfooneveryrequestfromthedatabase.Thecodedoesthis:

//CaptureuserinformationforAuthTicket
//andissueFormsAuthtoken
UserStateuserState=newUserState()
{
Email=busUser.Entity.Email,
Name=busUser.Entity.Name,
UserId=busUser.Entity.Id,
IsAdmin=busUser.Entity.IsAdmin
};
this.IssueAuthTicket(userState,true);

whereIssueAuthTicket()lookslikethisdoingstandardFormsAuthenticationticketassignment:

///<summary>
///IssuesanauthenticationissuefromauserStateinstance
///</summary>
///<paramname="userState"></param>
///<paramname="rememberMe"></param>
privatevoidIssueAuthTicket(UserStateuserState,boolrememberMe)
{
FormsAuthenticationTicketticket=newFormsAuthenticationTicket(1,userState.UserId,
DateTime.Now,DateTime.Now.AddDays(10),
rememberMe,userState.ToString());

stringticketString=FormsAuthentication.Encrypt(ticket);
HttpCookiecookie=newHttpCookie(FormsAuthentication.FormsCookieName,ticketString);
if(rememberMe)
cookie.Expires=DateTime.Now.AddDays(10);

HttpContext.Response.Cookies.Add(cookie);
}

Oncetheauthticket’sbeenissuedtheapplicationsimplyredirectstoanotherpageintheapplication(thenewsnippetpageinthiscase).

BTWjustforclarfication,theuserStateobjectcontainslogictoeasilypersisttoandfromstringsothisobjectiseasilyretrievedatthebeginningofarequestandstoredonupdates.InmybasecontrollerIdo:

protectedoverridevoidInitialize(System.Web.Routing.RequestContextrequestContext)
{
base.Initialize(requestContext);

//Grabtheuser'slogininformationfromFormsAuth
UserStateuserState=newUserState();
if(this.User.Identity!=null&&this.User.IdentityisFormsIdentity)
this.UserState.FromString(((FormsIdentity)this.User.Identity).Ticket.UserData);

//havetoexplicitlyaddthissoMastercanseeuntypedvalue
this.ViewData["UserState"]=this.UserState;
this.ViewData["ErrorDisplay"]=this.ErrorDisplay;
}

TheprocesstoauthenticateusingOpenIdisprettystraightforward.Nosurpriseshere,really.AllweneedtohaveissomeconditionallogictorouteforredirectinitiallyandpickuptheresultwhentherequestcomesbackandhandletheauthenticationandcreationofanewFormsAuthenticationticket.Whentherequestreturnsandismarkedasauthenticatedyoucansafelyassumetheuserisvalid.Becauseofthemessageencryptionandtimedsecuritytokensthedataissafeandnothijackable.

Registration–Alittlemorecomplex

Theloginprocessisstraightforwardprimarilybecausetherequestdoesn’tneedtotrackdatathatisalreadyonthepage.Youloginandyoumoveontoanotherpage.Howeverinaloginform(Figure3)theremaybeadditionalpiecesofinformationthatyouneedtotrack.Moreimportantlyyoutypicallyhavesomestateassociatedwiththeregistrationpage–likewhichuser(oranewuser)iscurrentlybeingdisplayedandthatinformationhastobecarriedforwardwhentheOpenIdrequestreturnsfromtheprovider.Sothecodetohandlethisisalittlelonger.

TheHTMLlayoutinFigure3isnearlyidenticaltotheHTMLlayoutontheloginformexceptthere’ssomeconditionallogictodisplaytheinputboxorthecheckboxandmessagethatidentifiesthecurrentOpenIdassociation.

<%using(Html.BeginForm(new{Controller="Account",Action="OpenIdRegistrationLogOn"})){%>
<fieldset>
<divclass="containercontent"style="padding:10px20px;">

<%if(string.IsNullOrEmpty(Model.busUser.Entity.OpenId)){%>
<labelfor="openid_identifier"class="labelheaderblock">EnteranOpenIdUrl:</label>
<inputid="openid_identifier"size="40"name="openid_identifier"/>
<inputid="btnOpenIdLogin"type="submit"value="Login"/>
<imgid="imgOpenIdLoginProgress"src="<%=ResolveUrl("~/css/images/loading_small.gif")%>"style="display:none"/>

<divid="divOpenIdIcons">
<imgsrc="<%=ResolveUrl("~/images/openid-icon.png")%>"onclick="openIdUrl('openid')"title="myopenid.com"class="hoverbutton"/>
<imgsrc="<%=ResolveUrl("~/images/google-icon.png")%>"onclick="openIdUrl('google')"title="Google"class="hoverbutton"/>
<imgsrc="<%=ResolveUrl("~/images/yahoo-icon.png")%>"onclick="openIdUrl('yahoo')"title="Yahoo"class="hoverbutton"/>
</div>

<%}else{%>
<labelfor="openid_identifier"class="labelheaderblock">OpenIdAssociation</label>
<imgsrc="<%=ResolveUrl("~/css/images/greencheck.gif")%>"/>Thisuseraccountislinkedto<b><%=Html.Encode(Model.busUser.Entity.OpenId)%></b>
<inputid="btnOpenIdUnlink"name="btnOpenIdUnlink"type="submit"value="Unlink"/>
<%}%>

<%=Html.Hidden("Id2",this.Model.busUser.Entity.Id)%>
</div>
</fieldset>
<%}%>

NoticethathereweneedtokeeptrackoftheactiveUserifany.Sinceyoucanattach/detachfromanexistingprofilewedohavetoknowwhichuserwe’redealingwith.Thissoundseasyenoughbutrememberthatyoucan’tuseplainPOSTdataforthisbecausethepagegoesofftheOpenIdproviderandcomesbackwithaGETthatjustholdstheresultvalues.Thismeanstoredisplaythepagetheentirepagehastobere-renderedbasedonthemodel.Todothiswe’llneedtokeeptrackoftheuseridfortherequestsequence.

Thisbaseprocessisverysimilartotheloginprocessandusesthesametwostagesofrequestsendingandreturn.FirstcapturethevaluefromahiddenformvariableandthenstoreitintheSessionobjecttopassbetweenthetworequests.Here’swhattheopenidattachment/detachmentcodelookslike:

[AcceptVerbs(HttpVerbs.Post|HttpVerbs.Get),ValidateInput(false)]
publicActionResultOpenIdRegistrationLogOn(FormCollectionformVars)
{
returnOpenIdRegistrationLogOn(null,true);
}

privateActionResultOpenIdRegistrationLogOn(IAuthenticationResponseresponse,boolreserved)
{
this.ViewData["IsNew"]=true;

varopenid=newOpenIdRelyingParty();

if(response==null)
response=openid.GetResponse();

if(response==null)
{
stringuserId=Request.Form["Id2"];//havetotracktheuser’sid

//Checkforunlinkoperation
if(!string.IsNullOrEmpty(Request.Form["btnOpenIdUnlink"]))
{
if(busUser.Load(userId)==null)
{
this.ErrorDisplay.ShowError("Couldn'tfindassociatedUser:"+busUser.ErrorMessage);
returnRedirectToAction("Register",new{id=userId});
}
busUser.Entity.OpenId="";
busUser.Save();
returnRedirectToAction("Register",new{id=userId});
}

Identifierid;
stringopenIdIdentifier=Request.Form["openid_identifier"];
if(Identifier.TryParse(openIdIdentifier,outid))
{
try
{
//Weneedtoknowwhichuserweareworkingwith
//andsowepasstheidthrusession–(isthereabetterway?)


Session["userId"]=userId;

varreq=openid.CreateRequest(id);

varfields=newClaimsRequest();
fields.Email=DemandLevel.Request;
fields.FullName=DemandLevel.Request;
req.AddExtension(fields);

returnreq.RedirectingResponse.AsActionResult();
}
catch(ProtocolExceptionex)
{
this.ErrorDisplay.ShowError("Unabletoauthenticate:"+ex.Message);
this.busUser.NewEntity();
returnView("Register",this.ViewModel);
}
}
else
{
ViewData["Message"]="Invalididentifier";
returnView("Login");
}
}
else
{
//Reestablishtheuserwe’redealingwith

stringuserId=Session["userId"]asstring;
if(string.IsNullOrEmpty(userId))
ViewData["IsNew"]=true;
else
ViewData["IsNew"]=false;

//Stage3:OpenIDProvidersendingassertionresponse
switch(response.Status)
{
caseAuthenticationStatus.Authenticated:
varclaim=response.GetExtension<ClaimsResponse>();
stringemail=null,fullname=null;
if(claim!=null)
{
email=claim.Email;
fullname=claim.FullName;
}
stringidentifier=response.ClaimedIdentifier;

varuser=busUser.Load(userId);
if(user==null)
{
user=busUser.ValidateUserOpenIdAndLoad(identifier);
if(user==null)
user=busUser.NewEntity();
}

//associateopenidwiththeuseraccount
busUser.Entity.OpenId=identifier;

if(!string.IsNullOrEmpty(email)&&string.IsNullOrEmpty(busUser.Entity.Email))
busUser.Entity.Email=email;

if(!string.IsNullOrEmpty(fullname)&&string.IsNullOrEmpty(busUser.Entity.Name))
busUser.Entity.Name=fullname;

if(string.IsNullOrEmpty(busUser.Entity.Name))
{
stringhost=StringUtils.ExtractString(response.FriendlyIdentifierForDisplay,".",".com");
if(!string.IsNullOrEmpty(host))
host="("+host+")";

busUser.Entity.Name="Unknown"+host;

this.ErrorDisplay.DisplayErrors.Add("Pleaseenterausername","Name");
}

if(!busUser.Validate())
{
busUser.Entity.OpenId="";
this.ErrorDisplay.ShowError(busUser.ErrorMessage);
returnView("Register",this.ViewModel);
}
if(!busUser.Save())
{
busUser.Entity.OpenId="";
this.ErrorDisplay.ShowError(busUser.ErrorMessage);
returnView("Register",this.ViewModel);
}

UserStateuserState=newUserState()
{
Name=busUser.Entity.Name,
Email=busUser.Entity.Name,
UserId=busUser.Entity.Id,
IsAdmin=busUser.Entity.IsAdmin
};
this.IssueAuthTicket(userState,true);

//andreloadthepagewiththesaveddata
returnthis.RedirectToAction("Register",new{id=busUser.Entity.Id});
caseAuthenticationStatus.Canceled:
this.busUser.NewEntity(true);
this.ErrorDisplay.ShowMessage("Canceledatprovider");
returnView("Register",this.ViewModel);
caseAuthenticationStatus.Failed:
this.busUser.NewEntity(true);
this.ErrorDisplay.ShowError(response.Exception.Message);
returnView("Register",this.ViewModel);
}
}
returnnewEmptyResult();
}


Therearetwomethodshere–oneasanactualendpointandanotherthatcanbecalledwithanexistingresponsepassedtoit–thisisusefultoautomaticallyforcealoginthatdoesn’texistdirectlyintotheregistrationformviacode.ADotNetAuthresponsecanonlybeparsedonceorelseit’sconsideredinvalidandsopassinginanexistingresponsewasarequirementtohandlethissortof‘manual’routing.ForthesimplecallbackscenarioyoucanjustuseasimpleControllerendpointmethod.

AsinthelastexampletheOpenIdRelyingPartyisusedtohandletheactualrequestsemanticsofsendingtheredirectandreceivingthedata.Thispartoftheprocessreallydoesn’tchange.However,howyoudealwiththedatareceivedandabortscenariosaddsasignificantamountofadditionalcodecomparedtothepreviousloginonlyexample.

Ifthere’snoOpenIdresponse,thecodefirstchecksforunlinkrequests.ForthisitneedstheidtoloadaninstanceofthebusinessentityandupdateitbyremovingtheOpenIdfromit.ThepageisthenredisplayedagainusingtheIDasthekey.

OtherwisetherequestissentofftotheOpenIdprovider.TheproviderthenreturnsandagaintheOpenIdresponsewillnowbeset.OnsuccessthecodetriestoretrievetheClaimsRequestvalues.Claimsrequestissentwiththeinitialrequesttoasktheservertoreturnvaluesfromtheuser’sprofile.ForCodePaste.netI’minterestedinfullnameandemailasoptionalresults.NotethatsomeOpenIdonlyprovidersuseacommonformatcalledSREGtoreturnvalues.myOpenDnsdoesasdoseveralotherofthededicatedservices.However,thisstandardsisonlysketchilysupportedbythebigproviders.GoogleforexamplerequiresthatyouDemanddataandthenwillonlyreturntheemailaddress(whichseemsironicgiventhat’sthemostprivateitemintheprofile).Yahoodoesn’treturnanything–accordingtotheirOpenIdpagetheyonlyshareprofileinformationwithcertainsitestheydeemappropriate.SREGhoweverisgainingmomentumsoit’slikelythatallproviderswillevenutallysupportSREG.

InDotnetOpenAuththerequestfor‘claimdata’ishandledlikethis:

//RequestadditionalProfile
varreq=openid.CreateRequest(Request.Form["openid_identifier"]);informationwithClaimsRequest
varfields=newClaimsRequest();
fields.Email=DemandLevel.Demand;
fields.FullName=DemandLevel.Demand;
req.AddExtension(fields);
YoucanRequestorDemandclaims.IntheoryDemandmeansthatiftheprovideroruserrefusestoprovidetherequestedkeystherequestfails.InrealitythoughproviderslikegoogleignoreRequestcommandsandsoDemandisoftenrequiredtoretrieveinformation.

ToretrievetheclaimsdatausesGetExtension<T>():

//Retrievecustomprofileinformationifavailable
varclaim=response.GetExtension<ClaimsResponse>();
stringemail=null,fullname=null;
if(claim!=null)
{
email=claim.Email;
fullname=claim.FullName;
}
Justrememberthatthere’snoguaranteethesevaluesareset.

Theotherissueinthiscontrollerdealswithstatemanagementoftheuser.Whentheusercomestothepagetohookupwehavestateonthepageforthatparticularuser,butwhenwere-directtotheOpenIdprovidersiteweloosethatstate.SointhiscasetheuseridneedstobetrackedandI’musingaSessionobjectforthis.Ihaven’thadmuchneedforsessioninMVCapplicationsbutthisisonecasewhereIdon’tseeawayaroundthisunfortunately.Beforetherequestissenttheuser’sidisstoredinaSessionobject.Whentheauthenticatedrequestreturnsthesessionidisretrievedandusedtolookuptheuser’sbusinessobjectwhichistheupdatedwiththeopenididentifierandoptionallytheprofileinformationifany.Therestofthebulkycodedealswithafewchecksonthedatareturnedandisolatingwhichdatatoupdate.

OpenIdIntegration–Morecomplicatedthanitlooks

Phew–thislookslikealotofcodeyouhavetowriteandwhileit’saprettygoodchunkthelargestpartofthiscodehastodowiththebusinesslogictosaveandupdatetheuser’sinformationandsettingtheFormsAuthenticationticket.TheactualOpenIdrelatedlogicisrelativelysimpleonceyouunderstandthebasicconceptofhowtheflowofanOpenIdauthenticationworks.

AsanyWebbasedcallbackmechanismthecodeisbulkybecauseyouhavetoeffectivelyroutetheresponsesinsideyourcodeandyouendupwithsomecodethatcouplesWebandbusinesslogicwhichisalwaysugly.IrememberfightingsimilarissuesyearsagowithPayPalintegration.ProcesseslikethesearecumbersomebecausetheyaresocloselytiedtotheactualWebrequest–theControllerActioninthiscaseanddon’tprovideforaneasywaytoabstractandwrapintobusinesslogic.That’sthepenaltyyoupayforadistributedsysteminmanysituationsandthisisnodifferent.

TherearealsoothersolutionoutthereforOpenIdintegration.RobConeryhasbeenmuckingaroundandtryingtoconvincemetotryRPXwhichusesamaninthemiddleapproachforhostingOpenIdauthentication.TheybasicallyprovidetheloginAPIviaapopupwindowandyouthenretrievetheauthenticationdataviaabackendservicecall.ButthisrequiresyetanotherproviderinthemiddlethatyoutalktothroughaproprietaryAPI.Itmighthelpisolatingtheauthlogic(servicecallbackvs.acontrolleraction).Butit’snotforme–butforsomeofyouthismightbeinterestingtocheckoutespeciallyifyoulikethewaytheRPXlooks.It'lldefinitelybealesscodeapproach.

Ihopethoughthatthisarticleisusefultosomeofyou.IcertainlywishIwouldhavehadsomethingtolookatwhenIstartedlookingatOpenId.There’salotofhighlevelposturingstuffouttherethattalksaboutthebenefitsandgoalsofopenIDbutpreciouslylittlecodethatactuallyshowstheprogressionofstepsincontextthatexplainsOpenIdflowonahighlevelalongwithanimplementation.

Ialsowanttothank

Resources

DotNetOpenAuthLibrary
.NETlibraryforprocessingOpenId,oAuth,InfoCardandvariousotherauthenticationAPIs.Verycomprehensiveandthoughtfulfunctionalityalthoughdocumentationisabitonthesparseside.Excellentsupportandfeedbackfromthedeveloperwho’sreallypassionateaboutthislibraryandcloselymonitorsandlistenstofeedback.

CodePaste.netSourceCode
ThisarticleisbasedonthecodeonCodePaste.netandifyoulikeyoucancheckoutthesourcecodefromtheSubversionrepository.




PostedinASP.NETMVC




FeedbackforthisWeblogEntry

#IntegratingOpenIDinanASP.NETMVCApplicationusingDotNetOpenAuth
byDotNetKicks.comSeptember17,2009@9:18pm
You'vebeenkicked(agoodthing)-TrackbackfromDotNetKicks.com

#re:IntegratingOpenIDinanASP.NETMVCApplicationusingDotNetOpenAuth
byWilliamKapkeSeptember18,2009@12:09am
Thereisdefinitelyalackofusefulinformationoutthere.ThanksforgettingthisASP.NETwalkthroughtogether.

I'vebeenhopingsomeonewouldputtogetherawellmadevideooftheprocessoutliningtheTECHNICALdetails.Suchasananimatedflowofthedata.

Isn'tthereaspecouttherethatistryingpushusing"username@myhost.com"insteadof"httt://username.myhost.com"?Ibelieveitworksbyfirstsendingarequestouttomyhost.comandgettingaurlpatternback-whichittheninjectstheusernameinto.(IwishIknewwhereIreadaboutthat)

Ithinkusingtheemailpatternwillhelpwithadoption.

#

re:IntegratingOpenIDinanASP.NETMVCApplicationusingDotNetOpenAuth
byRickStrahlSeptember18,2009@1:23am
@William-I'mnotsurethatavideowalkthroughwouldbeallthathelpful-thisisthesortofthingwhereyouneedsomecodetolookatandcopyandpasteandafairbitofit.

Astotheurls-theyareactuallyfairlysimpleformostproviders.TheyareURLsbecausethoseareuniquewhereasemailaddressesareflakeythatwayespeciallywithemailaddresseschanging.ItlookslikemostprovidersaregoingtherouteofusingasimpleURLonly.YahooisjustYahoo.comandtheyfigureouttoroutetotheopenidproviderbasedonthequerystringdatacomingin.IseereallynobenefitofhavinganysortofuseridentificationwiththeURL-ifyou'renotloggedinyouhavetoprovideyourcredentialsanywayanditmakestheurleasiertoworkwithifit'sthesameforallusers.Onceyou'reloggedinwithanOpenIdprovider(whichifyouuseitismostofthetimetheycanpullupyourprofilebeforeyoulogonandauto-fillwhatevernon-privateinformationonthesignonform.

ThesimplerthebetterandthesimplestisthesameURLforeveryoneforeachprovider.

#

re:IntegratingOpenIDinanASP.NETMVCApplicationusingDotNetOpenAuth
byRaxitSeptember18,2009@3:38am
ThanksRickforsharingthis.OnethingIamnotclearwithgoogleopenid.IwanttodisplayUserEMailoncetheusersuccessfullygetsauthenticatedandreturntomywebsiteverysimilartowhatyouhavedonebutitreturnstokenizedURLlikewww.google.com/accounts/o8/id?id=AItOawmtAnQvSXGhRTkAHZ...whenIaccesse.Response.FriendlyIdentifierForDisplay.

UponsearchingforawhileIgotthisthreadhttp://stackoverflow.com/questions/1355292/friendly-name-from-google-using-openidwhichsaysgoogledoesnotreturnfriendlyIdentifiers.LetmeknowifIammissingsomethinghere.

FYI:IamusingcontrolsprovidedwithDotnetOpenAuthi.eOpenIdLogin,OpenIdButton.

#

re:IntegratingOpenIDinanASP.NETMVCApplicationusingDotNetOpenAuth
byAndrewArnottSeptember18,2009@5:52am
HiRick,

Firstofall,thanksforyourpost.That'sprobablythemostdetailedandthoughtoutoneI'veseenyet.

Butyouhaveasmall(big)problem.You'reusingIAuthenticationResponse.FriendlyIdentifierForDisplaytomakesecuritydecisions.ItshouldONLYbeusedtodisplaytheidentifiertotheuser(thusthelongname;).YoushouldbeusingIAuthenticationRespones.ClaimedIdentifierastheformsauthticketusernametokeepyoursitesecureagainstidentityspoofingattacks.

#IntegratingOpenIDinanASP.NETMVCApplicationusingDotNetOpenAuth-RickStrahl
byDotNetShoutoutSeptember18,2009@7:01am
Thankyouforsubmittingthiscoolstory-TrackbackfromDotNetShoutout

#ВнедряемOpenIDвприложениеASP.NETMVC
byprogg.ruSeptember18,2009@9:57am
Thankyouforsubmittingthiscoolstory-Trackbackfromprogg.ru

#

re:IntegratingOpenIDinanASP.NETMVCApplicationusingDotNetOpenAuth
byRickStrahlSeptember18,2009@11:51am
@Raxit-The'friendlyidentifier'isdeterminedbytheOpenIdproviderandyouhavenocontroloverwhatthatis.Googlereturnsalongandnastystringforthiswhichisfine-youdon'treallyneedtodisplayitanywhere(ijustdidbecauseitwasashowcasemoreorless).Ideallytherealinformationthatyouwanttostore/keep/holdontoisprofileinformationbutasIpointedoutinthearticlethisinformationisofmixedqualityandnotguaranteedtobeprovided.

FriendlyIdentifierinthecodeaboveiswhatDotnetOpenAuthreturns.Ibelieveifsomethingismissingit'lljustreturntheuniqueurlreturnedwhichisrequiredaspartoftheOpenIdconversation.Someothersiteslikemyopenid.netreturnname.myopennet.idasthefriendlyidentifierwhichisnicerforsure.

#

re:IntegratingOpenIDinanASP.NETMVCApplicationusingDotNetOpenAuth
byRickStrahlSeptember18,2009@11:58am
@Andrew-thanksforthefeedback.NotsureIfollowthough.I'mstoringthefriendlyidintheprofileonlyasalookupwhichisONLYcheckedafteraAuthenticationStatus.Authenticatedcallbackthatshouldbeguaranteedtobesecure(otherwiseyourlibrarywouldn'treturnthiscodeanditwouldbepointless,right?:-}).

Theformsauthenticationticketincludes*nothing*atallrelatedtoOpenId.IbasicallyonlyauthenticatethroughOpenIdandonceI'mdoneIissueatotallyunrelatedformsauthenticationticketthathasnodirectconnectiontotheOpenIdidentifieroranythingelseforthatmatter.PlainFormsAuthticket.

SincetheonlywayauthenticationoccursisthroughtheAuthenticationStatus.AuthenticatedcallbackIdon'tseehowthiscouldbeinsecure.ThefriendlyIdismerelyusedasthelookupkeythatmapstheOpenIdaccountandtheuserrecordonmysite.

UsingtheticketretrievedfromOpenIdwouldn'tworkinthisscenariobecausethatticketwillchangewitheachlogin-sonocontinuity.ThewayitisnowiftheFormsAuthticketexpiresortheuserlogsout,nexttimetheyhavere-authenticatewithOpenIdandgothroughthesameassignmentcycleagain.

Ifyouseeaholeherepleasecouldyouclarify,becauseIdon'tseeit?

#

re:IntegratingOpenIDinanASP.NETMVCApplicationusingDotNetOpenAuth
byMikeSeptember18,2009@1:57pm
Rick
YouCANgetemailinfofromgoogle...youhavetoenablethisintheweb.configandaddasanextensiononyourrequest:
request.AddExtension(newClaimsRequest
{
Email=DemandLevel.Require,
});
It'sdocumentedonthedotnetopenauthsiteprettywellandIjustimplementedthisforMonoRaileasily.

#

re:IntegratingOpenIDinanASP.NETMVCApplicationusingDotNetOpenAuth
byAndrewArnottSeptember18,2009@2:01pm
Rick,

YouarecreatingtheauthticketusingtheOpenID"friendlyidentifier":

...newFormsAuthenticationTicket(1,userState.Name...

This,andthefactthatyouusethefriendlyidentifierforlookupiswhat'swrong.YouMUSTuseClaimedIdentifierforusernamelookup,whichinturnmeansyoureallyshoulduseitfortheformsauthticket'susernameaswell.TheFriendlyIdentifierForDisplayisNOTguaranteedtobeuniqueforeachuserastheOpenIDClaimedIdentifieris.Forinstance,https://somebody.comandhttp://somebody.comarebyOpenIDstandardstwoDIFFERENTpeople,andyetFriendlyIdentifierForDisplaywillbe"somebody.com"forbothofthem.That'swhyyoumustuseClaimedIdentifier,whichincludesthefull,uniquestringoftheuser'sOpenIDwheneveryoulookup"ok,sothisguyisloggingin,butwhere'shisuserrecordanddata?"

AndsinceformsauthenticationinASP.NETmakesHttpContext.Current.User.Namesoeasytogetandmakesecuritydecisionson,youshoulduseClaimedIdentifierwhencreatingtheformsauthticketthereaswell.FriendlyIdentifierForDisplayshouldonlybeusedwhenyou'reemittingHTMLtothebrowsersoyoucansay"Welcome,somebody.com"!Sincethat'seasierontheeyesthan"Welcome,https://somebody.com/".That'sallthefriendlyidentifierisfor.

TheClaimedIdentifierisNOTrotatedateachloginsothattheuserwouldhavetogothroughtheassignmentcycle.It'saconstantforauseracrossalllogins,soit'saperfect(andtheonlysecure)waytolookupauserinyourdatabase.

Ihopethishelps.

#

re:IntegratingOpenIDinanASP.NETMVCApplicationusingDotNetOpenAuth
byRickStrahlSeptember18,2009@2:09pm
@Mike-IknowyoucangetemailfromGoogle,butnothingelse.Youcannotgetfullnameoranyotherprofileitemsatleastnotcurrently.

#

re:IntegratingOpenIDinanASP.NETMVCApplicationusingDotNetOpenAuth
byRickStrahlSeptember18,2009@7:56pm
@Andrewthanksfortheclarification.I'vechangedthecodeaboveandonthesite(alongwithsomelogictoupdateidscurrentlyinthereifnecessary-lookslikeseveraloftheprovidershavetheclaimedidandfriendlyIdactuallybeingthesamethingthough.

Justforclarificationthough-I'mnotusingtheOpenIdinmyAuthTicketbecausenoteveryoneisusingopenauth.Rathertheuseridisused(thanksyoualsofoundabugthere-Iwasusingthenamenottheid).Istillallowplainusername/passwordloginsandlookingatthesitemostnewsignupsstillusethoseratherthanOpenId.Ihavelessthan10%ofusersusingtheOpenIdcurrently.<shrug>

Thanksforlookingthisover,thefeedbackandofcourseDotNetOpenAuth.It'smadetheactualloginportionveryeasy.

#IntegratingOpenIDinanASP.NETMVCApplicationusingDotNetOpenAuth-RickStrahl'sWebLog
byDotNetShoutoutSeptember21,2009@2:53pm
Thankyouforsubmittingthiscoolstory-TrackbackfromDotNetShoutout

#

re:IntegratingOpenIDinanASP.NETMVCApplicationusingDotNetOpenAuth
byJarrettVanceSeptember22,2009@1:14pm
I'vealsodonesomeworkonOpenIDinASP.NETMVC.However,ItookasomewhatoldschoolapproachandcreatedanOpenIdAuthenticationModulethatcouldbepluggedinside-by-sidewiththeFormsAuthenticationModulethatalreadyexists.

SomeworkstillneedstobedonetorequestausernamefrompeopleusingGoogleOpenIdas,fornow,itjustuses"OpenIDuser"astheirnickname.

Youcanseethesourcecode@http://code.google.com/p/atomsite/source/browse/trunk/OpenIdPlugin/OpenIdAuthenticationModule.cs

#

re:IntegratingOpenIDinanASP.NETMVCApplicationusingDotNetOpenAuth
byRickStrahlSeptember23,2009@7:03pm
@Jarret-likethatapproachtooandmightactuallybealittlemorereusablearoundtheapplication.Cool,thanksforpostingthelink.

##.think.ininfoDose#43(11thSeptember-22ndSeptember)
by#.think.inOctober02,2009@6:43am
#.think.ininfoDose#43(11thSeptember-22ndSeptember)

#

re:IntegratingOpenIDinanASP.NETMVCApplicationusingDotNetOpenAuth
byBenjaminRobbinsOctober04,2009@12:09am
I'verecentlyswitchedfromRPXtoDotNetOpenAuthinapersonalproject.I'mveryhappywithDotNetOpenAuth'scapabilities,butifIneededsimpleFacebookorMySpaceintegration,thenI'dgowithRPX.

AsanalternativetousingSessiontostoreyourstate,youcanuseIAuthenticationRequest.AddCallbackArguments(stringkey,stringvalue)duringstage1beforeyousendtheinitialOpenIDrequestandIAuthenticationResponse.GetCallbackArgument(stringkey)afteryoureceivetheauthenticatedresponsefromtheOpenIDproviderduringstage2.ThesemethodsaddandretrieveyourstatefromthequerystringthatispassedaroundduringtheOpenIDmagicdance.Inthisspecificcase,Idon'tthinkusingSessionwouldbethatbigofadeal,butifyouwereinasituationwhereusingSessionwaspainful,thenstoringyourstateinthequerystringbecomesmuchmoreuseful.

#re:IntegratingOpenIDinanASP.NETMVCApplicationusingDotNetOpenAuth
byGermánOctober05,2009@3:11am
Hi!

Great,greatsample.InordertoavoidsuchalongmethodIhavesplitteditintwoactionswithanActionMethodSelectorAttributesothere'snoneedofaagreatifcodeblock.

Thanks.Germán.

usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Web;
usingSystem.Web.Mvc;
usingDotNetOpenAuth.OpenId.RelyingParty;
usingSystem.Reflection;

namespaceSherezade.Web.Filters
{
publicclassOpenIdAuthenticationCallbackAttribute:ActionMethodSelectorAttribute
{
publicoverrideboolIsValidForRequest(ControllerContextcontrollerContext,System.Reflection.MethodInfomethodInfo)
{
varopenid=newOpenIdRelyingParty();
varresponse=openid.GetResponse();

//WedohaveanopenIdresponse,it'stheprovidercallingback
if(response!=null)
{
//lookforIAuthenticationResponseparameterandpasstheresponseobjecttotheactionaswecan'tcallGetResponse()twicelaterintheactionmethod
varparameterName=methodInfo.GetParameters().Where(pi=>pi.ParameterType.Equals(typeof(IAuthenticationResponse))).Select(pi=>pi.Name).SingleOrDefault();
if(!String.IsNullOrEmpty(parameterName))
{
controllerContext.RouteData.Values.Add(parameterName,response);
}
returntrue;
}
returnfalse;
}
}
}


//handlesopenidformsubmissionfromuser
publicActionResultOpenIdLogin()
{
varopenid=newOpenIdRelyingParty();
Identifierid;
if(Identifier.TryParse(Request.Form["openidIdentifier"],outid))
{
try
{
varreq=openid.CreateRequest(Request.Form["openidIdentifier"]);
varfields=newClaimsRequest();
fields.Email=DemandLevel.Require;
req.AddExtension(fields);
returnreq.RedirectingResponse.AsActionResult();
}
catch(ProtocolExceptionex)
{
returnView("Registro",null);
}
}
else
{
returnView("Registro",null);
}
}
//handlestheprovidercallback
[OpenIdAuthenticationCallback]
publicActionResultOpenIdLogin(IAuthenticationResponseresponse)
{
switch(response.Status)
{
caseAuthenticationStatus.Authenticated:
break;
caseAuthenticationStatus.Canceled:
break;
caseAuthenticationStatus.Failed:
break;
}

returnnewEmptyResult();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐