# Authorizing API requests

If you have decided to use one of the Bunny SDKs then you dont need to worry about this part as the SDK takes care of access token generation and including it as a header on each request for you.

If you've decided to build your own implementation and not use a Bunny SDK then you will need to do the following.

1. [Generate an access token](#generate-an-access-token)
2. [Include the access token in an authorization header](#set-an-authorization-header)
3. [Handle expired access tokens](#handle-expired-access-tokens)

### Generate an Access Token

Bunny uses the OAuth 2.0 protocol for issuing tokens and authorizing requests.

{% hint style="warning" %}
To generate an access token you will need the **Client ID** and **Client Secret** of Bunny API client application. [If you don't have an API client application setup yet then follow these steps](https://docs.bunny.com/developer/api#create-an-api-client).
{% endhint %}

To generate an access token we will be using the Client Credentials grant flow. This flow is designed to be a server side request so is not suitable for requesting tokens from a client side / javascript application.

#### Request

From your server side you will make the following POST request including your `client_id`, `client_secret` and the `scope` of access that you require for form encoded values.

{% tabs %}
{% tab title="Curl" %}
{% code overflow="wrap" %}

```bash
curl --location --request POST 'https://<subdomain>.bunny.com/oauth/token' \
--form 'grant_type="client_credentials"' \
--form 'scope="standard:read standard:write"' \
--form 'client_id="<client_id>"' \
--form 'client_secret="<client_secret>"'
```

{% endcode %}
{% endtab %}

{% tab title="Ruby" %}
{% code overflow="wrap" %}

```ruby
require "uri"
require "net/http"

url = URI("https://<subdomain>.bunny.com/oauth/token")

https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Content-Type"] = "application/x-www-form-urlencoded"
form_data = [
    ['grant_type', 'client_credentials'],
    ['scope', 'standard:read standard:write'],
    ['client_id', '<client_id>'],
    ['client_secret', '<client_secret>']
]
request.set_form form_data, 'multipart/form-data'
response = https.request(request)
puts response.read_body
```

{% endcode %}
{% endtab %}

{% tab title="Javascript" %}

```javascript
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");

var formdata = new FormData();
formdata.append("grant_type", "client_credentials");
formdata.append("scope", "standard:read standard:write");
formdata.append("client_id", "<client_id>");
formdata.append("client_secret", "<client_secret>");

var requestOptions = {
  method: 'POST',
  headers: myHeaders,
  body: formdata,
  redirect: 'follow'
};

fetch("https://<subdomain>.bunny.com/oauth/token", requestOptions)
  .then(response => response.text())
  .then(result => console.log(result))
  .catch(error => console.log('error', error));
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
You can only request scopes that have assigned to your API client application.
{% endhint %}

#### Response

You will get a JSON response containing the access token or alternately a error description describing why the request failed.

{% tabs %}
{% tab title="200 OK" %}
Success! An access token is returned.

{% code overflow="wrap" %}

```json
{
    "access_token": "eyJraWQiOiJPRFNuNWpnd2pESmRJdmxfTERiYmNJTkdKcTVITHo5cEhxN292NGVsb3VZIiwiYWxnIjoiSFM1MTIifQ.eyJpc3MiOiJCbGFoIEJsYWgiLCJpYXQiOjE2NTMwOTk3NDUsImp0aSI6ImFkYjliMzY5LWJiYmMtNGU1MC1iNjdjLWE1NGU5MDZjY2I5ZCIsImNsaWVudF9pZCI6Ik9EU241amd3akRKZEl2bF9MRGJiY0lOR0pxNUhMejlwSHE3b3Y0ZWxvdVkiLCJhdWQiOiJodHRwczovL2J1bm55LmludGVybmFsIiwiZXhwIjoxNjUzMTA2OTQ1LCJzY29wZSI6IiIsInN1YiI6ImM2MTBkOTFhLWQwZDUtNDQxMi1iZTE2LWQzNWQyNjVmOTRhZCJ9.tHN1jOMYojDSbg3ryrWqu4GBCUQhIqq2mRa_heXUDDHqomTFjUoPRFMmInz4X8orIdPy4Zzxu_UsoT1MZf7_iQ",
    "token_type": "Bearer",
    "expires_in": 7200,
    "created_at": 1653099745
}
```

{% endcode %}
{% endtab %}

{% tab title="400 Bad Request" %}
You've likely requested a scope that is not approved for this API client application

```json
{
    "error": "invalid_scope",
    "error_description": "The requested scope is invalid, unknown, or malformed."
}
```

The `grant_type` must be `client_credentials`

```json
{
    "error": "unsupported_grant_type",
    "error_description": "The authorization grant type is not supported by the authorization server."
}
```

{% endtab %}

{% tab title="401 Unauthorized" %}
Either the `client_id` or `client_secret` has been entered incorrectly or the API client has been deleted.

```json
{
    "error": "invalid_client",
    "error_description": "Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method."
}
```

{% endtab %}
{% endtabs %}

### Set an authorization header

Add an Authorization header to your Bunny API request, including the access token as a bearer token.

*For example,* when getting a list of accounts.

{% tabs %}
{% tab title="Curl" %}
{% code overflow="wrap" %}

```bash
curl --location --request POST 'https://<subdomain>.bunny.com/graphql' \
--header 'Authorization: Bearer <access_token>' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query accounts ($filter: String, $limit: Int) {\n    accounts (filter: $filter, limit: $limit) {\n        id \n        name\n    }\n}","variables":{"filter":"","limit":10}}'
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Handling expired access tokens

Depending on the access token expiry length that was defined for your API client application at some point your token will expire and an API request will fail.

In this case you will want to generate a new access token and try the request again.

*For example,* if your API client application has an access token expiry of 7200 seconds then every 2 hours you would need to request a new token. If you proceeded to make a request after 2 hours with an expired token you would get the following response.

{% tabs %}
{% tab title="401 Unauthorized" %}

```json
[
    {
        "description": [
            "Token is expired"
        ]
    }
]
```

{% endtab %}
{% endtabs %}

In this case your code could watch out for the HTTP 401 status and attempt to generate a new access token. Then with the new access token you should retry the original API request.
