===== 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 |