OAuth 2.0: Best Practices

We are very serious about the privacy of our customers. When we grant access to our APIs, we expect you to take our customers’ privacy just as seriously as we do.

We use OAuth 2.0 authentication protocol to ensure that applications on our platform are safe and easy to use.

Ensure your application follows these best practices.

Use HTTPS

All requests to our authentication servers must be made over HTTPS to prevent man-in-the-middle attacks. Additionally, OAuth 2.0 requests require you to use HTTPS. Your app should also ideally be hosted on a secure server.


Client ID and secret

The Client ID is a public identifier of your application. The Client secret is confidential and should only be used to authenticate your application and make requests to Apaleo’s APIs.

Do not distribute Client secrets in email, distributed native applications, client-side JavaScript, or public code repositories. Do not store it in files on a web server that can be viewed externally. For example, configuration files, include files, log files, etc.

When you exchange an OAuth 2.0 authorization code for an access token, the Client secret is passed as part of the request. Ensure that you do not expose this request publicly.


OAuth access tokens

OAuth 2.0 tokens are entrusted to you by users who give you permission to act and access data on their behalf. Using access tokens, you can access a user’s private information through the Apaleo APIs. To keep access tokens safe:

  • Do not store them in insecure or easily accessible locations. Client-side files, such as JavaScript or HTML files, should never be used to store sensitive information, as these can easily be accessed.
  • When making calls, always pass access tokens over a secure (HTTPS) connection.
  • Do not discard your OAuth token after one-time use. API calls to retrieve an OAuth token are rate-limited per application. To avoid hitting your call limit, cache, and re-use tokens until the expiration time. For more information, see OAuth 2.0: Refresh token grant flow.

Thwart cross-site request forgery (CSRF) attacks

Your app can use a state parameter to ensure that the response belongs to a request initiated by the same user, therefore preventing CSRF attacks. If the state does not match, it means that there could be a possible redirect by a malicious attacker. For more information, see RFC-6749.


Redirect URIs

The redirect URI must adhere to RFC 6749 Section 3.1.2. The URI must meet the following conditions:

  • Must use HTTPS
  • URLs must be an absolute URI. For example, https://example.com/auth/callback, not /auth/callback.
  • URL arguments are ignored. For example, https://example.com/?id=1 is the same as https://example.com/.
  • URLs cannot include a #. For example, https://example.com/auth/callback#mycat is invalid.

Scopes and permissions

Make sure that you only request what you absolutely need. When you do this, you are more likely to gain user consent when required because users are more likely to authorize access for limited, clearly-specified scopes and permissions.

Prefer a fine-grained client. A fine-grained client is granted only the permissions it needs, per API, and the hotel chooses which of its properties your app can access. Design your app to work with a subset of the account’s properties. It shouldn’t assume that it can see all of them, because hotels can change the selection later.

Request only the permissions you need. Every user who connects your app needs a role that includes all of its permissions. The more permissions you request, the fewer users can use your app.

If you use a scope client, request only the scopes you need. When an operation accepts more than one scope, choose the narrowest one that covers what your app does, for example a .read scope if your app only reads. To see which scopes and permissions an operation requires, see Scopes and permissions.

Request everything up front. If you change your app’s scopes or permissions later, users have to re-authorize your app for the change to take effect.

If you are using OpenID Connect enabled app, then you need to use the following standard scopes.

Scope Description
openid A mandatory scope that returns a sub claim, which represents a unique identifier for the authenticated user.
profile This scope requests access to the end-user’s default profile claims.
offline_access Requests a refresh token, so your app can get new access tokens without the user signing in again.

Always pass scope variable as a set of case-sensitive and space-delimited strings as follows:

Scope client:

scope: 'offline_access availability.read reservations.read'

In an OpenID Connect implementation (scope client):

scope: 'offline_access openid profile availability.read identity:account-users.read rates.read reservations.read'

Fine-grained client. Only OpenID Connect scopes go in scope, because API access is configured on the app. API scopes you send anyway are ignored:

scope: 'offline_access'

In an OpenID Connect implementation (fine-grained client):

scope: 'offline_access openid profile'

Handle lost access

A fine-grained client can lose access after it has been connected, for example when the hotel changes which properties the app can access, or when the user who connected the app no longer has a role and property access that cover what the app was granted. Make sure your app handles this without breaking:

  • 403 Forbidden on an API call. Your app lacks the permission the endpoint requires, hasn’t been granted access to the property the request is about, or the user whose token you use no longer has the required permission. Don’t retry the same call. Tell the hotel which permission or property is missing, so they can grant it. Your access token is still valid, and calls for other properties keep working.
  • 400 Bad Request when you refresh a token. The user who connected the app is no longer eligible, and your app has lost access for that hotel. Stop using the stored refresh token, mark the connection as disconnected, and ask the hotel to connect again. See OAuth 2.0: Refresh token grant flow.
  • Don’t show these messages to end users as they are. The refresh error contains the user name and the account code.
  • Don’t use scope to find out what your app can do. For a fine-grained client, scope in the token response doesn’t list API scopes. It contains the marker scope identity:permissions and, for apps where a user signs in, the OpenID Connect scopes, even though the app’s permissions are in effect.

Error handling

The error codes and their descriptions are well documented in the OAuth2 RFC, such as Code Grant Errors.

For errors returned by the APIs, such as 403 Forbidden, see HTTP status codes and error types.

Authorization response error codes

Error code Description Solution
invalid_request This error is typically due to a required parameter missing from the request. For example, omitting the response_type parameter would raise this error. Check whether all the parameters are correct, client_id exists, and so on.
unauthorized_client Your app is not authorized to request an authorization code using this grant method. The redirect_URI is either incorrect or not provided. Make sure that the provided redirect_URI is correct, and at least one redirect_uri is defined in the client app.
unsupported_response_type Apaleo does not support obtaining an authorization code using this method. Try to change the selected method of authorization.
invalid_scope If your app requests invalid, unknown, or malformed scope. API scopes sent by a fine-grained client are ignored and don’t raise this error. Make sure that the request is correct.

Token response error codes

Definition of errors of issuing Access Token in the RFC6749 standard

Error code Description Solution
invalid_request The request repeats a parameter, misses a required parameter, includes an unsupported parameter value (other than grant type), utilizes more than one mechanism for authenticating the client, or is otherwise malformed. Check the response header and request.
invalid_client Client authentication failed. For example, there’s no such client with that client_id and client_secret.
invalid_grant The Refresh token has been revoked. The authorization code has been already consumed or does not exist.
unauthorized_client Client is not allowed for code grant flow or for refreshing tokens.
unsupported_grant_type The authorization grant type is not supported by Apaleo.

For a fine-grained client, refreshing a token can also fail with 400 Bad Request and a response like the one below, when the user who connected the app is no longer eligible for it. See OAuth 2.0: Refresh token grant flow.

{
  "error": "User {userName} is no longer eligible for client {clientId} on account {accountCode}"
}