# Integrate your external CRM system with the B2COPY API

This guide explains how to integrate your external CRM system with B2COPY to authorize in B2COPY, obtain an access token, and use it to work with the B2COPY API.

## Prerequisites

* Create a **service admin** in the Admin panel, on whose behalf your external CRM system will make API requests to B2COPY.

{% hint style="info" %}
It's recommended to explicitly specify the list of IP addresses from which the external CRM system will send requests for this service admin.
{% endhint %}

* Configure your external CRM system to generate signed JWT tokens. Each token must include the following claims:
  * `admin_id` — the identifier of the created service admin for whom the JWT token is issued.
  * `device_ip` — the IP address from which the request is made.
  * `key_id` — the identifier of the private key.

## Generate keys

Run the following OpenSSL commands to generate an EC (Elliptic Curve) private and public key pair for signing and verifying JWT tokens:

```bash
# Generate private key
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 \
    -out ec_private_pkcs8.pem

# Generate public key
openssl pkey -in ec_private_pkcs8.pem \
    -pubout -out ec_public.pem
```

{% hint style="success" %}

### Security note

* Keep your **private key** secure and never share it.
* Provide the **public key** to the B2COPY support team. It will be used to verify signed JWT tokens.
  {% endhint %}

## Contact B2COPY support

To complete the integration, provide the following to the B2COPY support team:

* Your **public key** (ec\_public.pem).
* The unique key identifier.
* The IP addresses from which requests to the B2COPY API will be made.

The B2COPY support team will register your integration, configure your public key in the system, and provide further instructions to enable API authorization.

## Generate JWT tokens

Use the following Java code to generate and sign JWT tokens with your **private key**. The token includes the `admin_id` and `device_ip` claims and is signed using the ES256 algorithm.

```java
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.SignatureAlgorithm;
import java.security.Key;
import java.time.Instant;
import java.util.Date;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
public class JwtIssuer {
    
    /**
     * Load EC private key from PKCS8 PEM file
     */
    static PrivateKey loadEcPrivatePkcs8(String pemPath) 
            throws Exception {
        String pem = Files.readString(Path.of(pemPath));
        String base64 = pem
            .replaceAll("-----\\w+ PRIVATE KEY-----", "")
            .replaceAll("\\s", "");
        
        byte[] der = Base64.getDecoder().decode(base64);
        
        return KeyFactory
            .getInstance("EC")
            .generatePrivate(new PKCS8EncodedKeySpec(der));
    }
    /**
     * Issue JWT token for external system authentication
     * 
     * @param adminId   Administrator ID for token
     * @param deviceIp  IP address making the call
     * @param privPemPath Path to private key file
     * @return Signed JWT token
     */
    public static String issue(
            long adminId,
            String deviceIp, 
            String privPemPath) throws Exception {
        
        var priv = loadEcPrivatePkcs8(privPemPath);
        
        return Jwts.builder()
            .setIssuer("your-service")
            .setIssuedAt(Date.from(Instant.now()))
            .setExpiration(Date.from(Instant.now().plusSeconds(1800)))
            .claim("admin_id", adminId)
            .claim("device_ip", deviceIp)
            .claim("key_id", deviceIp)
            .signWith(priv, SignatureAlgorithm.ES256)
            .compact();
    }
    public static void main(String[] args) throws Exception {
        String token = issue(
            "42", 
            "203.0.113.10", 
            "/path/to/ec_private_pkcs8.pem"
        );
        System.out.println(token);
    }
}
```

## Obtain B2COPY access and refresh tokens

To obtain a pair of access and refresh tokens from B2COPY, use the following method:

<mark style="color:blue;">`POST`</mark> `[host]/api/admin/auth/v2/admin/adminservice/authexternalcrm`

### Request

**Body parameters**

<table><thead><tr><th>Name</th><th width="81">Type</th><th width="105">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>external_token</code></td><td>string</td><td>Yes</td><td>The generated JWT token.</td></tr></tbody></table>

**Request example**

```sh
curl -X POST "https://demo-standalone-v2.prod.b2copy.tech/api/admin/auth/v2/admin/adminservice/authexternalcrm" \
     -d '{
           "external_token": "<your-generated-jwt-token>"
         }'

```

### Response

The response returns access and refresh tokens for the service admin specified in the token claims. All other API requests to B2COPY must include the obtained access token in the `Authorization` header.

**Response example**

```json
{
  "accessToken": "<access-token>",
  "refreshToken": "<refresh-token>"
}
```

## Refresh the access token

To refresh the expired access token, use the following method:

<mark style="color:blue;">`POST`</mark> `[host]/api/admin/auth/v2/admin/adminservice/refreshtoken`

### Request

**Request example**

```sh
curl -X POST "https://demo-standalone-v2.prod.b2copy.tech/api/admin/auth/v2/admin/adminservice/refreshtoken" \
     -H "Authorization: Bearer <refreshToken>"
```

### Response

The response returns a new pair of access and refresh tokens.
