Belighted

Platypus - Belighted developers' playground

Platypus

Belighted developers' playground

Belighted Dev’s Insider #8

Last news in live from Belighted dev chat room !

See you soon !

Comments

Why Ruby on Rails ?

“Why Ruby on Rails?” was a conference organized september 16 by the Feweb in the coworking space of Louvain-la-Neuve. The keynote was split in two parts, the first one about the Ruby programming language and the Rails framework and the second part was a live coding session. Here are the slides and annotations of the talk given by Nicolas Jacobeus and Stephane Amant, respectively founder and developer at Belighted.

Annotations are available here.

Comments

Building a Centralized Authentication System

Some time ago one of our customers, for which we had already developed a highly specialized e-commerce/ERP, decided to add a new service to the one they were already offering. So they naturally asked us to extend the existing application with a whole new section covering their new business. However, the legacy code was build on top of Rails 3.0 and for various reasons1 it was if not impossible, at least highly impractical for us to upgrade to the most recent version (3.2 at the time). As we didn’t want to write this new part (which we knew would become larger and more complex than the original in the long run) on an already outdated framework, we decided to write a new, separated, Rails application.

The single sign on problem

If this solution allowed us to use the latest version of the gems and to write everything from scratch (a luxury developers can not always afford), it generated another problem: from the end users’ point of view, these two applications had to be seen as two parts of a single, integrated, one. That meant that when they were logged in on one application, they should have been automatically considered logged in on the other as well.

The Devise gem provides an easy way to authenticate users through third party OAuth2 service providers like Google or Facebook, but in our case using an external service was out of the question: the users are employees, and they have to go through a request/approval procedure before gaining access to any application. Furthermore, they already had their credentials on the legacy application. So we wrote a third application: our own OAuth2 service provider2. We used Devise to manage the server-side authentication and Doorkeeper to provide the authorization mechanism: in addition to the user credentials used for client-side authentication, the application also controls the access to resources such as user profile information and preferences. It is also this application that manages the whole user registration and approval process.

The other side of the equation: single sign out

Single sign on is fairly straightforward to implement: even if you have to write your own OAuth2 provider, there are a lot of gems, tutorials and examples to just help you do that (actually, Devise and Doorkeeper documentation should cover 99% of your needs). Troubles arise when you try to consider the reverse operation: single sign out. To understand the problem and the solution we eventually used, you first need to understand how the sign on with Devise and OAuth2 works.

Let’s say we have two client applications A1 and A2, and the authentication server AS. When a user tries to access A1 for the first time, Devise checks the session cookie associated with the domain name of A1 to see if the user is already locally logged in. If not, it contacts AS and asks for some user credentials. On AS, Devise checks the session cookie associated with AS domain and if the user is not already logged in, it asks for his e-mail and password. Once the user is properly authenticated, AS calls back A1 on a specific callback URL and sends the signed user’s credentials (mainly its UUID). With this information, A1 can retrieve the user from its own database (or create a new one if he does not exist yet) and log him in locally (i.e. set up a proper session cookie). Now when the user goes to A2, the same process takes place, but as he is already logged in on AS, AS calls back A2 directly without presenting the login screen to the user, and A2 logs him in locally. From the user’s point of view, it’s as if he was already logged in on A2.

The problem is when the user wants to log out. Since he is actually logged in independently on the three applications, if he logs out from AS (i.e. technically his session cookie associated with AS domain is destroyed or invalidated), he is still locally logged in on A1 and A2.

Our solution

The first solution we tried was to implement a logout callback on A1 and A2, that would be called by AS when the user signs out. Unfortunately this didn’t work: the session is specific to each HTTP client (you can be logged in on your Gmail account in Chrome but logged out in Firefox), and since AS had to instanciate its own HTTP client to call back A1 or A2, the session information was lost.

Eventually, the trick was to exploit a specificity of our production environment: the domain of A1, A2 and AS were all of the form xyz.ourcustomer.com. Browsers send back cookies only to servers within the emitting domain, which means that if the browser has saved the three session cookies (from a1.ourcustomer.com, a2.ourcustomer.com and as.ourcustomer.com), it will only send the a1.ourcustomer.com cookie with its requests to A1, because the other two don’t belong to the same domain. However, you can configure your Rails application to mark your session cookies as belonging to the top level domain instead of the fully specified one with the ‘domain’ parameter:

1
MyApp::Application.config.session_store :cookie_store, :key => "_my_app_key", :domain => :all

Once our three applications were configured this way, their respective session cookies were all emitted as belonging to *.ourcustomer.com and each of them received the three session cookies with each request3. It then sufficed to destroy all three session cookies (since we now had access to them) when logging out from any of the application, effectively logging out from the others as well:

1
2
3
4
5
6
class Users::SessionsController < Devise::SessionsController
  def destroy
  super
  cookies.clear(:domain => :all)
  end
end

This solution is probably not the most elegant, but it is now deployed in production for more than one year and without any problem so far. Anyway, if you think of a more orthodox way of doing it, feel free to post it in the comments: we always look forward to improve our applications.

References

Notes

  • [1] Mainly, some parts of the code heavily relied on gems whose newer versions were totally incompatible with the older ones and an upgrade would have forced us to rewrite these parts entirely, something which wasn’t on the planning.
  • [2] To be accurate, OAuth2 is not an authentication mechanism. It is an authorization mechanism to control access to resources. It can however be used for authentication, typically by requesting access to the user’s credentials.
  • [3] However, since each cookie is encrypted with a different secret token, the applications are only able to read the content of their own.
Comments

Belighted Dev’s Insider #7

Here is our last Belighted dev chat room summary proudly provided on Platypus !

See you soon !

Comments

Belighted Dev’s Insider #6 - Front-end Edition

This week, it’s a special edition of the Belighted dev chat room summary with a focus on front-end development!

CSS, Dev, HTML, JS, Web
Comments

Belighted Dev’s Insider #5

Last news in live from Belighted dev chat room !

See you next week !

Comments

WTF: Beloved JS - Javascript … What Else ?

Here are the latest trending Javascript libraries we had a look at in the last few weeks ! We just can’t wait to use them in one of our future projects !

js
Comments

Belighted Dev’s Insider #4

Here is our last Belighted dev chat room summary proudly provided on Platypus !

And finally, here is what happens on the internet while you were reading our blog !

https://s3-eu-west-1.amazonaws.com/uploads-eu.hipchat.com/34861/246507/ZTEe4cO0qcJMoHz/Internet-minute.jpg

See you soon !

Comments

WTF: MapReduce and Columnar DB’s

At Belighted, we stay in touch with the latest technologies! Even more if they relate to huge amount of data, such as companies like Facebook, Google and Amazon are used to handle.

Here is a small introduction of key points of interest if you need to enter this new world of huge, distributed, and replicated databases.

This presentation introduces two fundamental topics: MapReduce and Columnar DB’s.

Comments

Gulp & Grunt: How to Automate the Work of Front-end Developer?

http://i.imgur.com/frWoyic.jpg

This article will be split in two distinct parts: the first will be more “theoretical” and will explain the main principles of Gulp and Grunt, as well as what I prefer to use; in the second, we will show you how to build a boilerplate for your project (in both way).

As front-end developers, we are often faced in our daily work with recurring and time consuming tasks. For the sake of efficiency and comfort of work, we have listed a few things that could improve the workflow of our team:

  • Fast and automated creation of a common working environment.
  • Automation of tasks such as image compression, JS files concatenation or SASS files compilation.

To meet these needs, two tools are in competition: Grunt and Gulp.

Grunt and Gulp are two task runners that automate most tasks (compilation, test creation,…), saving time and helping us to focus on the essential: development.

Ultimately, Grunt and Gulp do exactly the same job. The communities around the two systems are quite large and modules can be found fairly easily for each of the two systems. What differentiates the two is their mode of operation.

Grunt

Grunt works on the principle of “per plugins” configuration. This means that each plugin must be independently configured and used in tasks. Here’s how a plugin is configured, with options, a source and sometimes a destination file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
sass: {
  dist: {
    options: {
      style: 'expanded'
    },
    files: {
      'dist/assets/css/main.css': 'src/styles/main.scss',
    }
  }
},

autoprefixer: {
  dist: {
    options: {
      browsers: [
        'last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'
      ]
    },
    src: 'dist/assets/css/main.css',
    dest: 'dist/assets/css/main.css'
  }
},

grunt.registerTask('styles', ['sass', 'autoprefixer']);

This methodology allows to configure plugins, then inject them into tasks.

  • The advantage is that each plug-in is independent of the others.

  • The downside is manipulating files between tasks. If you plan a series of tasks via plug-ins, this means that the files will be handled as many times as the number of defined tasks.

Gulp

Gulp, meanwhile, works on the principle of “per tasks” configuration. The big difference is that here we will configure and inject all the plugins we need within the task configuration:

1
2
3
4
5
6
gulp.task('sass', function() {
  return gulp.src('src/styles/main.scss')
    .pipe(sass({ style: 'compressed' }))
    .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
    .pipe(gulp.dest('dist/assets/css'))
});

The situation here is quite different: the different desired plugins are injected into the “pipe”.

  • The advantage of Gulp is that the file does not pass from one task to another, but is transformed once. This prevents handling the final files multiple times. It also use the powerful NodeJS Stream (see at the end of this article for Stream)

  • The disadvantage is that the same configuration for a task can be repeated multiple times. (If we take care of the HTML files in a different way, it is possible to have re-write the same code in multiple places)

Conclusion

Both technologies meet our needs, with the same result. The big difference is their functional approach. You may prefer to configure the entire plugins and add tasks or conversely, create jobs and inject plugins.

Next week we will dive deeper in these two technologies, with more specific examples. So stay tuned!

Here are some good articles about streams:

Thanks to @zedissime, Dominique and Gregory for the review

Comments