Introduction
Eric Woods and Matthew Marum are implementing support for OAuth 2.0 in Apache Shindig. This article provides an overview of the implementation including high level design, supported flows, common How-Tos, and future considerations.
The OAuth 2.0 specification is here: http://tools.ietf.org/html/draft-ietf-oauth-v2-20
Note that the implementation is still evolving, so consider this design document to be a draft.
High Level Design
All OAuth 2.0 requests are received by the OAuth2Servlet. Per the spec, there are two OAuth 2.0 related endpoints: /oauth/authorize and /oauth/token. The request is quickly delegated to either the OAuth2AuthorizationHandler or the OAuth2Token handler accordingly.
The first thing either handler does is normalize the incoming OAuth 2.0 request using the OAuth2NormalizedRequest class. This class is designed to receive the incoming HttpServletRequest and normalize all OAuth 2.0-related fields. For example, a pre-registered client may authenticate using Basic Authentication or by including "client_secret" as a URL parameter. These permutations make it difficult to process an incoming request consistently. OAuth2NormalizedRequest will make the client secret accessible by getClientSecret() regardless of authentication method, thus "normalizing" the method that was used to authenticate, pass a token, pass a grant type, etc.
After normalization, the request is processed. We have two distinct layers to process OAuth requests: OAuth2Service and OAuth2DataService. The OAuth2Service handles all OAuth processing and enforcement as defined by the OAuth 2.0 specification. For example, the OAuth2Service handles client authentication and request validation for an authorization code, access token, refresh token, or resource. The OAuth2Service also handles generation of authorization codes, access tokens, and refresh tokens.
The second layer, OAuth2DataService, represents the data store to manage authorization codes, access tokens, refresh tokens, and association with a pre-registered client. For example, the OAuth2DataService manages registering/unregistering the various OAuth 2.0 codes/tokens, retrieving a client by ID, and retrieving a code/token by value.
We provide "poor-man" implementations of the two service interfaces; the OAuth2ServiceImpl uses in-memory data structures, and the OAuth2DataServiceImpl uses canonical.json.
OAuth 2.0 Flow Support
- Authorization Code Flow (done)
- Implicit Grant Flow (in progress)
- Client Credential Flow (in progress)
How-Tos
How to Register a Client
Clients are pre-registered in canonicaldb.json. Client registration, authorization codes, access tokens, and refresh tokens are keyed by client. Here's the current structure (subject to change):
// Registry of OAuth 2.0 clients with Shindig's service provider. "oauth2" : { "advancedOpenSocialClient" : { "registration" : { "id" : "advancedOpenSocialClient", "secret": "advancedOpenSocialClient_secret", "title": "Most Advanced OpenSocial Client Ever!", "redirectUri" : "http://localhost:8080/oauthclients/OpenSocialClient", "type" : "confidential" }, "authorizationCodes" : { "advancedClient_authcode_1" : { // Authentication code has been consumed since associatedSignature exists "redirectUri" : "http://localhost:8080/oauthclients/OpenSocialClient", "associatedSignature" : "advancedClientOS_accesstoken_1" }, "advancedClient_authcode_2" : { "redirectUri" : "http://localhost:8080/oauthclients/OpenSocialClient" } }, "accessTokens" : { "advancedClient_accesstoken_1" : { "redirectUri" : "http://localhost:8080/oauthclients/OpenSocialClient" } } }, "testClient" : { "registration" : { "id" : "testClient", "redirectUri" : "http://localhost:8080/oauthclients/OpenSocialClient", "type" : "public" } } }
How to Add a Custom Grant Handler
Per the OAuth 2.0 specification, grant types are extensible beyond the 4 pre-defined types (authorization_code, client_credentials, refresh_token, and password). To accommodate, validation of new grant types, grant handlers are registered with the OAuth2Service. The appropriate grant handler is identified by its registered "grant_type" and invoked within OAuth2Service.validateRequestForAccessToken(...) to validate requests.
TODO: where and how are grant handlers registered?
How to Enable/Disable OAuth 2.0 Security
You can add OAuth2 support to protect your Social APIs by using an AuthenticationHandlerProvider that provides the OAuth2Authenticationhandler.
You can disable the OAuth2 access token and authorization endpoints by removing mappings for the OAuth2Servlet in the Shindig web.xml
If your AuthenticationHandlerProvider doesn't return the OAuth2AuthenticationHandler, your resources won't be protected by OAuth2.
Future Considerations
Currently, the entire OAuth 2.0 implementation is located in a single package within Shindig's social-api module. We need to determine the most appropriate way to integrate this package into shindig. We could integrate this package into social-api following the existing patterns, or it may even be broken out as an entirely new module within Shindig. To be determined...
Additionally, client registration is currently facilitated by modifying canonicaldb.json. We could expose client registration services via a REST API and provide a UI for clients to register themselves.