Authenticate All the Things with oPRO, the Basics

For the past few months, I’ve written a bunch of topics covering numerous authentication solutions from low-level (AuthLogic) to full-fledged ones (Devise). these days we tend to area unit planning to discuss OAuth two all over again, but now I’ll show you the way to make your own authentication supplier and customise it pro re nata.

Today’s guest is oPRO (short for OAuth Provider) created by Richard Schneeman. this is often a Rails engine that permits you to make your own authentication supplier quickly and simply. oPRO comes with a bunch of pre-defined settings, controllers, views, and models. Most of those things may be extended or changed additional.

If, for a few reason, you've got ne'er worked with the OAuth two protocol, you'll be able to browse this introductory tutorial or talk to my article on exploitation OAuth two with Rails. to place it merely, this is often a protocol that permits some third-party application to realize restricted access to a service on a user’s behalf. The cool issue concerning it's the user will management what action(s) the app will perform while not access to the user’s parole.

oPRO makes solely 2 assumptions concerning your setup:

You have some reasonably a user model for authentication
You are exploitation ActiveRecord
To see it in action and browse some docs, examine this sample app.

This article can accommodates 3 elements. we'll cowl plenty of various things:

Preparing authentication supplier (let’s decision it “server app”)
Preparing demo shopper application (“client app”)
Setting up basic authentication at the server app
Integrating oPRO
Providing basic OAuth work flow
Customizing the default views and controllers
Coding an easy API adapter
Adding custom knowledge to the authentication hash
Working with scope
Adding practicality to refresh tokens
Rate limiting
Introducing a custom authentication answer for the server app
Exchanging user credentials for associate degree access token
This appears like plenty of labor to try and do, however I’ll be there to guide you on the approach, therefore concern not!


The source code for the server and client applications can be found on GitHub.

Preparing Server Application

Take a deep breath and create a new Rails app. This is going to be our authentication provider:
$ rails new OproServer -T
For this demo I am using Rails 4.2, but oPRO is compatible with Rails 3.1 and 3.2, as well.
We will require two gems for now:
Gemfile
[...]
gem 'opro'
gem 'devise'
[...]
Devise is suggested as the default authentication solution by oPRO and, therefore, we will stick to it (however I will give you some instructions on using different authentication mechanisms).
Run
$ bundle install
Now let’s setup Devise. I am not going to provide a deep explanation, but if you want to learn more refer to this article.
Run the devise generators to provide some basic configuration and create a User model:
$ rails generate devise:install
$ rails generate devise User
Tweak the config/initializers/devise.rb file as necessary. Also, modify the layout to display flash messages as Devise relies on them:
views/layouts/application.html.erb
[...]
<% flash.each do |key, value| %>
  <div class="alert alert-<%= key %>">
    <%= value %>
  </div>
<% end %>
[...]
For this demo I’m not going to use any styling at all, because we have a lot of other things to do. Now run another generator to create an initializer file and mount routes for oPRO:
$ rails g opro:install
Lastly apply all the migrations:
$ rake db:migrate

Registering a New Client Application

By default, oPRO comes with a documentation page, so boot your server and navigate tolocalhost:3000/oauth_docs to access it. If it does not show up, make sure you haven’t skipped anything from the previous section.
When you are ready, create a static pages controller, a root route and a view:
pages_controller.rb
class PagesController < ApplicationController
end
config/routes.rb
[...]
root to: 'pages#index'
[...]
views/pages/index.html.erb
<h1>Welcome to my Auth provider!</h1>
<%= link_to 'Register a new client app', new_oauth_client_app_path %>
This link leads to a page where clients can register their applications, just like they do when receiving their key pair on Twitter or Facebook. You’ll be asked to authenticate when visiting this page, so register a sample user. On the “Create An OAuth Client Application” page enter your app’s name and click Create (the name can be changed later). You will be presented with client id and secret key pair, so leave this page open for now.
The next thing we need to do is create a client application, so proceed to the next step!

Preparing Client Application

Create yet another Rails app called OproClient:
$ rails new OproClient -T
We need a place to store our client ID and secret keys received on the previous step. Of course, we might place it right into the code, but that’s not very secure, so I’ll stick with environmental variables. In development mode values will be loaded from the config/local_env.yml file:
config/local_env.yml
opro_client_id: 'your_id'
opro_client_secret: 'your_secret'
opro_base_url: 'http://localhost:3000'
For convenience I’ve also included server’s base URL.
Now tweak the application.rb file to set ENV properly:
config/application.rb
[...]
if Rails.env.development?
  config.before_configuration do
    env_file = File.join(Rails.root, 'config', 'local_env.yml')
    YAML.load(File.open(env_file)).each do |key, value|
      ENV[key.to_s] = value
    end if File.exists?(env_file)
  end
end
[...]
Now you have access to ENV['opro_client_id']ENV['opro_client_secret'], andENV['opro_base_url'] from your code.
Don’t forget to exclude local_env.yml from version control:
.gitignore
[...]
config/local_env.yml
Add a static pages controller and a welcoming page:
pages_controller.rb
class PagesController < ApplicationController
end
config/routes.rb
[...]
root to: 'pages#index'
[...]
views/pages/index.html.erb
<h1>Welcome!</h1>
<%= link_to 'Authenticate via oPRO', "#{ENV['opro_base_url']}/oauth/new?client_id=#{ENV['opro_client_id']}&client_secret=#{ENV['opro_client_secret']}&redirect_uri=%2F%2Flocalhost%3A3001%2Foauth%2Fcallback" %>
There are a couple of things to note:
  • /oauth/new is the default oPRO’s route to authenticate users via OAuth.
  • You have to pass your client_id and client_secret for authentication to work correctly (later we will learn that this is not the only way).
  • You also have to specify a redirect_uri – this is where users will be redirected after authentication takes place.
So, as you can see, this is very similar to what other OAuth 2 providers do.
Not sure about you, but to me, this URL seems a bit too long so I’d like to move it somewhere else:
application_controller.rb
[...]
private

def new_opro_token_path
  "#{ENV['opro_base_url']}/oauth/new?client_id=#{ENV['opro_client_id']}&client_secret=#{ENV['opro_client_secret']}&redirect_uri=%2F%2Flocalhost%3A3001%2Foauth%2Fcallback"
end

helper_method :new_opro_token_path
[...]
I am placing it inside the controller, because later we’ll need to call it from other actions as well.
views/pages/index.html.erb
<h1>Welcome!</h1>
<%= link_to 'Authenticate via oPRO', new_opro_token_path %>
Now we need to setup a callback URL.

Callback URL

First of all, provide a new route for your client app:
config/routes.rb
[...]
get '/oauth/callback', to: 'sessions#create'
[...]
Now we need a SessionsController with a create action. When a user is redirected to this action, acode parameter will be sent by oPRO, so the URL will look like http://localhost:3001/?code=123(we are using port 3001, because 3000 is already occupied by the server – don’t forget about it!). This code is then used to obtain the actual access token by sending an HTTP POST request tohttp://localhost:3000/oauth/token.json along with client ID and secret.
Therefore, we need a library to perform HTTP requests. There are multiple solutions available and of course you are free to choose your favorite, but I’m going to stick with rest-client because is simple and yet powerful.
Gemfile
[...]
gem 'rest-client'
[...]
Run
$ bundle install
and now create a new controller:
sessions_controller.rb
class SessionsController < ApplicationController
  def create
    response = JSON.parse RestClient.post("#{ENV['opro_base_url']}/oauth/token.json",
                    {
                        client_id: ENV['opro_client_id'],
                        client_secret: ENV['opro_client_secret'],
                        code: params[:code]
                    },
                    accept: :json)
    session[:access_token] = response['access_token']
    session[:refresh_token] = response['refresh_token']
    redirect_to root_path
  end
end
Using RestClient.post we send a POST request to http://localhost:3000/oauth/token.jsonand set the three required parameters. oPRO responds with JSON containing access and refresh tokens as well as an expires_in field saying how soon the token is going to become invalid (by default it does not expire at all, so we will put it aside for now).
Then, we use the generic json gem to parse the response and store tokens in the user’s session, redirecting them back to the main page.

Sample API Request

So far so good, but we need to test that the token is actually working by performing some request. Luckily, oPRO comes with a sample routehttp://localhost:3000/oauth_tests/show_me_the_money.json that responds with a success message only if the access token is valid.
Therefore, create a new controller:
api_tests_controller.rb
class ApiTestsController < ApplicationController
  def index
    redirect_to root_path and return unless session[:access_token]
    @response = JSON.parse RestClient.get("#{ENV['opro_base_url']}/oauth_tests/show_me_the_money.json",
                                          params: {
                                              access_token: session[:access_token]
                                          },
                                          accept: :json)
  end
end
If the access token is not set, simply redirect the user back to the main page. Otherwise, send a GET request to the sample route, provide the access token as the sole parameter and then parse the response.
In the corresponding view display the message returned by the server app:
views/api_tests/index.html.erb
<%= @response['message'] %>
Don’t forget to add the route:
config/routes.rb
[...]
resources :api_tests, only: [:index]
[...]
Lastly, modify the main page view:
views/pages/index.html.erb
<h1>Welcome!</h1>
<% if session[:access_token] %>
  <%= link_to 'Show some money', api_tests_path %>
<% else %>
  <%= link_to 'Authenticate via oPRO', new_opro_token_path %>
<% end %>
Now boot your server (don’t forget that port 3000 is occupied):
$ rails s -p 3001
and check how this all is working. When clicking the “Show some money” link you’ll see an “OAuth worked!” message – this means that we are on the right track.
If you are getting a 401 error message, that means that you are either not sending a token or it is invalid. Double check your code – you probably missed something while executing the previous steps.

Customizing Controllers and Views

You’ll likely want to customize the views or exclude some default controller provided by oPRO (for example, the controller with docs). This can be done by passing a hash of arguments to themount_opro_oauth method inside your config/routes.rb file. This method supports the following options:
  • except – pass a symbol or an array of symbols to exclude some of the routes. Possible options:
    • docs – controller with guides
    • tests – controller with methods to test the API
    • client_apps – controller to manage client API applications
  • controllers – pass a hash with a controller’s name and a new path. Possible options:
    • oauth_docs – same as docs
    • oauth_tests – same as tests
    • oauth_client_apps – same as client_apps
    • oauth_new – controller to request permissions from the user. Note that you can only redefine thenew action. create action will still be called from the default controller.
Full implementation of this method can be found here.
So first of all, let’s exclude the documentation routes. To do that, simply write:
config/routes.rb
[...]
mount_opro_oauth except: :docs
[...]
What I want to do is to tweak the page that users see when authenticating via our application. To do that, of course, we need a custom controller.
config/routes.rb
[...]
mount_opro_oauth controllers: {oauth_new: 'oauth/auth'}, except: :docs
[...]
We are namespacing AuthController under Oauth, so inside the controllers directory create anoauth folder with an auth_controller.rb file inside:
controllers/oauth/auth_controller.rb
class Oauth::AuthController < Opro::Oauth::AuthController
end
This custom controller inherits from Opro::Oauth::AuthController defined by oPRO. I am not monkey-patching any actions here, because we only need to change a route. Still, it is totally possible to override the new action as needed.
Create a view under views/oauth/new.html.erb and tweak it in any way you like. For example, you can add some explanations to the access rights that the app is requesting:
views/oauth/new.html.erb
[...]

<h2>Explanation</h2>

<ul>
  <li><strong>Read</strong> - the app will be able to read information about your account.
  This permission is required.</li>
  <li><strong>Write</strong> - the app will be able to modify your account.</li>
</ul>
Maybe include some branding and contact information here. You can use the same approach to customize other views as well.
Authenticate All the Things with oPRO, the Basics Authenticate All the Things with oPRO, the Basics Reviewed by JohnBlogger on 2:22 PM Rating: 5

No comments: