03
Jan 12

Simple behind-the-scenes API authentication with OAuth2

Like many others I’ve been spending a lot of time with OAuth2 lately. The single-sign-on system we’ve built at GDS acts as a very simple oauth provider for our other apps (effectively just joining up the oauth2-provider and devise gems), and we’re probably going to be extending our API adapter code so that we can use it for those apps whose APIs need authentication.

What I’d not explored for a while was the simplest way to implement app-to-app oauth where there’s no UI for user interaction so over the New Year break I pulled something together for another project. It’s all pretty straightforward but not very well documented so I thought I’d better share.

The easiest thing to do if you want to allow an oauth client to work with your app is just to generate the ID, secret and access token for whoever’s responsible for the app and to provide them (securely) for direct use.

In order to do that in the rails app I was focussed on I knocked up a class to help me with that when using the aforementioned oauth2-provider:

and then a few rake tasks for interacting with it:

In the oauth-provider world, any “authorization” can be owned by a resource, which is any other model in your app. In a standard app like our SSO solution that’ll probably be a user, but in the app I’m working on here it’s an organisation that may have many users. You get access to that resource in your controllers with, eg:

And with that I had my API protected using everyone’s favourite standard authentication protocol.


07
Jan 11

Adding actions to Devise controllers

Adding Actions to Devise Controllers

It wasn’t the most fun I could imagine having during a “holiday season” but while holed up in Chicagoland over Christmas I spent a couple of days porting a few of my older Rails apps to use a more up to date stack: Rails 3, Devise, Inherited Resources, Formtastic, etc. The idea is that if the apps are on a stack I use every day, I’ll spend less of my time reloading old tools into my head when the inevitable tweaks are required. We’ll see how that goes.

Anyway…

Two of the apps I was working on include member profile pages. Having carefully chosen my devise model names to match the language of my app I wanted to use the same controller that’s used for registrations for other member actions. Initially I tried just extending the relevant controller and updating the route:

config/routes.rb

  devise_for :members, :controllers => { :registrations => "members" }

app/controllers/members_controller.rb

class MembersController < Devise::RegistrationsController
  def show
    ...
  end
end

but I kept getting ActionController::UnknownAction exceptions when I tried to request a member profile page.

It turns out that Devise runs a before_filter called is_devise_resource? in its controllers and that wasn’t recognising any actions that aren’t included in the core devise controllers. It also runs authenticate_scope! for relevant actions.

With that discovered it was easy enough to update my controller to

class MembersController  [:index, :show, :edit, :update]
  skip_before_filter :authenticate_scope!, :only => [:edit, :update, :destroy]

  def show
    ...
  end
end

and everything fell into place.


24
Oct 07

A Ruby on Rails OpenID Server

Since I wrote my first piece on extending a rails app to accept OpenID quite a few other tutorials and an official plugin have appeared to make that process easier. OpenID is quickly becoming quite mainstream, at least amongst developers, and that is very good news.

It’s becoming so mainstream in fact, that recently I’ve been asked to implement an OpenID server on top of an existing user database so that those users can have an easy single-sign-on option across a range of sites. Writing the server side piece is not quite so straightforward and there’s not much documentation yet. A few sample servers are available but the rails examples don’t run cleanly on the latest gems, so while I took some code from them it made most sense to start from scratch. Over the past couple of days I’ve hacked together something that works for me and even though it could still do with some polish a few notes follow. Please do use the comments to correct anything I may have gotten wrong or skipped over.

Firstly, a word on the process. The OpenID process works along the lines of:

  1. The client sends a request to the server to establish communication, shared keys, etc.
  2. Having established the keys, the client then redirects the end-user to the server to log in
  3. The server logs the end-user in however it wishes
  4. The server redirects the end-user back to the client with a query string indicating success or failure
  5. The client then confirms the success of the request with the server

I was starting from a very simple rails app that provided a Users resource and a Session resource. It’s really little more than a skeleton app with restful_authentication installed and configured. Users can log in and anyone can see a user’s page, and that’s all there is to it. I also had the ruby-openid gem installed and used a fair bit of code from the sample apps supplied with the gem.

Preparing The Existing Site

The first thing you’ll want to do is make sure that the HTML pages for your user resources have the correct headers. Chances are you will want users to be able to use their page (eg. http://yourapp.com/users/jystewart) as their OpenID but that won’t actually be where the login takes place, so you’ll want to put something like:

<link rel="openid.server" href="" />
<link rel="openid2.provider" href="" />

in the header block of your user page HTML. From that the OpenID client will know to redirect users to your sessions_url for authentication.

Preparing The Models

To my database that so far held the user details, I added four tables:

create_table :nonces do |t|
  t.string :nonce
  t.integer :created
  t.timestamps
end

create_table :associations do |t|
  t.binary :server_url, :secret
  t.string :handle, :assoc_type
  t.integer :issued, :lifetime
  t.timestamps
end

create_table :settings do |t|
  t.string :setting
  t.binary :value
  t.timestamps
end

create_table :trusts do |t|
  t.integer :user_id
  t.string :trust_root
  t.timestamps
end

The first three hold the various details we need for the negotiations with the client, and the fourth stores the details of any sites a given user has said they want to always trust so that we don’t need to confirm those details every time.

We also need an implementation of the OpenID::Store class for the OpenID library we’re using. I simply copied the one from the sample rails app distributed with the gem. With the models and that interface in place we can move on to managing the requests.

Distinguishing OpenID Requests

I decided to use a Mime::Type alias to distinguish between requests made directly in my app and those coming through OpenID. To do that I added:

Mime::Type.register_alias "text/html", :openid

in RAILS_ROOT/config/initializers/mime_types.rb and added the following code in my controllers:

before_filter :check_for_openid_request

private
  def openid_server # :nodoc:
    @openid_server ||= OpenID::Server::Server.new(ActiveRecordOpenIDStore.new)
  end

  def openid_request
    @openid_request ||= openid_server.decode_request(
      openid_params.delete_if{ |key, val|
        key == 'openid.session_type' && val == ''})
  end

  def check_for_openid_request
    @openid_params = session[:openid_params] if session[:openid_params]

    if openid_request.is_a?(OpenID::Server::CheckIDRequest)
      session[:openid_params] = @openid_params
      request.format = :openid
    end
  end

Because there are going to be several steps to our process during which we want to be able to hang on to the OpenID request details, I chose to store them in the session

Handling The Requests

Having defined our Mime::Type alias, we can now use openid as a type in respond_to blocks. The first step then is to change our SessionsController#create action from:

def create
  self.current_user = User.authenticate(params[:login], params[:password])
  if logged_in?
    if params[:remember_me] == "1"
      self.current_user.remember_me
      cookies[:auth_token] = {
        :value => self.current_user.remember_token,
        :expires => self.current_user.remember_token_expires_at }
    end

    flash[:notice] = "Logged in successfully"
    redirect_back_or_default(user_path(current_user))
  else
    render :action => 'new'
  end
end

to:

def create
  self.current_user = User.authenticate(params[:login], params[:password])
  if logged_in?
    if params[:remember_me] == "1"
      self.current_user.remember_me
      cookies[:auth_token] = {
        :value => self.current_user.remember_token,
        :expires => self.current_user.remember_token_expires_at }
    end

    respond_to do |wants|
      wants.html {
        flash[:notice] = "Logged in successfully"
        redirect_back_or_default(user_path(current_user))
      }
      wants.openid {
        if @trust = current_user.trusts.find_by_trust_root(openid_params['openid.trust_root'])
          successful_openid_login
        else
          redirect_to new_trust_url
        end
      }
    end
  else
    render :action => 'new'
  end
end

This checks for a successful login and if it’s an OpenID request then checks to see if we already trust that site. If we do, we call the ‘successful_openid_login’ method and if not, we redirect the user to ask if they want to trust the client site. The successful_openid_login method looks like:

def successful_openid_login
  session[:openid_params] = nil
  @resp = openid_request.answer(true)
  @resp.add_field(nil, 'is_valid', 'true')
  add_sreg
  render_openid_response
end

def render_openid_response
  response.headers['Content-Type'] = 'charset=utf-8'
  @resp = openid_server.encode_response(@resp)

  case @resp.code
    when OpenID::Server::HTTP_OK
      render :text => @resp.body, :status => 200
    when OpenID::Server::HTTP_REDIRECT
      redirect_to @resp.redirect_url
    else
      render :text => @resp.body, :status => 400
  end
end

And is almost entirely taken from the code supplied with the gem. I ran into some trouble with the ‘is_valid’ field not being added to the responses but being needed by my clients, so added the line

@resp.add_field(nil, 'is_valid', 'true')

The TrustsController implementation is quite straightforward and I’ll not go into details here, but will include it in the zip file you can download at the end of this entry.

This code will handle most of the process, but we have one remaining query to handle, and that is the final check_authentication request that is used to confirm everything fell into place. The actual request will come as a POST to your /session path so you will want to be ready to handle it there. I decided to implement a before_filter to intercept it and handle it separately rather than clutter my create method still further:

before_filter :handle_openid_check_auth_request

def handle_openid_check_auth_request
  if openid_params['openid.mode'] == "check_authentication"
    signatory = OpenID::Server::Signatory.new(ActiveRecordOpenIDStore.new)
    @resp = openid_request.answer(signatory)
    @resp.add_field(nil, 'is_valid', 'true')
    render_openid_response
  end
end

Once I had the code working acceptably I pulled it out into a separate module in the ‘lib’ folder. As the code matures it would probably be a good candidate for a rails plugin, but I’m not going to have time to put that together on my own for a while. If anyone wants to collaborate on that, get in touch in the comments (or by email) and maybe we can make it happen.

All the code above along with various other utility methods can be found in this zip file.


21
Feb 07

OpenID for a Ruby on Rails app

The buzz about OpenID keeps building, and with the announcement that all AOL/AIM users now automatically have OpenIDs it doesn’t look set to slow down any time soon. For those who’re not familiar with the concept, OpenID is a distributed single sign-on system that lets you set up one account and then log in to any OpenID compliant site using that account. For a better introduction, check out this screencast from Simon Willison.

There’s already been a fair bit of work done to allow ruby and rails apps to make use of open ID. There’s a gem, ported from the python library which provides APis for consumers and servers. And there’s a rails plugin that provides some generators for creating the models and controllers you need to interact with the gem.

I’ve been playing with the plugin on and off for a couple of months, but yesterday finally got round to integrating it properly with an application that I’ve been gradually developing. The app uses restful_authentication to handle its login needs, and I had to make a few customizations to get everything working as I want.

The process I adopted was to say that people can either create a standard account with a regular username and password, or they can sign in with their OpenID and then create an account in my system that will be authenticated against that OpenID. So the first step was simply to adapt the sign up form to include some explanation, and an OpenID sign in form.

I then had to update the controller that the plugin had created for me. By default it responds to a successful Open ID sign in by presenting a user with a page presenting the details of their Open ID identity, but I wanted to take it a step further.

I replaced the default code in the controller with:

when OpenID::SUCCESS
  user = User.find_by_openid_url(open_id_response.identity_url)
  if user.nil?
    session[:openid] = open_id_response.identity_url
    flash[:attributes] = open_id_fields
    flash[:notice] = 'Thank you, we have confirmed your identity. Please proceed to create your account'
    redirect_to new_user_url and return
  else
    self.current_user = user
    flash[:notice] = 'Thank you. You are now logged in'
    redirect_to "/" and return
  end

That checks whether there’s already an account for that open id. If there is, we consider the user logged in and take them to the homepage. If not, we store their openid details in the session and send them to complete the sign up.

By also adding:

params[:user][:openid_url] = session[:openid] if session[:openid]

in UsersController#create and making a few simple changes to the default User model supplied with restful_authentication so that it doesn’t require a login/password when it has an OpenID, I have an app which can take full advantage of OpenID, and my users have one less set of login details to remember.

Given the popularity of acts_as_authenticated and restful_authentication, it would be good to see a plugin which does all the work of integrating OpenID with those libraries. It shouldn’t be much work; if time allows, I may have a go, but if anyone beats me to it please let me know!


15
Feb 06

Granting and Checking Permissions with LiveUser

I was reminded by maxi_million in the comments on one of my previous LiveUser tutorial entries that I never completed the promised third entry in that series. After the initial procrastination wore off and I initially turned my mind to writing this piece, my main project using LiveUser ended up being converted (for various reasons) into a drupal site, so my further use of the library has been quite minimal. But I do have a little code sitting around, so will try and draw together a few notes on how I was using Liveuser.

While many powerful authorization systems are purely role-based, something that can be achieved in LiveUser with the groups functionality covered in the previous entry on this subject, LiveUser also supports much more fine grained permissions. Each permission is its own entity, and permissions are grouped into areas. I created my areas directly in the database so won’t be covering that here. Once you have your area’s id in the variable $area_id and an instance of LiveUser_Admin in $admin you can call:

$right = $admin->perm->addRight(
  array('area_id' => $area_id,
    'right_define_name' => 'a name for your right'));

to create the right and capture its ID in $right.

You can then ‘grant’ that right to a user with:

$admin->perm->grantUserRight(
  array('right_id' => $right,
    'perm_user_id' => $perm_user_id));

Or grant it to a group with:

$admin->perm->grantGroupRight(
  array('right_id' => $right,
    'group_id' => $group_id));

Where I ran into problems was when I attempted to check those permissions. From the API documentation, it wasn’t clear how to check for a right given only its name, but after a little exploration I put together the following method (used within my wrapper class) that handles it. $this->user is an instance of LiveUser and $this->admin is an instance of LiveUser_Admin:

function checkRight($area_id, $right_desc) {
  if (empty($this->admin)) {
    $this->getAdminInstance();
  }
  $filter = array(
    "fields" => array("right_id"),
    "filters" => array(
      "area_id" => $area_id,
      "right_define_name" => $right_desc));
  $rights = array_keys($this->admin->getRights($filter));
  $right_id = $rights[0];
  return $this->user->_perm->checkRight($right_id);
}

You’ll note that the final line makes use of the _perm property. In PEAR coding conventions a preceding underscore means a method or property is private and should not be accessed from outside the class, but this was the neatest approach I was able to find in the time. If anyone can tell me how to do this within the approved public API I’ll happily update this entry.

Update: Be sure to check out the comments, where Lukas has been adding some very useful information.


15
Aug 05

LiveUser Documentation

It’s taking me a while to get to my next installment on LiveUser but in the meantime, readers may want to check out the updated documentation that Lukas published yesterday. It’s not yet a comprehensive introduction/guide to the package, but gathering together a lot of the available information is a huge step forward.


04
Aug 05

Getting Started with LiveUser Permissions

UPDATE (Aug 9th ’05): Thanks to feedback from Lukas Smith and Laurens Nienhaus I’ve made some updates which show better ways to get at some properties.


Having described in my ‘Configuring LiveUser’ entry how to configure and instantiate LiveUser it’s now time to talk about how we start connecting together our login system with more sophisticated permissions management.

This time around we’re going to be making use of the LiveUser_Admin module, which can be instantiated using the same configuration array as LiveUser, with:

$admin = LiveUser_Admin::factory($liveuser_config);

LiveUser keeps ‘authentication’ and ‘permissions’ distinct and in order to get started with permissions we must create entries for our users in the permissions system.

The LiveUser Admin API provides us with the method:

$admin->perm->addUser($user_to_add);

to add a user to the permissions system, where $user_to_add is an array with keys matching the fields of the liveusers_perm_users table. In particular we need ‘auth_container_name’ and ‘auth_user_id’. Unfortunately there is no method which will generate this array for the currently logged in user, but we can get at the instance variables directly to perform:

$user_to_add = array();
$user_to_add['auth_container_name'] = $live_user->getProperty('containerName');
$user_to_add['auth_user_id'] = $live_user->getProperty('auth_user_id');

$admin->perm->addUser($user_to_add);

Once we have a user we can start to create permissions and grant them those permissions, but before moving on to that I wanted to set up permission groups and add users to those groups (we can also grant permissions to groups and in general this is the facility I’m likely to use most).

To add a group we make use of the addGroup() method:

$admin->perm->addGroup($group_info);

Where $group_info is an array with the keys ‘group_define_name’ (string) and ‘is_active’ (boolean). I used it as:

$group = $admin->perm->addGroup(array(
'group_define_name' => 'admins',
'is_active' => 1));

Having set up the group we’ll want to add users to that group, which is achieved through the method:

$admin->perm->addUserToGroup($data);

Once again the parameter is an associative array with the keys ‘group_id’ and ‘perm_user_id’. The return value from addGroup will have given us the group ID, or we can retrieve it using the getGroups method:

$params = array(
'fields' => array('group_id'),
'filters' => array('group_define_name' => 'admin'));
$group_details = $admin->perm->getGroups($params);

which will return something like:

Array
(
[0] => Array
(
[group_id] => 2
)

)

Meaning we can add our user to the group using:

$group_info['group_id'] = $group_details[0]['group_id'];
$group_info['perm_user_id'] = $live_user->getProperty('permUserId');;
$admin->perm->addUserToGroup($group_info);

So there we have it, our ‘admins’ group is defined and our current user is a member of it. Next time I’ll cover how we use this to create, edit, grant, revoke and check rights.


29
Jul 05

Configuring LiveUser

For a current (PHP) project I need a highly flexible authentication and permissions management system. Having heard plenty of mentions on the various PEAR lists I decided to try out LiveUser, which seems to currently be the most comprehensive such system for PHP. It’s a large codebase that seems to cover most of the necessary permutations and includes a number of prominent coders amongst its developers.

What I hadn’t taken into account, of course, was the lack of documentation. I spent quite a while trawling through their wiki and googling before concluding that the best way to work LiveUser out would be to work through the code piece by piece. And since that’s a long, slow process it seemed worth documenting here. I’m going to do that step by step, starting today with configuration and instantiation.

LiveUser’s initial configuration is managed through a large associative array that should be passed to the factory in order to instantiate the core object. Unlike some of the other arrays used as arguments to functions, this one is documented inline, but it still took a while to pick apart.

Here’s an example:

$liveuser_config = array(

First up we have some core parameters for LiveUser

autoInit determines whether or not the class tries to instantiate the various ‘containers’ when it is first called

'autoInit' => true,

the login parameter allows us to specify whether login should be attempted off-the-bat and whether or not the session ID should be regenerated after a successful login.

'login' => array(
'force' => true,
'regenid' => true),

With the core parameters set, we can move onto the ‘containers’. LiveUser employs containers to allow for flexibility. Containers can be thought of as ‘plugins’ that allow access to any sort of authentication or permissions management system you want to connect.

First up I define my permissions container. There are three container types that come with LiveUser out of the box: ‘simple’, ‘medium’, and ‘complex’. I’m looking for as much flexibility as possible so am using ‘Complex’.

We then provide some ‘storage’ parameters. LiveUser provides several storage option for permissions (primarily PEAR::DB, MDB and MDB2) and you can add extras in. Here I am using PEAR::DB and so specify a DSN and the prefix I’m using for my tables.

If I already had a DB object instantiated, I could remove the ‘dsn’ parameter and replace it with ‘connection’ which would be a reference to my existing connection.

'permContainer' => array(
'type' => 'Complex',
'storage' => array(
'DB' => array(
'dsn' => $dsn,
'prefix' => 'liveuser_'))),

For me, one of the key draws of LiveUser for me was the fact that I could authenticate against multiple sources for one application. This application includes a directory for a membership-based organisation, and I want all members to be able to login. There are also a few administrators who are not members, but need to be able to log in to the site.

authContainers is itself an associative array, with the keys being the names by which you want to address the different authentication sources. In this case ‘members’ and ‘admins’. You can specify a few options that should be familiar to anyone who’s used PHP’s sessions and then a few others. I’m again using the PEAR::DB interface for my authentication so I specify type as ‘DB’.

Options for the DB container are then given in the ‘storage’ parameter. As with the permissions, ‘dsn’ can be replaced with ‘connection’. ‘alias’ is an array that maps the table (‘users’) and fields that LiveUser’s DB container uses by default to the table and fields that we have in our application.

'authContainers' => array(
'members' => array(
'type'            => 'DB',
'loginTimeout'    => 0,
'expireTime'      => 3600,
'idleTime'        => 1800,
'updateLastLogin' => false,
'allowDuplicateHandles' => false,
'allowEmptyPasswords'   => false,
'passwordEncryptionMode' => 'PLAIN',
'storage' => array(
'dsn' => $dsn,
'prefix' => '',
'alias' => array(
'users' => 'member',
'auth_user_id' => 'id',
'is_active' => 'status',
'handle' => 'username',
'passwd' => 'password'))),
'admins' => array(
'type'            => 'DB',
'loginTimeout'    => 0,
'expireTime'      => 3600,
'idleTime'        => 1800,
'updateLastLogin' => false,
'allowDuplicateHandles' => false,
'allowEmptyPasswords'   => false,
'passwordEncryptionMode' => 'MD5',
'storage' => array(
'dsn' => $dsn,
'prefix' => '',
'alias' => array(
'users' => 'admins',
'auth_user_id' => 'id',
'is_active' => 'id',
'handle' => 'username',
'passwd' => 'password')))));

With that array set up, LiveUser can easily be instantiated using:

$live_user = LiveUser::factory($liveuser_config, $username, $password);

or

$live_user = LiveUser::singleton($liveuser_config, $username, $password);

and we can check whether the user is now logged in with

$live_user->isLoggedIn() which returns true if login succeeded, false if it failed.

(NB: LiveUser’s own database schema is distributed within the examples that come in the PEAR package. It is provided as an MDB-compliant XML file)