04 Dec 2014
Note: There have been some changes in the JWT Gem that make some of the below not work exactly right (it’ll still be about 90% the same). Specifically, they added expiration support.
I’m a big proponent of rolling your own authentication solution, especially if you’re only doing simple username/password based logins (as opposed to logging in via an OAuth provider). I’ve tried to use Devise on a number of Rails apps, but I always end up ripping it out. It’s not because Devise is a bad gem, but because it always takes me more time to customize it to my liking than it does to just write everything myself. And the flexibilitity of a custom solution almost always comes in handy down the road. I have generally implemented it the same way that Ryan Bates does in this Railscasts episode.
- The same authentication API can be used by all types of clients (web app, mobile app, etc).
- It is stateless, so the web server does not have to keep track of session information, which is good for scaling.
- Protected against CSRF (cross-site request forgery) attacks
- All of your views are rendered by the client, rather than a mix of server and client rendered views.
A relatively new standard for accomplishing this is JSON Web Tokens (abbreviated to JWT). I won’t dig into the details because there are plenty of good resources, but JWT is a way of digitally signing data to be transferred between two parties. The data is represented as an encoded JSON object. In a nutshell, these tokens are passed to the client upon successful authentication and then subsequently used in every HTTP request in order to verify the identity of the client.
22 Sep 2014
While working on an angular.js application recently, I found myself needing some form of authorization logic (not to be confused with authentication / login). I needed to restrict content in my app based on a user’s role as well as some other factors. At first, I created a single
AuthService service that dealt with login, authorization, and session management. But this felt messy and violated the Single Responsibility Principle, so I decided to make something cleaner. My goal was for the API to look something like this:
(Warning: lots of coffeescript ahead!)
LoginService.login(email, password).then((u) ->
# ... Elsewhere ....
user = Session.getCurrentUser()
authorizer = new Authorizer(user)
authorizer.canAccess(APP_PERMISSIONS.viewAdminSettings) # returns a boolean
By doing it this way, I was fairly sure I could split my formerly monolithic
AuthService into 3 separate services that had no dependencies on one another. I won’t go too detailed into the login and session services because they are fairly straight forward.
LoginService has one method that simply makes an HTTP request with a username and password and, if successful, returns the user object.
Session is a singleton service that, given a user, can create or destroy the current session. But my solution to Authorization was fairly interesting, so I thought I’d share.
20 Jun 2014
31 Jan 2014
In a recent post, I talked about how to generate PDF reports in Rails using Prawn. This approach is great for generating PDF’s with lots of data tables and other variable-length content. But an alternative situation is when you already have a template authored in an application such as Adobe Acrobat and you want to populate it with data from your database. This makes it more difficult to insert variable-length content, but on the plus side, you no longer need to worry about the layout of the document.
22 Jan 2014
“Onboarding” is one of those things we sometimes forget about when developing an application, but it really deserves more attention. Showing the user how to use your app can be critical in retaining them. Some people might say that if you need onboarding, your app just needs to have a better UX, but I don’t think this is practical in all situations, particularly complex business applications.