Registrar: Standardized Multi-Provider Registration

Gem Version

Gem | Source | Documentation

Introduction

Registrar standardizes the authentication process through Rack Middleware and works well with common authentication mechanisms like OmniAuth.

Description

You can think of Registrar as a thin wrapper around your sign up / sign in process.

Registrar already has build in support for the OmniAuth Auth Hash Schema.

If you want to use a different authentication mechanism feel free to implement your own AuthBuilder.

Getting Started

Let us start with a short example.

I'm using a fork (see here why i use a fork) of the omniauth-facebook-access-token OmniAuth strategy to authenticate my user.

Add registrar and the authenticaton mechanism you want to use to your Gemfile

gem 'registrar'
gem 'omniauth-facebook-access-token' # we are just using one OmniAuth strategy here

Add the authentication mechanism of your choice

require 'omniauth-facebook-access-token'

app = Rack::Builder.app do
  # Store authenticated user in env['omniauth.auth']
  use OmniAuth::Builder do
    provider omniauth-facebook-access-token
  end

  run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] }
end

run app

Add a appropriate AuthBuilder to your Middleware Stack to transform the previous authentication result into registrar schema.

require 'registrar'
require 'omniauth-facebook-access-token'

app = Rack::Builder.app do
  # Store authenticated user in env['omniauth.auth']
  use OmniAuth::Builder do
    provider omniauth-facebook-access-token
  end

  # Transform the OmniAuth Schema env['omniauth.auth'] into the Registrar Schema env['registrar.auth']
  use Registrar::AuthBuilder::OmniAuth

  run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] }
end

run app

Go to the Facebook Graph API Explorer, generate an access token and copy it to your clipboard.

Start the application, visit localhost:port/auth/facebook_access_token and paste the access token from your clipboard. Click the submit button.

env['omniauth.auth'] returns the schema OmniAuth builds for you

!ruby/hash:OmniAuth::AuthHash
  provider: facebook
  uid: '100000100277322'
  info: !ruby/hash:OmniAuth::AuthHash::InfoHash
    email: [email protected]
    name: Jan Ow
    first_name: Jan
    last_name: Ow
    image: http://graph.facebook.com/100000100277322/picture
    urls: !ruby/hash:OmniAuth::AuthHash
      Facebook: http://www.facebook.com/100000100277322
    location: Bochum, Germany
    verified: true
  credentials: !ruby/hash:OmniAuth::AuthHash
    token: CAACEdEose0cBABZBEayxJNmeCRdvrwT6RbiEbtYUyAZCY24E5xxCoPQJ0oCVR8XFsUEtTpnMjwrRMwvjliQe2xDRM2c76ONriaNQaMwuAKH1YjQki9lK8evIkN18TqPopB1blbeRuOIkes2l4JQ3Ga7HL9vHXHqhAjcbuZCHKhtOJMulZAN1wfWMlOxF7bBZCW0TdzJz654CW7ErAsIPj
    expires: false
  extra: !ruby/hash:OmniAuth::AuthHash
    raw_info: !ruby/hash:OmniAuth::AuthHash
      id: '100000100277322'
      email: [email protected]
      first_name: Jan
      gender: male
      last_name: Ow
      link: http://www.facebook.com/100000100277322
      location: !ruby/hash:OmniAuth::AuthHash
        id: '106544749381682'
        name: Bochum, Germany
      locale: en_US
      name: Jan Ow
      timezone: 1
      updated_time: '2015-01-10T11:52:30+0000'
      verified: true

env['registrar.auth'] returns the schema which registrar builds (in this case Registrar::AuthBuilder::OmniAuth)

  provider:
    name: facebook
    uid: '100000100277322'
    access_token: CAACEdEose0cBABZBEayxJNmeCRdvrwT6RbiEbtYUyAZCY24E5xxCoPQJ0oCVR8XFsUEtTpnMjwrRMwvjliQe2xDRM2c76ONriaNQaMwuAKH1YjQki9lK8evIkN18TqPopB1blbeRuOIkes2l4JQ3Ga7HL9vHXHqhAjcbuZCHKhtOJMulZAN1wfWMlOxF7bBZCW0TdzJz654CW7ErAsIPj
  profile:
    name: Jan Ow
    email: [email protected]
    location: Bochum, Germany
    image: http://graph.facebook.com/100000100277322/picture
  trace:
    ip: 127.0.0.1
    user_agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)
      Chrome/40.0.2214.91 Safari/537.36
    timestamp: '1427800292'

Currently, the AuthBuilder just transforms the Hash into a different structure.

To do something with the authenticated user (e.g. sign up or sign in), use the profile builder.

Open up you Middewarestack again.

require 'registrar'
require 'omniauth-facebook-access-token'

app = Rack::Builder.app do
  # Store authenticated user in env['omniauth.auth']
  use OmniAuth::Builder do
    provider omniauth-facebook-access-token
  end

  # Transform the OmniAuth Schema env['omniauth.auth'] into the Registrar Schema env['registrar.auth']
  use Registrar::AuthBuilder::OmniAuth

  # Handle Registrar Schema env['registrar.auth'] and store processed result into env['registrar.profile']
  use Registrar::ProfileBuilder, Proc.new {|schema| #find_or_create_profile_in_persistence }

  run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] }
end

run app

In this case I passed the Registrar Schema to an application related service called ProfileRegister which returned me a application specific Profile.

This profile is stored in env['registrar.profile']

!ruby/object:ProfileRegister::Services::ProfileBoundary::Profile
  o: 
  t:
    :profile_uid: '1'
    :provider: facebook
    :access_token: a39147c2f57f3797f58a
    :external_uid: '100000100277322'
    :display_name: Jan Ow
    :country_code: 
    :avatar: http://graph.facebook.com/100000100277322/picture
    :email: [email protected]
    :terms_accepted: false
    :registration_platform: 
    :gender: 
    :language: 
    :birthday: 
    :fresh: true
    :last_login: &1 !ruby/object:ProfileRegister::Services::ProfileBoundary::LastLogin
      o: 
      t:
        :time: 2015-03-31 11:11:32.000000000 Z
        :address: 127.0.0.1
        :user_agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like
          Gecko) Chrome/40.0.2214.91 Safari/537.36

If your application supports the concept of a session your could store env['registrar.profile'] in your session to log the user in.

if env['registrar.profile']
  @session['current_profile'] = env['registrar.profile']
end

If you decide to do this you could also add some helper methods.

def current_profile
  @session['current_profile']
end

def logged_in?
  !!current_profile
end

def logged_out?
  !logged_in?
end

If you are using Rails you should probably check out registrar-rails which gives you a small interface to configure your middleware as well as some helper methods as suggested above.

Contributing

  1. Check out the Wiki
  2. Fork it ( https://github.com/JanOwiesniak/registrar/fork )
  3. Create your feature branch (git checkout -b my-new-feature)
  4. Commit your changes (git commit -am 'Add some feature')
  5. Push to the branch (git push origin my-new-feature)
  6. Create a new Pull Request