# Authorization to APIs

## Create a service account <a href="#create-a-service-account" id="create-a-service-account"></a>

Service accounts provide an identity for your processes to access our APIs. They can authenticate themselves using either a Public Key or Password Authentication.

We recommend that you use Public key authentication which doesn't require sending secrets over the network.

{% tabs %}
{% tab title="Public Key Authentication" %}
You will need a private key to sign your access token requests, and a corresponding public key that our identity server will use to verify the signature.

**Generate an RSA private key**

```bash
openssl genrsa -des3 -out private.pem 2048
```

OpenSSL will ask you for a passphrase that will be used to encrypt the private key file.

**Generate the corresponding public key**

```bash
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
```

Enter the private key passphrase when prompted.

**Create the service account**

Please reach out to your Technical Consultant to request a service account to be created. Specify that you chose Public Key Authentication and provide your public key.\
They will give you in return a Client ID and tenant ID that you will need in the following steps.
{% endtab %}

{% tab title="Password Authentication" %}
Please reach out to your Technical Consultant to request a service account to be created. Specify that you chose password authentication.\
They will provide you with a Client ID, secret and tenant ID that you will need in the following steps.
{% endtab %}
{% endtabs %}

## Obtaining access tokens <a href="#obtaining-access-tokens" id="obtaining-access-tokens"></a>

Access tokens can be obtained using the OpenId Connect Client Credentials grant.

The token endpoint to use is: `https://iam.attraqt.io/auth/realms/${tenantId}/protocol/openid-connect/token` where `${tenantId}` is the identifier of your Attraqt tenant.

{% hint style="danger" %}
This endpoint is rate-limited at 10 requests per seconds, which is more than enough when tokens are properly cached and reused until expiration.\
Hitting the rate limit will cause HTTP 429 errors. It is recommended to implement a retry strategy to handle those errors (see also [https://github.com/Attraqt/product-discovery-documentation/blob/main/new-docs/sending-and-managing-data/sending-product-data/item-catalog-management/troubleshooting-api-errors.md](https://github.com/Attraqt/product-discovery-documentation/blob/main/new-docs/sending-and-managing-data/sending-product-data/item-catalog-management/troubleshooting-api-errors.md "mention")).
{% endhint %}

The process differs depending on whether you are using Public Key or Password authentication.

{% tabs %}
{% tab title="Public Key Authentication" %}
Service accounts with the JWT authentication method need to send a JWT signed with their private key to get access tokens, as described in [RFC 7523](https://tools.ietf.org/html/rfc7523).

The basic steps are:

1. Build a [JWT](https://en.wikipedia.org/wiki/JSON_Web_Token) with the following claims:
   * `iss`: your client ID (c.f. previous step)
   * `sub`: also your client ID
   * `aud`: the token endpoint
   * `jti`: a uniquely generated ID
   * `exp`: a rather short expiration time, one minute is more than enough.
2. Sign the JWT with the private key you generated earlier.
3. Send a `client_credentials` grant request to the authorization server token endpoint.
4. Use the `access_token` in the response to obtain Requesting Party Tokens that will be needed to authenticate your requests to Attraqt private APIs (see below).

**Node.js example**

```javascript
const { promisify } = require('util');
const { readFile } = require('fs');
const { sign } = require('jsonwebtoken');
const uuid = require('uuid');
const request = require('request-promise-native');

// ...

// replace this by the path of the PEM file containing your encrypted private key
// (c.f. "Create a Service Account" section)
const encryptedPrivateKeyPem = await promisify(readFile)('/path/to/my/pkcs8/key.pem', 'utf8');
// replace this by the passphrase you used when generating your private key
const encryptedPrivateKeyPassphrase = 'yourSecretPassphrase'
// replace this by the client ID of your service account
// (c.f. "Create a Service Account" section)
const serviceAccountClientId = 'your-client-id';
// replace this by your Attraqt tenant ID
const tenantId = "myTenantId";
const tokenEnpoint = `https://iam.attraqt.io/auth/realms/${tenantId}/protocol/openid-connect/token`;

const jwt = await promisify(sign)(
    {},
    {
        key: encryptedPrivateKeyPem,
        passphrase: encryptedPrivateKeyPassphrase
    },
    {
        jwtid: uuid.v4(),
        issuer:  serviceAccountClientId,
        subject:  serviceAccountClientId,
        audience:  tokenEnpoint,
        expiresIn:  "1min",
        algorithm:  "RS256"
    }
);

const credentials = await request({
    method: 'POST',
    uri: tokenEnpoint,
    form: {
        grant_type: 'client_credentials',
        client_id: serviceAccountClientId,
        client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
        client_assertion: jwt,
    },
    json: true,
});

const expiresIn = kcCredentials.expires_in; // seconds
const authorizationHeader = `${kcCredentials.token_type} ${kcCredentials.access_token}`;
// Cache the authorization header and use it in your
// calls to Attraqt APIs until its expiration
```

**Java example**

To work with the Java SDK, it is easier to convert your private key to the PKCS12 format:

```
openssl pkcs12 -export -nocerts -inkey private.pem -out key.p12 -name your-key-alias
```

And you can generate JWTs with [Nimbus OAuth 2.0 SDK](https://bitbucket.org/connect2id/oauth-2.0-sdk-with-openid-connect-extensions/src/master/):

* Apache Maven:

```
<dependency>
  <groupId>com.nimbusds</groupId>
  <artifactId>oauth2-oidc-sdk</artifactId>
  <version>9.2</version>
</dependency>
```

* Gradle:

```
implementation 'com.nimbusds:oauth2-oidc-sdk:9.2'
```

**Sample Code**

```javascript
// replace this by the path of the PKCS12 file containing your private key
File p12File = new File("/path/to/my/pkcs12/key.p12");
// replace this by the passphrase you used when generating your PKCS12 file
char[] p12Passphrase = "yourSecretPassphrase".toCharArray();
// replace this by the key alias you used when generating your PKCS12 file
String keyAlias = "your-key-alias";
// replace this by the client ID of your service account
// (c.f. "Create a Service Account" section)
ClientID serviceAccountClientId = new ClientID("your-client-id");
// replace this by your Attraqt tenant ID
String tenantId = "myTenantId";
URI tokenEnpoint = new URI("https://iam.attraqt.io/auth/realms/master/protocol/"+tenantId+"/token");

KeyStore store = KeyStore.getInstance("PKCS12");
try(InputStream p12stream = new FileInputStream(p12File)){
    store.load(p12stream, p12Passphrase);
}
Key key = store.getKey(keyAlias, p12Passphrase);

HTTPResponse httpResponse = new TokenRequest(
        tokenEnpoint,
        new PrivateKeyJWT(
                serviceAccountClientId,
                tokenEnpoint,
                JWSAlgorithm.RS256,
                (RSAPrivateKey)key,
                null,
                null
        ),
        new ClientCredentialsGrant()
).toHTTPRequest().send();

Object response = OIDCTokenResponseParser.parse(httpResponse);
if(response instanceof OIDCTokenResponse){
    AccessToken accessToken = ((OIDCTokenResponse)response).getOIDCTokens().getAccessToken();
    long expiresIn = accessToken.getLifetime(); // seconds
    String authorizationHeader = accessToken.toAuthorizationHeader();
    // Cache the authorization header and use it in your
    // calls to Attraqt APIs until its expiration
} else {
    String errorDescription = ((TokenErrorResponse)response).getErrorObject().getDescription()
    System.err.println("Error getting access token: " + errorDescription);
}
```

{% endtab %}

{% tab title="Password Authentication" %}
Service accounts with the "Secret" authentication method can authenticate using basic HTTP authentication, using the client ID as username.

**Example using curl:**

```bash
curl -X POST \
 https://iam.attraqt.io/auth/realms/${tenantId}/protocol/openid-connect/token \
  --user "your-client-id:your-client-secret" \
  --data "grant_type=client_credentials"
```

The identity server answers with a JSON document containing the following fields:

* `access_token`: the access that must be used to authenticate API calls.
* `expires_in`: time in seconds before the token expires.
  {% endtab %}
  {% endtabs %}

## Authenticating API calls <a href="#authenticating-api-calls" id="authenticating-api-calls"></a>

Calls to the Attraqt API must be authenticated with an `Authorization` header of type `Bearer` containing the acess token, for example:

```bash
curl -x GET \
  https://items.attraqt.io/catalogs/active?tenant=${tenant}&environment=${environment}
  -H "Authorization: Bearer ${access_token}"
```

You can use the access token as many times as you want before its expiration.

{% hint style="danger" %}
You must reuse access tokens as much as possible because calling the token endpoint is time and resource-consuming.
{% endhint %}

## Access token expiration <a href="#refreshing-rpts" id="refreshing-rpts"></a>

Access tokens expire after a time given in the `expires_in` field of the identity server response. You will then need to get a new one using the same HTTP query.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://crownpeak.gitbook.io/product-discovery/sending-and-managing-product-data/what-is-the-items-api/authorization-to-apis.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
