OAuth 2.0: Refresh token grant flow
The Refresh Token grant type is used by clients to exchange a refresh token for an access token when the access token has expired. You can get refresh tokens only for the OAuth 2.0: Authorization code flow.
App (Client)
│
│ 1. Authenticate with client ID, secret, and refresh token
│ to request a new access token
├──────────────────────▶ Apaleo Identity API (Authorization Server)
│
│ 2. Provide an access token
│◀────────────────────── Apaleo Identity API (Authorization Server)
│
│ 3. Authenticate with the access token to request data
│ from the resource server
├──────────────────────▶ Apaleo Resource APIs (Resource Server)
│
│ 4. Provide the requested data
│◀────────────────────── Apaleo Resource APIs (Resource Server)
▼
New OAuth2 access tokens have expirations. Tokens return an expires_in field indicating how long the token will last.
A refresh token is a special kind of token that contains the information required to obtain a new access token or ID token. Usually, you will need a new access token only after the previous one expires.
Client requests a new token
The refresh token grant allows your app to ask Apaleo to issue a new access_token directly, without re-authenticating the user. This will work as long as the refresh token has not been revoked and, for fine-grained clients, the user who connected the app is still eligible (see Refresh can fail if the user is no longer eligible).
Refresh tokens are subject to strict storage requirements to ensure that they are not leaked.
To refresh your token, using the refresh_token you already got during authorization, make a POST request to the https://identity.apaleo.com/connect/token endpoint in the authentication API, using grant_type=refresh_token.
POST https://identity.apaleo.com/connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&client_id=SDXE-AC-MYAPP&client_secret=ZiEWFgFeSP......5yhTgsZgx&refresh_token=_A4CpoIDT.......qYEpxEW8g
| Request | Value |
|---|---|
| Method | POST |
| URL | Uses the OAuth endpoint https://identity.apaleo.com/connect/token. |
| Header | Value |
|---|---|
Authorization |
Basic authorization, using the client id and client secret of the OAuth client. |
Content-Type |
application/x-www-form-urlencoded |
Example cURL request
You can test the creation of token by running the following cURL command.
On Windows, use Windows Subsystem for Linux (WSL) to run the cURL command.
curl -X POST \
https://identity.apaleo.com/connect/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data client_id=SDXE-AC-MYAPP \
--data client_secret=ZiEWFgFeSP......5yhTgsZgx \
--data grant_type=refresh_token \
--data refresh_token=E042F......5EC95
| Key | Description |
|---|---|
| client_id | The ID assigned to your app when it was registered. |
| client_secret | Your client application’s secret. |
| grant_type | The value refresh_token. |
| refresh_token | The refresh_token that you already got when you exchanged authorization code for token in the OAuth 2.0: Authorization code flow. |
Refresh can fail if the user is no longer eligible
With a fine-grained client, the identity server checks the connecting user’s eligibility every time you refresh a token and on every API call, not only when the user first connects your app. A user is eligible only if their role and property access cover all the permissions your app requires. If the user’s Apaleo role or property access no longer covers everything your app was granted, the refresh fails with 400 Bad Request, even though the refresh_token itself hasn’t expired or been revoked:
{
"error": "User {userName} is no longer eligible for client {clientId} on account {accountCode}"
}
When you get this response, your app has lost access for that hotel:
- Stop using the stored
refresh_tokenfor that connection, and mark the connection as disconnected in your app. - Ask the hotel to connect your app again. The connecting user’s role and property access must cover everything your app requests.
- Don’t show the error message to end users as it is, because it contains the user name and the account code.
If refresh calls that used to succeed start failing with this response, check whether the connecting user’s role or property access changed before assuming it’s an integration bug.
This is different from API calls that return 403 Forbidden. A 403 means that your app lacks the permission an endpoint requires, or hasn’t been granted access to the property the request is about. Your access token itself is still valid.
Authorization server grants a new token
After verifying the request, the identity server sends a response with a new access token to the connected app.
The response will include a new access_token, its type, its lifetime (in seconds), and the id_token.
{
"id_token": "eyJ...b9w",
"access_token": "eyJ...Vsg",
"expires_in": 3600,
"token_type": "Bearer",
"refresh_token": "E042F......5EC95",
"scope": "openid profile availability.read rates.read reservations.read identity:account-users.read offline_access"
}
The example above is for a scope client. For a fine-grained client, scope contains the marker scope identity:permissions and the OpenID Connect scopes (for example openid profile offline_access), but no API scopes. The app’s permissions are still in effect and are enforced separately. Don’t infer API access from this field; check the client’s configured permissions instead.
You should only ask for a new token if the access_token has expired, or you want to refresh the claims contained in the id_token. Calling the endpoint to get a new access_token every time you call an API works, but we wouldn’t call it the best practice.
During implementation or debugging, you might want to check the contents of those token, for example, to read the account code. Here’s one handy tool jwt.io. They write “[…] We do not record tokens, all validation and debugging is done on the client side”, and it should be okay to use them, but - better safe than sorry. We recommend to be cautious, and not post the production tokens of real customers there.