What's New in WebGUI 8 #2: Auth Improvements


Originally posted as: What's New in WebGUI 8 #2: Auth Improvements on blogs.perl.org.

Auth changes

WebGUI::Auth was developed as part of 6.0.0, back in 2003. To put that into perspective:

  • Auth predates Facebook, which was founded in 2004.
  • Since Auth, there have been two Summer Olympiads.
  • Auth was written when I was still in college.

Since then, it has not fundamentally changed, though everything about the Internet surely has.

We began our planning for 8.0 with the idea to completely rebuild Auth from scratch, but that quickly got scrapped when we realized both the scope of that project. But since Auth is one of the most visible parts of a WebGUI site for the end-user, who must be able to quickly and simply create an account and log in to participate in the community of your website, we decided to take a closer look at what we could do to improve the Auth API.

What resulted were a series of small changes (and only one break) that has made it far easier to create convenient choices for your users.

Multiple Methods

One of the largest changes in how the Internet works since Auth was created was the propagation of Single-Sign-On systems like OpenID and OAuth. Now, instead of requiring a username and password for every site you visit, websites will allow you to sign in with your existing Facebook, Twitter, or Google ID. This makes it easier for users to interact with your site, which improves the return rate.

Historically, a WebGUI user would have only one way to login. If they had a username/password, they could only use that. If they were an LDAP user, they could only use that. Worse, only one Auth plugin could be chosen as "default", which means that new users could only create themselves with that single plugin.

In 7.9, we added the ability for new users to choose what they use to create their account and login. Now, by passing in the name of the auth plugin, we can offer users multiple ways to join our site. As a test, we added a Twitter-based auth plugin based on the Net::Twitter module, and a Facebook auth plugin based on Facebook::Graph.

Fig 1,294: A WebGUI site with username/password and Facebook login

In addition, once the user has logged-in via Facebook or Twitter, they have the ability to set a password. In the extremely unlikely case that Facebook or Twitter goes down, they can still get into your site with their WebGUI password.

API cleanup

As one of the few features of WebGUI that predates the Asset system (our application plugins), the Auth API stuck out from our future API direction. In order to instantiate an Auth object, you had to call the superclass constructor and rebless the object. In order to call an Auth method from the web, you had to mark it using setCallable(). In order to update the user information for the Auth method, you had to call saveParams() and pass in the name of the auth method and the user to work with (which were both known by the object anyway).

# old-style Auth plugin for Twitter
package WebGUI::Auth::Twitter;

sub new {
    my ( $class, $session, $name, $userId ) = @_;
    my $self = $class->SUPER::new( $session, $name, $userId );
    $self->setCallable( [ qw( createAccount createAccountSave displayLogin login ... ) ] );
    return bless $self, $class;

sub createAccountSave {
    my ( $self ) = @_;
    my %formData = ();
    # ... validate data coming in, and then...
    $self->saveParams( $self->user, $self->authMethod, \%formData );

Now, we've massaged the Auth API to look like our other APIs. The constructor was fixed to not require re-blessing. Instead of setCallable(), we use www_ methods to mark which methods can be called from the web. This sets those methods apart from non-web methods, and removes the need to explicitly say which methods are web methods. Finally, saveParams() was changed to update() and works the same as the other updates: Take a hash or hashref, put it in the database table.

# new-style Auth plugin for Twitter
package WebGUI::Auth::Twitter;

# Don't need to override constructor anymore! 
# no re-bless. no setCallable. authMethod name is determined automatically

sub www_createAccountSave {
    my ( $self ) = @_;
    my %formData = ();
    # ... validate data coming in, and then...
    $self->update( %formData );

The most important part of this is that now if you understand the update() and www_ methods from the Asset system, the Form system, the Workflow system, the Operation system, and the Shop system, you understand the update() and www_ methods from the Auth system. When there are many valid ways of doing something, it doesn't matter which one you pick, as long as you are consistent in that choice throughout the project.

The only downside to the new Auth API is the one backwards incompatible change: The method signature of the new() method has changed, there is no authMethod name passed in. Luckily for most people, the migration is easy: Delete your new() method, and rename your web-callable subs to start with www_ and it will just work.

Future Plans

Through our massage of the Auth API, we've gotten new ideas on how Auth plugins should work. In the future, users will be able to configure as many auth methods as they want. Those choices will all be available from a single account screen. With FormBuilder (discussed later), we can make it even easier to create full-featured Auth methods. An auth method can inherit a form, add a few fields, and present that new form to the user.

To increase flexibility, we're going to refactor the Auth API to be more easily extensible. The default Auth methods can be used as stepping-stones to more specific auth methods, decreasing the development time for your site, and making a more stable platform for your organization.

Stay tuned for next time where I'll go over our brand-new upgrade system.