Although DotNetOpenId makes adding OpenID support to your ASP.NET web site as easy as dropping a control on your page design surface, there are reasons you may want to take the lower-level approach of writing a bit of the code yourself. DotNetOpenId fully supports both scenarios. In this post, I'll walk through a best practice minimal sample of how to get it working without using the controls.
First I'll define a few elements on my ASPX page: an ordinary TextBox and Button for the text field and login action, a CustomValidator control to prompt the user in the case of a malformed identifier, and a couple of Label controls to tell the user about failed authentication results.
<asp:Label ID="Label1" runat="server" Text="OpenID Login" /> <asp:TextBox ID="openIdBox" runat="server" /> <asp:Button ID="loginButton" runat="server" Text="Login" OnClick="loginButton_Click" /> <asp:CustomValidator runat="server" ID="openidValidator"
ErrorMessage="Invalid OpenID Identifier" ControlToValidate="openIdBox"
EnableViewState="false" OnServerValidate="openidValidator_ServerValidate" /> <asp:Label ID="loginFailedLabel" runat="server" EnableViewState="False"
Text="Login failed" Visible="False" /> <asp:Label ID="loginCanceledLabel" runat="server" EnableViewState="False"
Text="Login canceled" Visible="False" />
The label controls are initially invisible so they can be made visible only in failure cases. The EnableViewState property on them is set to false so that they will only remain visible for the immediate failure and then hide themselves again on the next postback.
Now to define the behavior on the page. Here is the code for your code behind .aspx.cs file:
using System;
using System.Net;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using DotNetOpenId;
using DotNetOpenId.RelyingParty;
public partial class loginProgrammatic : System.Web.UI.Page {
protected void openidValidator_ServerValidate(object source, ServerValidateEventArgs args) {
// This catches common typos that result in an invalid OpenID Identifier.
args.IsValid = Identifier.IsValid(args.Value);
}
protected void loginButton_Click(object sender, EventArgs e) {
if (!Page.IsValid) return; // don't login if custom validation failed.
OpenIdRelyingParty openid = new OpenIdRelyingParty();
try {
IAuthenticationRequest request = openid.CreateRequest(openIdBox.Text);
// This is where you would add any OpenID extensions you wanted
// to include in the authentication request.
// request.AddExtension(someExtensionRequestInstance);
// Send your visitor to their Provider for authentication.
request.RedirectToProvider();
} catch (OpenIdException ex) {
// The user probably entered an Identifier that
// was not a valid OpenID endpoint.
openidValidator.Text = ex.Message;
openidValidator.IsValid = false;
} catch (WebException ex) {
// The user probably entered an Identifier that
// was not a valid OpenID endpoint.
openidValidator.Text = ex.Message;
openidValidator.IsValid = false;
}
}
protected void Page_Load(object sender, EventArgs e) {
openIdBox.Focus();
OpenIdRelyingParty openid = new OpenIdRelyingParty();
if (openid.Response != null) {
switch (openid.Response.Status) {
case AuthenticationStatus.Authenticated:
// This is where you would look for any OpenID extension responses included
// in the authentication assertion.
// var extension = openid.Response.GetExtension<someextensionresponsetype>();
// Use FormsAuthentication to tell ASP.NET that the user is now logged in,
// with the OpenID Claimed Identifier as their username.
FormsAuthentication.RedirectFromLoginPage(openid.Response.ClaimedIdentifier, false);
break;
case AuthenticationStatus.Canceled:
loginCanceledLabel.Visible = true;
break;
case AuthenticationStatus.Failed:
loginFailedLabel.Visible = true;
break;
// We don't need to handle SetupRequired because we're not setting
// IAuthenticationRequest.Mode to immediate mode.
//case AuthenticationStatus.SetupRequired:
// break;
}
}
}
}
There you have it. Of course the point of this exercise is that you want more fine-grained control of the operation. The code above works as-is, and makes a great template to base your site on since it has all the checks necessary to provide a functional (albeit ugly) login page.
Making the login page prettier, without compromising the functionality, is left to you as an exercise. <g>
26 comments: