Now that we have dealt with exceptions in our API we need to provide various methods of authentication. We’re going to be talking grape and devise.
There’re several scenarios and issues to consider.
- We want some public APIs not to require authentication or registration at all. In our case this is only ping, the idea being that an absolute minimum set of code runs underneath.
- We want public APIs that require the caller to register an application. This gives us some ability to do accounting as well as to block a misbehaving application in the wild.
- We want users to be able to login via OAuth2 and we’d like to distinguish users between administrators and other types of users.
- When a user is logged into the website with a form, we’d like to allow browsing and exercising the API without having to do OAuth.
Here’s what we did. It’s far from ideal, please comment and suggest ways to move forward, especially if you think something belongs in Grape or Devise proper.
No Authentication
No authentication is easy. We don’t do anything.
A User Logged in with a Form
A previously logged in user is authenticated with Devise (based on Warden). There’s nothing special to do for the API except to insert an authenticated_user method in those APIs that require it.
The authenticated_user method uses warden.
Client Applications
We have a straightforward way to register ClientApplications which yield an application ID and secret key. Both are generated as random hashes. Somewhere in the registration process an administrator approves an app and gives the user who owns the app access to these values.
Access Grants
Before we hookup user authentication, note that we have multiple authentication schemes that yield some kind of access. We can all it an AccessGrant. We want the grant to expire. We’ll store the grant in the back-end so that we can check the grant after it has been handed out. A future version may improve on this by signing and serializing the grant to the client, therefore avoiding the database hit.
XApp
Our simpler scenario includes registered applications making calls without users being logged in. @sarcilav labeled this XApp.
To get an XApp token one would call the xapp_token API method with the client ID and secret. The authenticated method will now look for an xapp_token parameter.
This is an improvement over schemes like BASIC authentication. We’re only sending actual credentials (client ID and secret) once during authentication, which would happen under SSL. If subsequent unprotected traffic were to be logged and the logs stolen, the XApp token would have a limited value since it expires.
We found that the XApp authentication model is ideal for client apps that don’t require user registration.
OAuth2
OAuth2 is the preferred mechanism for authenticating users. It takes the login out of the hands of the client application – login happens in an external browser, therefore offering the best protection to the user.
Let’s start from the the tail-end. Assume the client application has gotten some kind of _access_token _and can pass it to each API. This should allow us to lookup an access grant and eventually a user. Let’s add this to the User model.
How does a user obtain such a token with OAuth2? We’ll need two routes.
OAuth2 starts by redirecting users to /oauth2/authorize?client_id=[client id]&redirect_uri=[redirect url]&response_type=code. This should render a login page. If the user chooses to login, he will be redirected back to the redirect url specified with the authorization code. The application should make a request to /oauth2/access_token?client_id=[client id]&client_secret=[client secret]&redirect_uri=[redirect url]&grant_type=[authorization code]&code=code which will return the JSON { "access_token": [access token], "refresh_token": [refresh token], "expires_in": "yyyy-mm-ddThh:mm:ss TZ" }
.
Finally, we can rewrite our API authenticate method appropriately and implement current_user. The latter can also tell us whether a user is an administrator or not.
This is a little thick. Maybe someone can take a stab at sinking some of this into Grape as authentication middleware?