OpenID Authentication using Events (ASP.NET)Use the OpenIdClient class to perform authentication requests, and respond to authentication responses from OpenID Providers. using ExtremeSwank.OpenId;
using ExtremeSwank.OpenId.PlugIns.Extensions;
public partial class _Default
{
protected void Page_Load(object sender, EventArgs e)
{
// If this is not a postback, start up the Consumer
// and handle any OpenID response messages, if present
if (!IsPostBack)
{
OpenIdClient openid = GetConsumer();
// Read the arguments in the current request and
// automatically validate any OpenID responses,
// firing events when actions occur.
openid.DetectAndHandleResponse();
}
}
protected OpenIdClient GetConsumer()
{
// Initialize a new OpenIdClient, reading arguments
// from the current request, and using Session and
// Application objects to store data. For more
// flexibility, see "Disabling Stateful Mode" and
// "Persisting Stateful Data" below for more information.
OpenIdClient openid = new OpenIdClient();
// Subscribe to all the events that could occur
openid.ValidationSucceeded += new EventHandler(openid_ValidationSucceeded);
openid.ValidationFailed += new EventHandler(openid_ValidationFailed);
openid.ReceivedCancel += new EventHandler(openid_ReceivedCancel);
// Subscribing to SetupNeeded is only needed if using immediate authentication
openid.ReceivedSetupNeeded += new EventHandler(openid_SetupNeeded);
return openid;
}
// This is the OnClick event for the submit button next to the OpenID text box.
protected void Button_Click(object sender, EventArgs e)
{
// Create an OpenIdClient using default settings
// (see Page_Load() above)
OpenIdClient openid = GetConsumer();
// Set the Identity to what the user entered on the form.
openid.Identity = LoginBox1.Text;
// Do an immediate request (go to the OpenID Provider to see if the user
// is logged in - if not, will immediately come back and fire a SetupNeeded
// event. If you'd rather do a normal request, use BeginAuth() or
// BeginAuth(false, true).)
openid.CreateRequest(true, true);
}
// The following events were registered in the GetConsumer() method above.
protected void openid_ReceivedCancel(object sender, EventArgs e)
{
// Request has been cancelled. Respond appropriately.
}
protected void openid_ValidationSucceeded(object sender, EventArgs e)
{
// User has been validated! Respond appropriately.
OpenIdUser thisuser = ((OpenIdClient)sender).RetrieveUser();
}
protected void openid_ValidationFailed(object sender, EventArgs e)
{
// Validating the user has failed. Respond appropriately.
}
protected void openid_SetupNeeded(object sender, EventArgs e)
{
// Immediate authentication response showed that the user isn't logged in.
// You should redirect the user to OpenID Provider to continue setup.
((OpenIdClient)sender).CreateRequest(false, true);
}
}OpenID Authentication Using Inline Handling (ASP.NET)The older (and still compatible) method of using OpenIdClient, is looking at the current RequestedMode, and determining what needs to be done: using ExtremeSwank.OpenId;
public partial class _Default
{
protected void LoginButton_Click(object sender, EventArgs e)
{
OpenIdClient openid = new OpenIdClient();
openid.Identity = LoginBox1.Text;
openid.CreateRequest();
}
protected void LogOutButton_Click(object sender, EventArgs e)
{
Session["OpenID_UserObject"] = null;
// Handle user logout here
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
OpenIdClient openid = new OpenIdClient();
switch (openid.RequestedMode)
{
case RequestedMode.IdResolution:
if (openid.ValidateResponse())
{
OpenIdUser thisuser = openid.RetrieveUser();
Session["OpenID_UserObject"] = thisuser;
// Authentication successful - Perform login here
}
else
{
// Authentication failure handled here
}
break;
case RequestedMode.CanceledByUser:
// User has cancelled authentication - handle here
break;
}
}
}
}Using OpenIDControlAs an alternative to using the class directly, consider using the OpenIDControl UserControl, which automatically handles everything for you: <%@ Register src="OpenIDControl.ascx" TagName="OpenIDControl" TagPrefix="uc1" %>
<form id="form1" method="get">
<uc1:OpenIDControl runat="server" ID="OpenIDControl1"
RequiredFields="nickname,email" OptionalFields="dob,gender,fullname"
OnValidateSuccess="OpenID_ValidateSuccess" />
</form> You can process the data received by hooking in to the OnValidateSuccess event: protected void LoginValidated(object sender, EventArgs e) {
OpenIDControl openidcontrol = (OpenIDControl)sender;
string UserOID = openidcontrol.UserObject.Identity;
string BaseOID = openidcontrol.UserObject.BaseIdentity;
string FullName = openidcontrol.UserObject.GetValue(SimpleRegistrationFields.FullName);
string NickName = openidcontrol.UserObject.GetValue(SimpleRegistrationFields.Nickname);
// Continue processing data
}
protected void Logout(object sender, EventArgs e) {
// Perform some proprietary logout functions
}OpenIDControl supports the following events: - OnValidateSuccess - Fires when validation is successful
- OnValidateFail - Fires when validation has failed
- OnLogin - Login button has been clicked, event fires before authentication processing
- OnLogout - Logout button has been clicked, event fires before user data is destroyed
- OnRemoteCancel - User has cancelled authentication request and has been returned
Working Outside ASP.NETThere are a few situations where the code may not be running in an ASP.NET environment. Here is simple example of creating static methods that implement a simple OpenID authentication check. You will likely make something a bit more complete in an actual implementation. using System.Collections.Specialized;
using ExtremeSwank.OpenId;
using ExtremeSwank.OpenId.PlugIns.Extensions;
using System;
public class MyClass
{
private static OpenIdClient GetConsumer(NameValueCollection arguments)
{
// Initialize the Consumer in Stateless mode
OpenIdClient openid = new OpenIdClient(arguments);
// Initialize any required plug-ins here
return openid;
}
public static Uri InitiateAuthentication(string identity)
{
// Get a new OpenIdClient object. Since we aren't responding to
// a response, pass an empty NameValueCollection.
OpenIdClient openid = GetConsumer(new NameValueCollection());
// Set Identity to the supplied OpenID Identity
openid.Identity = identity;
// Discover the OpenID Provider from the Identity and
// return the redirect URL for the browser. Since we are
// not in an ASP.NET session, we shouldn't try to automatically
// redirect the user.
return openid.CreateRequest(false, false);
}
// Confirm whether or not a HTTP request is a valid and positive authentication
// assertion. You will need to pass the received arguments as a NameValueCollection,
// which is the type of collection implemented by the Request object in ASP.NET.
public static OpenIdUser HandleAuthResponse(NameValueCollection arguments)
{
OpenIdClient openid = GetConsumer(arguments);
OpenIdUser thisuser = null;
switch (openid.RequestedMode)
{
case RequestedMode.IdResolution:
// Validate the response. If successful, populate "thisuser".
if (openid.ValidateResponse())
{
thisuser = openid.RetrieveUser();
}
break;
// Handle other cases as needed
}
// Return the retrieved user object. If authentication
// failed, return null.
return thisuser;
}
}To start authentication, you would: Uri url = MyClass.InitiateAuthentication("myname.myprovider.com");
// Redirect the web browser to the URL in "url" here. If "url" is null,
// discovery was not successful.To confirm an authentication response, you would: OpenIdUser user = MyClass.HandleAuthResponse(arguments);
// Use the "user" object. If "user" is null, then the response did not
// pass validation. You will have to figure out what is needed to pass the redirect URL to the browser, and to pass the arguments from the request to the static method. Since an ASP.NET context is not available, OpenIDConsumer will not be able to automatically pull in that information. Using Directed IdentityThere are some situations where: - You only support authentication from a small number of OpenID Providers
- You would rather not have the user type in their OpenID into a text box
These requirements can be fulfilled by using the Directed Identity feature. In this mode, the OpenID Consumer will receive the user's OpenID from the OpenID Provider after the user has logged in. You will have to know the URL of the OpenID Provider you want the user to log in to, and the OpenID Provider must support OpenID 2.0. using ExtremeSwank.OpenId;
public partial class _Default
{
protected void LoginButton_Click(object sender, EventArgs e)
{
OpenIdClient openid = new OpenIdClient();
// Enable Directed Identity
openid.UseDirectedIdentity = true;
// Use Yahoo! OpenID Provider
openid.ProviderUrl = new Uri("https://open.login.yahooapis.com/openid/op/auth");
openid.CreateRequest();
}
...
}Enabling Stateful ModeOpenIDClient defaults to Stateless mode, which although it simpler to implement, requires more repetitive work to be done at the OpenID Provider. You can enable stateful mode using EnableStatefulMode(). For ASP.NET environments: OpenIdClient openid = new OpenIdClient();
openid.EnableStatefulMode(associationManager, sessionManager); For non-ASP.NET environments: NameValueCollection receivedArguments;
OpenIdClient openid = new OpenIdClient(receivedArguments);
openid.EnableStatefulMode(associationManager, sessionManager); Persisting Stateful DataBy default, Stateful mode keeps track of data using the built-in ASP.NET Session and Application objects. This does not perform well in web garden or web farm environments, where different requests may be handled by different instances of the application, or on different servers in the environment. You can save information to a central repository using the ISessionPersistence and IAssociationPersistence interfaces. There are several built-in classes that have already been created which use these interfaces, and are fairly easy to implement in your own code. The example below uses the DbAssociationManager and DbSessionManager classes to implement stateful data persistence. First, use the BuildDb() method to create the table that will store your associations. You will only need to do this once. using ExtremeSwank.Authentication.OpenID.Persistence;
protected void SetupDatabase()
{
// Set up your database connection here
IDbConnection conn = new MySqlConnection(connectionString);
// Create the association manager using the connection string and a prefix that
// will be applied to all table names
DbAssociationManager assocmgr = new DbAssociationManager(conn, "Prefix_");
assocmgr.BuildDb();
// If you want to use database session persistence, set it up here
// SessionID is some value that is unique to the user's session.
DbSessionManager sessmgr = new DbSessionManager(conn, "Prefix_", sessionID);
sessmgr.BuildDb();
}Then, just use the OpenIdClient object normally, but create and use a DbAssociationManager instance: using ExtremeSwank.OpenId;
using ExtremeSwank.OpenId.Persistence;
public partial class _Default
{
public OpenIdClient InitConsumer()
{
// Set up your database connection here
IDbConnection conn = new MySqlConnection(connectionString);
// Create the association manager using the connection string and a prefix that
// will be applied to all table names
DbAssociationManager assocmgr = new DbAssociationManager(conn, "Prefix_");
// If you want to use database session persistence, set it up here
// SessionID is some value that is unique to the user's session.
DbSessionManager sessionmgr = new DbSessionManager(conn, "_Prefix", sessionID);
// Initialize the OpenIDConsumer object
OpenIdClient openid = new OpenIdClient();
openid.EnableStatefulMode(assocmgr, sessionmgr);
return openid;
}
protected void LoginButton_Click(object sender, EventArgs e)
{
OpenIdClient openid = InitConsumer();
openid.Identity = LoginBox1.Text;
openid.CreateRequest();
}
...
}Simple Registration ExtensionSimple Registration is a simple OpenID extension that provides for getting the most common information for website registration from the OpenID Provider. using ExtremeSwank.OpenId;
using ExtremeSwank.OpenId.PlugIns.Extensions;
using System;
using System.Collections.Specialized;
public partial class _Default
{
public OpenIdClient InitConsumer()
{
OpenIdClient openid = new OpenIdClient();
SimpleRegistration sr = new SimpleRegistration(openid);
// Get the user's full name, which is required
sr.AddRequiredFields(SimpleRegistrationFields.FullName);
// Get the user's e-mail address, which is optional
sr.AddOptionalFields(SimpleRegistrationFields.Email);
// Set the URL where your privacy policy is posted.
// This may be presented to the user at the OpenID Provider.
sr.PolicyUrl = new Uri("http://myhost.com/Policy.html");
return openid;
}
protected void LoginButton_Click(object sender, EventArgs e)
{
OpenIdClient openid = InitConsumer();
openid.Identity = LoginBox1.Text;
openid.CreateRequest();
}
...
}When validation has succeeded, get the OpenIDUser object and collect the value(s): OpenIdUser user = openid.RetrieveUser();
// Retrieve data using the same SchemaDefs, but retrieve using the alias
string fullname = user.GetValue(SimpleRegistrationFields.FullName);
string email = user.GetValue(SimpleRegistrationFields.Email); Attribute Exchange ExtensionAttribute Exchange is a newer OpenID extension supported by some providers. It provides an interface to fetch and store values using schema namespaces. A schema definition is a namespace URI and a configurable alias. For instance, a user's username can have the following namespace: http://openid.net/schema/namePerson/friendly For the authentication request, we might use the alias "Username" to make it more easily accessible. For convenience, most available namespaces and default aliases are available in the AttributeExchangeSchema static class. Each member consists of a SchemaDef object which includes both the namespace URI and a default alias. Here is a fetch example: using ExtremeSwank.OpenId;
using ExtremeSwank.OpenId.PlugIns.Extensions;
using System;
public partial class _Default
{
public OpenIdClient InitConsumer()
{
OpenIdClient openid = new OpenIdClient();
AttributeExchange ax = new AttributeExchange(openid);
ax.Mode = AttributeExchangeMode.Fetch;
// Get the user's full name, return only one value, and
// tell the OpenID Provider that it is required
ax.AddFetchItem(AttributeExchangeSchema.FullName, 1, true);
// Get the user's website, request 2 values (user can
// have more than one site), and a response is optional
ax.AddFetchItem(AttributeExchangeSchema.UrlWebsite, 2, false);
return openid;
}
protected void LoginButton_Click(object sender, EventArgs e)
{
OpenIdClient openid = InitConsumer();
openid.Identity = LoginBox1.Text;
openid.CreateRequest();
}
// ...
}When we're ready to retrieve the response, get the OpenIDUser object and collect the value(s): OpenIdUser user = openid.RetrieveUser();
// Retrieve data using the same SchemaDefs, but retrieve using the alias
string fullname = user.GetValue(AttributeExchangeSchema.FullName.Uri);
string urls = user.GetValue(AttributeExchangeSchema.UrlWebsite.Uri);
// Both URLs are included in the same string, separated by ", " You can also store values at the OpenID Provider, if supported by the Provider: using ExtremeSwank.OpenId;
using ExtremeSwank.OpenId.PlugIns.Extensions;
using System;
public partial class _Default
{
public OpenIdClient InitConsumer()
{
OpenIdClient openid = new OpenIdClient();
AttributeExchange ax = new AttributeExchange(openid);
ax.Mode = AttributeExchangeMode.Store;
// Set the user's full name
ax.AddStoreItem(AttributeExchangeSchema.FullName, "Your Name");
// Set the user's websites, one value for each
ax.AddStoreItem(AttributeExchangeSchema.UrlWebsite, "http://mypage.com/", "http://mybizpage.com/");
return openid;
}
protected void LoginButton_Click(object sender, EventArgs e)
{
OpenIdClient openid = InitConsumer();
openid.Identity = LoginBox1.Text;
openid.CreateRequest();
}
// ...
}
|
In the "Working Outside ASP.NET" section, you need to set TrustRoot? and ReturnUrl? before calling CreateRequest?:
If you don't set ReturnUrl?, the ExtremeSwank?.OpenId?.Utility.GetRedirectUrl? method will blow up with a NullReferenceException?.