I did a bit of Googling and came across a few posts which dealt with some of these challenges, and by putting them all together I arrived at a solution which I’ll detail here in case it’s of use to anyone else.
I’ve put together a demo app which hopefully illustrates everything. Have a play with it (installation instructions are in the README), then read on
The app consists of a few components:
- A jQuery global AJAX error handler
- An authentication failure handler
- An authentication success handler
- A kernel exception listener
As far as the rest of the app goes, I’m using the FOSUserBundle with a standard configuration, which supplies the rest of the auth functionality, including the HTML login page.
Let’s start with the server components – three classes and a bit of config:
XHRCoreExceptionListener class code is triggered whenever a kernel exception occurs – check the service definition for this listener, you’ll see we tell Symfony to call its
onCoreException method whenever an exception event is fired. Again, we only want to act if the request that caused the exception was an AJAX one. Assuming it is, we try to work out the status code to return from the exception code – if it’s a valid HTTP code, we use that, otherwise we assume a server error (500). As before, we’re reusing the exception message to provide context – but in this case, where the exception could relate to anything in the system (like a database query) we’d really want to be cautious about exposing it to the user, so this code certainly isn’t suitable for a production environment.
That’s it for the server, now for the client. All the functionality is in
First we define a global error handler, which will be fired whenever an uncaught AJAX error occurs:
If the problem is that the user does not have a valid authentication token, we invite them to log in.
The login process is interesting – the modal login form is essentially a duplicate of the HTML one, but lacks the CSRF token which is automatically injected into the HTML form by Symfony. This helps prevent spoofing so I didn’t want to remove it, so the approach I took was to request the HTML login page, capture the value of the CSRF field in the response, then use that in a second request to actually authenticate the user. This meant I could reuse the same authentication code on the server.
The demo app also includes two other demonstrations – making a valid request (which will fail, and force a login, if the user does not have a valid session) and an invalid one (which displays the error message returned by the server). There’s not much to say about those, the code should be self explanatory.
That’s it really. The code is a little rough and ready, but hopefully it’ll give you enough to go on if you’re trying to do something similar!