Recently, Marcos and I rewrote our log in and sign up code from scratch using, at Brad‘s suggestion, OmniAuth. The previous system hadn’t really been designed for the way we were using it and it was time to stop shoe-horning new functionality into it and make something with extensibilty and maintainability in mind.
I won’t reproduce OmniAuth’s
README here or anything, but basically it is a system to allow users to log into your application by authenticating through a third party and then presenting the results of that authentication in a uniform way. The nice thing is that it is pretty much built to handle a choice of third parties. This is perfect for us, since all our users are, by definition, users of some email service provider. Currently, we support Gmail, AOL and Yahoo for most of our applications and being able to write code that doesn’t have to know where the user authenticated is super helpful.
One thing we noticed, though, is that in the case of an exception, we didn’t get a whole lot of information. You see, most OmniAuth strategies (“strategy” is the term for a class defining how a user will authenticate with a given 3rd party), if they encounter a problem, call the method
fail! and pass in a symbol describing the problem like
:invalid_credentials and the exception they encountered. The
fail! method ends up calling
OmniAuth.config.on_failure and passing in the Rack environment (after doing a few other things like sticking the exception into the environment, as we’ll see below).
As you might be able to guess,
on_failure is an accessor to a configurable option and it’s intended to hold something that responds to
call. The default is this proc:
You can see that this is acting as a Rack endpoint, here, returning an array with a code and all that. It ends up redirecting the user to a failure URL which you’re intended to do something with. However, passing the message key (the symbol I mentioned above) doesn’t always tell you a lot.
We were seeing some problems logging in with a test Gmail account, but all we were seeing was
:invalid_credentials and we were fairly certain we’d typed the password in correctly, since Gmail let us through to the allow/deny page that is the second step of the OAuth process. So in the top of our OmniAuth initializer, we put this:
Let’s have a look. The first three lines is us pulling the stuff we want to know out of the environment. The next thing we do is log the information and then pass it to
ErrorNotifier. That module is a wrapper around Airbrake.
This way, if a user hits an exception trying to log in or sign up, we’ll see the notification and also be able to see what key the strategy sent along, which strategy did the sending and also the stack-trace involved. Only after that, do we send them along to the failure URL where we can show them an apology page or whatever we deem appropriate.
When writing this code, I would’ve loved to have had it do the logging and notifying and then just call the default proc, but I couldn’t make it work quickly and decided on the pragmatic thing of copying and pasting the relevant code. As of this writing, there’s some work being done on OmniAuth to make this kind of thing a bit easier, by separating
into a default
that can be customized, inherited from or called to get the same results we did with our simple