Client Developer Guide deprecated

General Assumptions

Before you can develop against the API of an OX App Suite deployment you must register your application at the according service provider. The registration process is out of scope here. Please contact your service provider for more information. As a result of the registration process you will get two tokens from your service provider: a client identifier and a client secret. The former one must be provided in every call targeting the authorization API. The latter one is only needed when an authorization code is exchanged for an access/refresh token pair and must strictly be kept secret.

Client applications must be of type confidential according to the web application profile, i.e. they must be able to securely store API access credentials on an application server without exposing them to the resource owner. The only supported grant type is authorization code. Every time a user grants access to his personal data, your application will receive both, an access token and a refresh token. The former one is a short-living token (usually one hour), that must be sent along with every API call. The latter one lives as long as the user does not revoke access for your application and can always be exchanged against a fresh token pair. The access token type is bearer as defined in RFC 6750.

All calls towards the authorization or token endpoint enforce HTTPS. Plain HTTP calls result in redirects to the secure location. However you must never perform plain HTTP calls. Especially calls to the token endpoint will otherwise expose your client secret!

Authorization Flow

Note: The following information about authorization flow only refers to the use of the built-in authorization server.

Step 1: Request an authorization code

GET https://ox.example.com/appsuite/api/oauth/provider/authorization

Parameter Description Required
client_id The client identifier provided by your OX service provider. Yes
redirect_uri The URI used for transmitting the authorization code. Must be one of the URIs that have been registered at your service provider, otherwise it will not be accepted. Yes
state An arbitrary string that helps you to prevent CSRF attacks and to associate the authorization code with the initial request. Yes
response_type Must always be code. Yes
scope The scope according to the API requests you are going to make after access was granted. Can be omitted, in which case the default scope is applied that has been submitted during client registration. A scope is a space-separated string of scope tokens, e.g. read_contacts write_contacts. No
language An optional locale (e.g. de_DE) that is used to translate the user-visible parts of the authorization flow if possible. If omitted en_US is used. No

The response is a login screen. You probably want to display it within a popup window: OAuth Login Screen

After signing in the user gets display a screen where he can choose to grant or deny access to the requesting application: OAuth Grant Screen

This screen will present all necessary information to the user, i.e. which app tries to obtain access for which scopes. The user can then decide to grant access (by providing his credentials) or to deny the request. In both cases as well as in certain error cases an authorization code or an error is provided to your application via another redirect:

HTTP/1.1 302 Found
Location: https://myclient.com/oauth/callback?code=07d3c9d7e6fc4e828c600bba0eee2ad0&state=1234
Parameter Description
code The authorization code. Only present if the request succeeded and therefore error is absent.
state The state parameter provided by your application during the authorization request. This parameter is always present except in one case: You did not include a state parameter and therefore receive an error.
error This parameter is present if your request failed or the user denied access. Its value is one of the error codes defined here. Potential errors are listed in the table below.
error_description A description that further describes an occurred error. Only present if error is present.
Error Code Description
invalid_request Your request was malformed, i.e. a mandatory parameter was missing or a parameters value was invalid.
invalid_scope The requested scope or one of its tokens was invalid.
temporarily_unavailable The service is currently not available. You may try again later.
server_error An internal error occurred. You may try again later or contact your service provider if it happens again.
access_denied The user denied your request or is not allowed to grant access to 3rd party applications.
unsupported_response_type You provided a response_type different than "code".

Step 2: Obtain a token pair

POST https://ox.example.com/appsuite/api/oauth/provider/accessToken

The request body must be of type application/x-www-form-urlencoded containing the following parameters:

Parameter Description Required
client_id The client identifier provided by your OX service provider. Yes
client_secret The client secret provided by your OX service provider. Yes
redirect_uri The same redirect URI that was used to request the authorization code. Yes
grant_type Must be "authorization_code". Yes
code The authorization code. Yes

The response is in JSON format and contains either a token pair or an error code, depending on the succession of the request. A successful response looks like this:

HTTP/1.1 200 OK
{
  "access_token": "0062a74e65a74cefb364dcd17648eb04",
  "refresh_token": "a6fa1687d3434a08aa4b60b2c8bab1ec",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "read_contacts read_calendar"
}

expires_in contains the number of seconds the access token is valid from now on. scope contains the scope that was granted. It might differ from the initial requested scope in cases the user itself does not have all necessary permissions that you requested.

The following error conditions exist:

  • invalid_request: The request was malformed. HTTP status 400.
  • unauthorized_client: Client secret was invalid. HTTP status 401.
  • server_error: An internal error occurred. HTTP status 500.

An error response will look like this:

HTTP/1.1 400 Bad Request
{
  "error": "invalid_request",
  "error_description": "invalid parameter value: client_id"
}

Step 3: Refresh an access token

You must remember the expires_in value and obtain a new access token before this timeout is exceeded by transmitting the refresh token.

POST https://ox.example.com/appsuite/api/oauth/provider/accessToken

Parameter Description Required
client_id The client identifier provided by your OX service provider. Yes
client_secret The client secret provided by your OX service provider. Yes
grant_type Must be "refresh_token". Yes
refresh_token The refresh token. Yes

The response is the same as in step 2. Note that also a new refresh token is generated and contained in the response. The one that was used in the request is not valid any longer.

Step 4: Revoke requested access

If you don't longer need a formerly granted access, you are encouraged to revoke it. This will invalidate the according access and refresh token what is desired from a security perspective.

GET https://ox.example.com/appsuite/api/oauth/provider/revoke

Parameter Description Required
access_token The access token. Note that the whole grant will be revoked, i.e. the according refresh token will also be invalidated. No
refresh_token The refresh token. No

It's up to you if you want to invalidate by access or refresh token, as long as you provide one them and it's still valid. If the token is not valid, an error is returned:

HTTP/1.1 400 Bad Request
{
  "error": "invalid_request",
  "error_description": "invalid parameter value: access_token"
}

Validating the token

Provided that the Open-Xchange Server is also acting as authorization server verifying a token using the Open-Xchange Authorization Server endpoint is relatively simple. Your application includes the access token in the access_token parameter for the following endpoint:

GET /appsuite/api/oauth/provider/tokeninfo?access_token=ae113KfFBGRNJru1FQd44AzqT3Zg...

That endpoint accepts an access token and returns information about that access token including which application was it issued to, the scopes the user consented to, the remaining lifetime of the token, and the context/user identifiers. Example:

{
  "audience": "ZGVmYXVsdA/4ecafb74...b94b0a8e4e0e325d07f6d0",
  "context_id": 1,
  "user_id": 2,
  "expiration_date": "2016-09-15T00:00:00",
  "scope": "write_contacts read_contacts"
}

If the token has expired, has been tampered with, or the permissions revoked, the Open-Xchange Authorization Server will respond with an error. The error surfaces as a 400 status code, and a JSON body as follows:

{"error":"invalid_token"}

Available APIs

HTTP JSON API

With OAuth you can access a subset of the existing HTTP API. There are a few differences to the existing API documentation you have to keep in mind:

  • The available modules and actions can be accessed via a special servlet path /appsuite/api/oauth/modules in addition to the normal path. But this subpath is deprecated an will be removed in the future.

    GET /appsuite/api/contacts?action=all&folder=123
    or
    GET /appsuite/api/oauth/modules/contacts?action=all&folder=123 (deprecated)
    
  • The session parameter must be omitted for every request. Instead the access token must be provided as authorization header:

    Authorization: Bearer 0062a74e65a74cefb364dcd17648eb04
    
  • In addition to the normal error handling every call may also result in a specific OAuth error response. Possible errors are:

    • Missing/invalid access token: No access token was provided or the provided one is invalid. The response follows the scheme defined in RFC 6750, section 3. Example:

      HTTP/1.1 401 Unauthorized
      WWW-Authenticate: Bearer realm="example",
                        error="invalid_token",
                        error_description="The access token expired"
      
    • Insufficient scope: You cannot perform this call because the OAuth grant does not include a required scope. Example:

      HTTP/1.1 403 Forbidden
      Content-Type: application/json;charset=UTF-8
      
      {
        "error": "insufficient_scope",
        "scope": "write_contacts"
      }
      
    • Invalid request: The request was considered invalid from an OAuth perspective. Example:

      HTTP/1.1 400 Bad Request
      Content-Type: application/json;charset=UTF-8
      
      {
        "error": "invalid_request",
        "error_description": "Some detailed description relevant for client developers."
      }
      

Find the OAuth-enabled modules and actions below. Every action is bound to a specific scope. However some actions are available implicitly if access for any scope is granted. E.g. you may always request a users details or configuration if any kind of OAuth access is granted, but you may only change a users configuration, if the write_userconfig scope is granted. The view on the folder tree is always limited by the granted scope. E.g. if you were granted read_contacts you can also perform all read-only requests that target contact folders. In turn you may only create/modify/delete contact folders if obtained the write_contacts scope.

More detailed information about the required scopes for each action can be found in the HTTP API documentation.

Module Scopes
config write_userconfig
folders <depends>
mail / mailcompose / mailfilter/v2 read_mail, write_mail
calendar / chronos read_calendar, write_calendar
contacts / addressbooks read_contacts, write_contacts
infostore / files / fileaccount / fileservice read_files, write_files
drive read_drive, write_drive
tasks read_tasks, write_tasks
reminder read_reminder, write_reminder
snippets write_userconfig
user/me <any>
import write_calendar, write_contacts, write_tasks
export read_calendar, read_contacts, read_tasks
attachments read_contacts, write_contacts, read_calendar, write_calendar, read_tasks, write_tasks

Card- and CalDAV

If installed, the interfaces for Card- and CalDAV are also available via OAuth. Clients can notice it based on the WWW-Authenticate headers of responses to unauthorized requests. If OAuth is supported, a header declaring Bearer as supported auth scheme will be present. The according scope values are carddav and caldav. The interfaces support OAuth natively so there is no extra servlet path or the like.