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 -
There are 4 different types of token grant are available. We use two of them which are famous and applicable to our case.
+--------+ +---------------+ | |--(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.
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 -
<oauth2-token> <access-token>6uJ0xn1mynyh9UZZ3gC46L8UPImLv6r9fsEWmz9T</access-token> <token-type>bearer</token-type> <expires-in>5183999</expires-in> </oauth2-token>
2. JSON -
{"oauth2_token":
{"token_type":"bearer",
"expires_in":5183999,
"access_token":"WFfKQaElw1dvNggDK4eBuiyNbrcS2xajCDs2LI2p"
}
}
Failed authentication Response
1. XML
<api>
<response>
<error>
<description>invalid_user</description>
<error-code>ERRR00005</error-code>
</error>
</response>
</api>
2. JSON
{"api":
"response":
{"error":
{"description":"invalid_user","error_code":"ERRR00005"}
}
}
+--------+ +---------------+
| |--(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.
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=<client_app_id>&redirect_uri=<client_app_redirect_uri>
This redirects user to a login page.
1. On successful login and authorize -
- The user will be redirected to <client_app_redirect_uri>?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 <client_app_redirect_uri>?error=<error_description>
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.
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
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.
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-
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 |