Adding OAuth 2.0 To Your Application

Step 1: Setup Your Database

The authorization server needs to keep track of clients, authorization
requests, access grants and access tokens. That could only mean one thing:
a database.

The current release uses MongoDB.
You're going to need a running server and open connection in the form
of a Mongo::DB object. Because MongoDB is schema-less, there's no
need to run migrations.

If MongoDB is not your flavor, you can easily change the models to support
a different database engine. All the persistence logic is located in
lib/rack/oauth2/models and kept simple by design. And if you did
the work to support a different database engine, send us a pull request.

Step 2: Use The Server

For Rails 2.3/3.0, Rack::OAuth2::Server automatically adds itself as
middleware when required, but you do need to configure it from within
config/environment.rb (or one of the specific environment files).
For example:

:realm – Authorization realm that will show up in 401 responses.
Defaults to use the request host name.

:logger – The logger to use. Under Rails, defaults to use the
Rails logger. Will use Rack::Logger if available.

If you only intend to use the UI authorization flow, you don't need to
worry about the authenticator. If you want to allow clients applications to
create access tokens by passing the end-user's username/password, then
you need an authenticator. This feature is necessary for some client
applications, and quite handy during development/testing.

The authenticator is a block that receives either two or four parameters.
The first two are username and password. The other two are the client
identifier and scope. It authenticated, it returns an identity, otherwise
it can return nil or false. For example:

Step 3: Let Users Authorize

Authorization requests go to /oauth/authorize.
Rack::OAuth2::Server intercepts these requests and validates the client ID,
secret, redirect URI, authorization type and scope. If the request fails
validation, the user is redirected back to the client application with a
suitable error code.

If the request passes validation, Rack::OAuth2::Server sets the request
header oauth.authorization to the authorization handle, and passes
control to your application. Your application will ask the user to grant or
deny the authorization request.

Once granted, your application signals the grant by setting the response
header oauth.authorization to the authorization handle it got
before, and setting the response header oauth.identity to the
authorized identity. This is typicaly the user ID or account ID, but can be
anything you want, as long as it's a string. Rack::OAuth2::Server
intercepts this response and redirects the user back to the client
application with an authorization code or access token.

To signal that the user denied the authorization requests your application
sets the response header oauth.authorization as before, and returns the
status code 403 (Forbidden). Rack::OAuth2::Server will then redirect the
user back to the client application with a suitable error code.

Rails actions must render something. The oauth method returns a helper
object (Rack::OAuth2::Server::Helper) that cannot render anything, but
can set the right response headers and return a status code, which we then
pass on to the head method.

Step 4: Protect Your Path

Rack::OAuth2::Server intercepts all incoming requests and looks for an
Authorization header that uses OAuth authentication scheme, like so:

Authorization: OAuth e57807eb99f8c29f60a27a75a80fec6e

It can also support the oauth_token query parameter or form field,
if you set param_authentication to true. This option is off by
default to prevent conflict with OAuth 1.0 callback.

If Rack::OAuth2::Server finder a valid access token in the request, it sets
the request header oauth.identity to the value you supplied during
authorization (step 3). You can use oauth.identity to resolve the
access token back to user, account or whatever you put there.

If the access token is invalid or revoked, it returns 401 (Unauthorized) to
the client. However, if there's no access token, the request goes
through. You might want to protect some URLs but not others, or allow
authenticated and unauthenticated access, the former returning more data or
having higher rate limit, etc.

It is up to you to reject requests that must be authenticated but are not.
You can always just return status code 401, but it's better to include
a proper WWW-Authenticate header, which you can do by setting the
response header oauth.no_access to true, or using
oauth_required to setup a filter.

You may also want to reject requests that don't have the proper scope.
You can return status code 403, but again it's better to include a
proper WWW-Authenticate header with the required scope. You can do
that by setting the response header oauth.no_scope to the scope
name, or using oauth_required with the scope option.

In Rails, it would look something like this:

classMyController<ApplicationControllerbefore_filter:set_current_useroauth_required:only=>:privateoauth_required:only=>:calc,:scope=>"math"# Authenticated/un-authenticated get different responses.
defpublicifoauth.authenticated?render:action=>"more-details"elserender:action=>"less-details"endend# Must authenticate to retrieve this.
defprivaterenderend# Must authenticate with scope math to do this.
defcalcrender:text=>"2+2=4"endprotecteddefset_current_user@current_user=User.find(oauth.identity)ifoauth.authenticated?endend

In Sinatra/Padrino, it would look something like this:

beforedo@current_user=User.find(oauth.identity)ifoauth.authenticated?endoauth_required"/private"oauth_required"/calc",:scope=>"math"# Authenticated/un-authenticated get different responses.
get"/public"doifoauth.authenticated?render"more-details"elserender"less-details"endend# Must authenticate to retrieve this.
get"/private"dorender"secrets"end# Must authenticate with scope math to do this.
get"/calc"dorender"2 + 2 = 4"end

Step 5: Register Some Clients

Before a client application can request access, there must be a client
record in the database. Registration provides the client application with a
client ID and secret. The client uses these to authenticate itself.

The client provides its display name, site URL and image URL. These should
be shown to the end-user to let them know which client application
they're granting access to.

Clients can also register a redirect URL. This is optional but highly
recommended for better security, preventing other applications from
hijackin the client's ID/secret.

You can register clients using the command line tool
oauth2-server:

$ oauth2-server register --db my_db

Or you can register clients using the Web-based OAuth console, see below.

You may want your application to register its own client application,
always with the same client ID and secret, which are also stored in a
configuration file. For example, your db/seed.rb may contain:

When you call register with id and secret
parameters it either registers a new client with these specific ID and
sceret, or if a client already exists, updates its other properties.

Step 6: Pimp Your API

I'll let you figure that one for yourself.

OAuth Web Admin

We haz it, and it's pretty rad:

To get the Web admin running, you'll need to do the following. First,
you'll need to register a new client application that can access the
OAuth Web admin, with the scope oauth-scope and redirect_uri that
points to where you plan the Web admin to live. This URL must end with
“/admin”, for example, “example.com/oauth/admin”.

The easiest way to do this is to run the oauth2-sever command line
tool:

$ oauth2-server setup --db my_db

Next, in your application, make sure to ONLY AUTHORIZE ADMINISTRATORS to
access the Web admin, by granting them access to the oauth-admin
scope. For example:

defgrant# Only admins allowed to authorize the scope oauth-admin
ifoauth.scope.include?("oauth-admin")&&!current_user.admin?headoauth.deny!elseheadoauth.grant!(current_user.id)endend

Make sure you do that, or you'll allow anyone access to the OAuth Web
admin.

Next, mount the OAuth Web admin as part of your application, and feed it
the client ID/secret. For example, for Rails 2.3.x add this to
config/environment.rb:

The Rack::OAuth2::Server::Client model represents the credentials of a
client application. There are two pairs: the client identifier and secret,
which the client uses to identify itself to the authorization server, and
the display name and URL, which the client uses to identify itself to the
end user.

The client application is not tied to a single Client record. Specifically,
if the client credentials are compromised, you'll want to revoke it and
create a new Client with new pair of identifier/secret. You can leave the
revoked instance around.

Calling revoke! on the client revokes access using these
credential pair, and also revokes any outstanding authorization requests,
access grants and access tokens created using these credentials.

You may also want to register a redirect URI. If registered, the client is
only able to request authorization that redirect back to that redirect URI.

Authorization Request

The authorization process may involve multiple requests, and the
application must maintain the authorization request details from beginning
to end.

To keep the application simple, all the necessary information for a single
authorization request is stored in the Rack::OAuth2::Server::AuthRequest
model. The application only needs to keep track of the authorization
request identifier.

Granting an authorization request (by calling grant!) creates an
access grant or access token, depending on the requested response type, and
associates it with the identity.

Access Grant

An access grant (Rack::OAuth2::Server::AccessGrant) is a nonce use to
generate access token. This model keeps track of the nonce (the
“authorization code”) and all the data it needs to create an access token.

Access Token

An access token allows the client to access the resource with the given
scope on behalf of a given identity. It keeps track of the account
identifier (supplied by the application), client identifier and scope (both
supplied by the client).

An Rack::OAuth2::Server::AccessToken is created by copying values from an
AuthRequest or AccessGrant, and remains in effect until
revoked. (OAuth 2.0 access tokens can also expire, but we don't support
expiration at the moment)

Credits

Rack::OAuth2::Server was written to provide authorization/authentication
for the new Flowtown API.
Thanks to Flowtown for making it happen
and allowing it to be open sourced.