Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
DQuid IO - OAuth2 - Implementation
===================
This document contains a guide to implement an OAuth2 client for DQuid IO's services.
## A brief overview of OAuth2
### Access Tokens
APIs protected via OAuth2 require what is known as an **access token** to be accessed. An access token allows to implement a form of HTTP authentication which requires the token to be sent **alongside every request**, most commonly in the **authorization** header of the HTTP request:
`Authorization: Bearer j4frw54-45633-653356-3664456646-545`
In the example above the string that follows the word *Bearer* is the access token, which is usually a randomly generated string.
What's important is how the token is obtained, described in the following sections.
### Authorization server
In OAuth2 the authorization server is an application which supplies access tokens. It does that using different flows, all described in the OAuth2 protocol. Whenever you need to generate an access token to call a protected API you'll need to do that by communicating with the authorization server using its HTTP API first.
### Clients
OAuth2 has the notion of *clients*. A client in OAuth2 is an application which wants to use a protected API. Anyone who wants to write an application using such an API must create a client, which in our case is (will be) possible via the developer portal.
A client is identified by two pieces of information: a **client id** and a **client secret**, which are automatically assigned when the client is created. This or part of data must be embedded in the application which wants to use the protected API, as it is necessary to obtain an access token, which is **always associated to a client**.
### User authentication and application authentication
Intuitively OAuth2 allows APIs to be accessed on behalf of users, which is the most common scenario. For example, an API which allows to retrieve all the units belonging to a user must be aware of who the user is when it's called. That is achieved using access tokens. When the HTTP request reaches the server, the server extracts the access token from the request and looks into its database of access tokens to discover which user the token is linked to.
Besides the common scenario described above it is important to understand that access tokens do not need to be linked to users. An access token may also not be linked to a user, in which case it cannot be used to access user-specific information. As mentioned above an access token is always tied to a client, and some APIs may be accessed using such a token if they don't require any association with a user. It's entirely up to the API implementer to decide which ones don't need to be accesses on behalf of a user. As an example, APIs which only expose client-related information may not need a user access token but only require a client access token.
### Token expiry
Access tokens should **expire**. Whenever an access token is generated its expiry in seconds is returned in the response (usually 3600 seconds - 1 hour). After the token expires it cannot be used again and another token must be obtained. A new token can be obtained using the same information used when obtaining the expired one, or a mechanism built into OAuth2 can be used in some scenarios, because obtaining again the information used when the first token was generated might not be feasible. This mechanism is called *refresh tokens* and is discussed later.
### Refresh tokens
Refresh tokens are a mechanism to obtain a new access token when one expires. Refresh tokens are tokens which are returned in some scenarios whenever an access token is generated. A refresh token should be stored in a secure place by the client who received it as it can be used to obtain new access tokens and the client should track its expiry in order to decide when to request a new one (or simply wait until a request to any protected API fails because the access token has expired).
Obtaining a new access token using a refresh token requires a request to the token endpoint as shown below:
```
Authorization: Basic {basic auth}
refresh_token={refresh token}&grant_type=refresh_token
```
A successful response will have this form:
```js
{
"access_token": "ohwgifheiorht349pgergpreirugheig",
"expires_in": 12345,
"token_type": "Bearer"
}
```
The only thing to be aware of is that access tokens created with refresh tokens will not create a new refresh token, because refresh tokens don't expire and can be reused forever or until they are revoked.
## OAuth2 flows
The OAuth2 specification includes several flows, which are basically different ways of obtaining access tokens which suit different kind of applications. This section provides a brief overview of the different flows, but a thorough explanation is beyond the scope of this document and it is advisable to read the full specification[^1].
### Authorization code
The authorization code flow consists in obtaining first an authorization code from the authorization server, and then using that authorization code to request an access token. This flow is commonly used in server-side applications, which are not covered in this document.
### Resource owner password credentials
This flow consists in sending the credentials of the user from the client to the authorization server, therefore it implies a trust relationship between the client enabled to use this flow and the authorization server.
> In DQuid's implementation of OAuth2 this flow is enabled only if the client is trusted (a flag stored in the database alongside each client)
The client prompts the user for its credentials, submits them to the authorization server which eventually responds directly with an access token.
Note that this flow implies that the client has access to its client secret because it is required to access the `/token` endpoint (protected via basic authentication). If a client cannot keep its client secret hidden there's a danger of client impersonation, that is, any other client can pretend to be a trusted client, once they discover the trusted client's credentials.
**Unfortunately, this is not a scenario that OAuth2 addresses in any way**, implementors are thus required to come up with their own way to balance the user-friendliness of this approach and the security threat inherent in its use.
#### Implementation
Obtaining an access token involves sending a POST request to the authorization server's token endpoint as shown below:
```
Authorization: Basic {basic auth}
username={username}&password={password}&grant_type=password
```
Strings in curly brackets represent placeholders. `{basic auth}` is the base64 encoding of the string `{client_id}:{client_secret}`, which is **always** required to access the token endpoint, regardless of the flow.
The body of the request carries the username and password of the user as well as a fixed parameter `grant_type=password` which informs the authorization server of the kind of flow used. The authorization server supports several content types for the body of the request.
The response will contain in its body a JSON document similar to:
```js
Content-type: "application/json"
{
"access_token": "fewohr938ygreilhp3h5ge9phg3wr9h",
"refresh_token": "wefo439tyehrgpthj39wgh3904h39gh",
"expires_in": 12345,
"token_type": "Bearer"
}
```
The most important piece of information is the `access_token` as it can be used to access the protected APIs from now on and until it expires (or it is revoked).
### Implicit
The implicit flow is a simplified flow which skips one intermediate step compared to the authorization code flow. It is initiated in the same way as the authorization code flow (although with different query string parameters) but rather than getting back an authorization code it obtains an access token immediately.
This flow does not require the client to keep its credentials secret and can therefore be used in scenarios like SPAs and mobile applications. The security guarantees offered by the client secret are basically replaced by the redirect URI, which all clients need to specify and which is where a user performing the authorization flow is redirected to after the flow has completed. This requires that a client implementing this flow has a way to show the user a Web page (which is initially the login screen on the authorization server) and accept incoming requests (the redirections from the authorization server back to the application).
This often does not require the client to be able to run a Web server but rather to simply access the query string or hash of the redirection url, which is where access tokens are commonly supplied when the authorization server redirects back to the application.
#### Implementation
The first step involves obtaining authorization from the user, which in turn requires that the user is authenticated on the authorization server and that he allows the client to access his data (via a consent screen).
This is accomplished by first pointing the user to the authorization server's authorization endpoint. The means by which the client performs this step are entirely up to the client and are usually either an embedded Web View (in the case of a mobile client) or an external browser window (in case of a Web client or a mobile client).
The url should look similar to this:
`/authorize?client_id={client_id}&redirect_uri={redirect_uri}&response_type=token`
The `client_id` parameter identifies the client and `redirect_uri` parameter should match that configured when the client was created (otherwise it would be possible for a client to impersonate any other client by redirecting the user to a redirect uri different from that configured in the original client).
The authorization server first checks if the user is already authenticated via traditional means like session cookies (which is how it's implemented in DQuid's authorization server), otherwise it prompts the user for his credentials.
One authenticated, the user is shown a *consent screen*, which asks for his confirmation to authorize that specific client to access his data.
Once the user accepts the authorization server redirects him back to the client, supplying the access token in the hash of the redirection url, that the client can then access.
`{redirect_uri}/#access_token={...}`
Because not all client types can accept requests to own urls there exist other mechanisms, not originally in the OAuth2 specification, to provide an access token using the implicit flow. Namely, they return the access token either in the title of a page hosted on the authorization server such that the client can access it, or in the body of the page, prompting the user to copy and paste it somewhere else.
## OAuth2 demo application
The flows described above and others are available in the *oauth-demo* hybrid mobile application[^2], which can be run in the browser as well as deployed on a mobile phone. It is advisable to check out its source code to better understand how to accomplish each scenario.
## Advice
OAuth2 was not designed to handle installed applications (mobile apps or desktop applications) and in general any application which either cannot keep a secret stored locally **and** cannot identify itself by means of a redirection uri. Especially with mobile application this becomes very tricky and there doesn't seem to be any ideal solution to the problem.
Keeping in mind that the problem is eventually limited to a client impersonating another client, which can be an issue in case a client has good reasons for wanting to impersonate another client, there are no further serious security threats.
When possible the user should be shown a consent screen clearly displaying information about the client that is requesting access to the user's information.
The simplest flow to implement in a mobile app is therefore the *resource owner password credentials* flow, although it lends itself to this problem.
Next comes the *implicit* flow, which doesn't suffer from such security problems but requires a login and consent screen page to be created on the authorization server, in addition to a redirect uri, which is not easily accomplished in mobile applications.
That's probably one of the reasons why Google came up with its own (non-standard) extensions to the protocol. When registering a client application on Google's API console as an *installed application* (mobile, desktop) and it is specified that the app will run on Android or iOS, the developer is prompted for details identifying unequivocally the client application when distributed on the respective platform app stores. It's not clear how Goolge uses and validates this information, the only conjecture is that if Google's own SDK is used to carry out the authorization flow and call the APIs then this check might be happening within the SDK itself, which has access to the mobile device's information.
One additional mechanism that Google put in place is similar to redirect uris (and can go along with it, although it's not been implemented in DQuid's authorization server) but pertains instead javascript origins. This only applies to SPAs though, as Ajax requests usually carry the address of the page they originated from.
## Additional options
Although not investigated, mobile platforms have the notion of *account managers*. Android for example provides specific classes in its SDK[^3] to implement such a feature, which basically seems to store account information for various services (which can build their own account managers) locally and in a secure way.
This obviously requires that the mobile application itself implements such a feature and that the authorization server collaborates somehow to establish an initial trusted communication which allows the mobile app and the authorization server to exchange whatever information can be later used to allow the client to call the protected APIs.
Although probably the most demanding solution to implement, this sounds like the way to go in order to provide secure access to protected resources without the security risks involved in using OAuth2 alone on a mobile application.
[^1]: https://tools.ietf.org/html/rfc6749
[^2]: https://bitbucket.org/dquid/oauth-demo
[^3]: http://developer.android.com/reference/android/accounts/AccountManager.html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.