Next time you point your browser to a /login url wait a minute before submitting your credentials. There is a complex system you’re going to use when you submit that form and it must be honored in some way.
You’re a software craftman and you want to get the job done. Users have to register to your app and they must have the chance to login.
Attackers will hit this code first and they will try to bypass it or rather to exploit it. It’s a very critical subsystem that you have to design with care.
Don’t reinvent the wheel unless you’re forced to
Unless you do want to cook an authentication subsystem yourself, and you have some hard-to-fight constraints that force this decision, I suggest to you using a third-party opensource library implementing OAuth2 over well known identity providers like Google, Facebook, Github, OpenID.
To my taste, omniauth is one of best and easy to use ruby library implementing OAuth authentication.
Let’s see how easy is to let your users to authenticate to your applications using their github.com account.
Omniauth and Github.com integration
The followings are real code snippets from the codesake.com source code. Since I need to bind my users with their github account, using omniauth for github was the number 1 choice.
Register your application to github
Before going further, you must register your web application to github. You will get a client identifier and a secret that you must not share.
While in development it’s not a good idea to register your web application using real urls since the code will be likely running on localhost.
When I registered codesake.com, I gave localhost:3000 as base URL and /auth/github/callback as callback URL.
The callback URL is the one used by the identity provider (github in this example) to redirect user after he authorizes codesake.com to use his github.com account informations.
Specifiying the ID provider in the callback url can be a clever choice if in a future you want to add other ID providers.
Tell bundler about your choice
Now, it’s time for you to te tell bundler you will use omniauth for github gem.
1 2 3 | |
Run bundler update now and you’re ready to tell your application the secrets it will share with github.com website.
Place this code into your main Padrino::Application, let’s say you have the default app/app.rb in your code:
1 2 3 | |
Now, you can start padrino and OAuth workflow is working out-of-the-box right now. Just point your browser to the following url: /auth/github
You will be redirect to a github.com page asking you to authorized your website to access your data. If you grant it, the call back url will be called and… an exception will be thrown unless you already created the auth controlled as described… now!
Create the auth controller
Create a controller to handle authentication:
1
| |
Add some code in case of callback (authentication succeeded) and authentication failed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
In the environement it’s placed the omniauth.auth hash object containing all github users informationss. Now it’s time to make your users informations to be persistent.
Create a user model
I use datamapper as ORM but the only differnt thing here is that the migrate command changes accordingly with the ORM you like most.
1 2 | |
To my personal taste, I always default the created_at and update_at model properties to be equal to Time.now.
1 2 | |
I rather enforce the presence of a valid email, also if this should be there in the user’s github account informations.
I use factory girl rubygem to mock model instances so I added some rubygems in my Gemfile for the testing environment.
1 2 3 4 5 6 | |
FactoryGirl must be initialized in the spec_helper.rb file in order to have rspec to look for factories.
1 2 3 4 5 6 7 8 9 10 11 | |
And now… let’s write a trivial test for a user with empty email field that it must be not valid.
1 2 3 4 5 | |
Run our test and we should see it to fail:
1
| |
Add the model validation constraint and now the rspec test passes. Great.
1 2 3 | |
Now you may want to add to your user model some helper to search users from github uid field and to create a new user if not present in your db.
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Now your callback handler, when called it search for the user in the database and if not present the user is created.
Off by one
With the post I gave you some hints about using github as identity provider for your web application.
From the security point of view you:
- don’t have to care about password complexity rules, password aging, password reset issues. That means less code for you to write and less chances to introduce security issues.
- don’t have to be compliant to laws about sensitive data storage (unless your application doesn’t handle PCI, SOX or something like that data)
- rely on github.com security that means that a break-in can also affect your web application. Fortunately you can reset your webapplication secrets so people will have to grant your application access again.
And you? Which is your opinion about using a third party identity provider to manager authentication in your application?