===== OAuth 2.0 Implementation for APIs =====
We should be able to provide a way for other applications to use our APIs by authenticating and authorizing through Oauth.
This should cover the following -
* Server flow as described in Oauth 2.0 (We follow v22 spec of Oauth - http://tools.ietf.org/html/draft-ietf-oauth-v2-22)
* Endpoint for clients to request authorization code.
* Endpoint to request an access token using the authorization code.
* Admin screens for site admins to manage Oauth2 clients.
===== OAuth 2.0 Spec & discussion =====
There are 4 different types of token grant are available. We use two of them which are famous and applicable to our case.
==== 1. Grant type - password ====
+--------+ +---------------+
| |--(A)- Authorization Request -> | Authorization|
| | (with username, password) | Server |
| |<-(D)----- Access Token ------- | |
| | +---------------+
| Client |
| | +---------------+
| |--(E)----- API request ------> | Resource |
| | (With access token) | Server |
| |<-(F)------ API response ------- | |
+--------+ +---------------+
In our case both Resource server and Authorization server are crossbow app.
The following are the important points from our oauth implementation.
- The token exchange and authentication will happen over SSL so that the token security is not compromised.
- The access token is expired every 60 days.
- We do not use 'refresh_token' to regenerate access token, to keep the implementation simple. Every time a token is expired user has to enter his username and password with the client and get the access token generated.
=== Sample Oauth Request and Response ===
**Request URI -** https://api.crossbow.com/api/authentication/token
**Parameters -** client_id, client_secret, grant_type, username, password, format,
Except format all are mandatory.
The value of grant_type should be - **passsword**
The default value of format is xml.
__**Successful authentication response**__
**1. XML -**
6uJ0xn1mynyh9UZZ3gC46L8UPImLv6r9fsEWmz9T
bearer
5183999
**2. JSON -**
{"oauth2_token":
{"token_type":"bearer",
"expires_in":5183999,
"access_token":"WFfKQaElw1dvNggDK4eBuiyNbrcS2xajCDs2LI2p"
}
}
**__Failed authentication Response__**
**1. XML**
invalid_user
ERRR00005
**2. JSON**
{"api":
"response":
{"error":
{"description":"invalid_user","error_code":"ERRR00005"}
}
}
==== 2. Grant type - auth_code ====
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
In our case both Resource server and Authorization server are crossbow app.
=== Sample Oauth Requests and Response ===
**__STEP 1: Client app sends a request to authorize url of crossbow -__**
The Client application sends a request to the authorize url of crossbow.
**Request URI -** https://api.crossbow.com/api/authentication/oauth/authorize?response_type=code&client_id=&redirect_uri=
This redirects user to a login page.
**1. On successful login and authorize -**
- The user will be redirected to **?code=W25JoW2cktPurc7vpBaI**
The client sends a request to the token url mentioned above and receives token.
The only difference is that the grant_type param value should be **authorization_code** when requesting for token.
**2. On unsuccessful login and authorize -**
The client will be redirected to **?error=**
The most common error_desription will be **access_denied**
**__STEP 2: 1. Client app sends a request for access token-__**
The Client application sends a request to the token url of crossbow.
**Request URI -** https://api.crossbow.com/api/authentication/token
**Parameters -** client_id, client_secret, grant_type, username, password, format,
Except format all are mandatory.
The value of grant_type should be - **authorization_code**
The default value of format is xml.
The response for this call is same as the one we have above for password grant_type.
===== Implementation ======
==== Providing OAuth Service ====
The service provision for Oauth is developed by inspiring from the plugin - https://github.com/pelle/oauth-plugin
This gem does not cover the complete flow but it can be used base and the tweaks can be made to incorporate it with our app flow.
**__Prerequisites__**
* This plugin requires oauth2 gem to be installed.
* App should use restful_athentication or any other equivalent plugin.
**__Note -__**
One more alternative explored was - https://github.com/ThoughtWorksStudios/oauth2_provider
It has a cleaner design but we are not using it as it only supports version 2-09 of OAuth 2.0 spec.
==== Consuming OAuth service (for clients) ====
The Oauth plugin (https://github.com/pelle/oauth-plugin) has some code which can be tweaked to implement a client for the Oauth service.
In addition to this we can also use the following ruby based libraries to implement clients-
https://github.com/intridea/oauth2
https://github.com/aflatter/oauth2-ruby
===== Database Design =====
The following tables are needed to store the details of the clients and tokens.
Table name - client_applications
^ Column_name ^type ^ Description ^
| id | integer | primary key |
| name | string | name of the client application |
| key| string | unique client identifier |
| secret | integer | unique client secret |
|url | string | The home page url of the client app|
| callback_url | string | The default call back url to which the redirection should happen after authorizing. |
|support_url| string | The support page url of the client if any. This is optional|
| company_id | integer | foreign key for companies table|
|status| integer | used to determine if the client is active or not|
|action| integer | used to determine if the client is approved, active or rejected|
|activated_at| date time | The time when the client app is activated |
| created_at| date_time |created date |
| updated_at| date_time|updated date |
Table name - oauth_tokens
^ Column_name ^type ^ Description ^
| id | integer | primary key |
| user_id | inetger |id of user |
| client_application_id | id | client id which is requesting the access. (Foreign key)|
|type| string | Type of the token. Useful if we maintain multiple types of tokens(Ex- OAuth 2.0, Oauth 2.1). For now, it has one value. |
| token | string | unique token for user |
| secret | string | unique secret for user |
|callback_url| string | client app's call back url for this access token|
|verifier| string | Not used for now |
|scope| string | Useful when we have different types of access privileges for tokens. Not used now. |
| expires_at | date_time | time when the token expires |
| created_at| date_time |created date |
| updated_at| date_time|updated date |
Table name - oauth_nonces
//(Note - We do not use this table at this moment as we don't refresh tokens. But this is created to maintain the code consistency.)//
^ Column_name ^type ^ Description ^
| id | integer | primary key |
| nonce | string ||
| timestamp |integer | |
| created_at| date_time |created date |
| updated_at| date_time|updated date |