# External Masking Interface

{% hint style="warning" %}
**Deprecation notice**: Support for this feature has been deprecated.
{% endhint %}

{% hint style="info" %}
**Use Deterministic IVs/Salt**

Use deterministic IVs/salt to ensure the same value is masked consistently throughout the data, as Immuta always pushes down the masked version of the literal when the querying user is exempt from the policy.
{% endhint %}

## Authentication

Immuta can make requests to your External Masking service with one of two authentication methods:

1. **Username and password authentication**: Immuta can send requests with a username and a password in the `Authorization` HTTP header. In this case, your service will need to be able to parse a [Basic Authorization Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization#directives) and validate the credentials sent with it.
2. **PKI Certificate**: Immuta can send requests using a CA certificate, a certificate, and a key.

Alternatively, Immuta can make unauthenticated requests to your REST masking service. This is recommended only if you have other security measures in place (e.g., if the service is in an isolated network that's reachable only by your Immuta environment.)

## Endpoints

### POST /

#### Description

{% hint style="warning" %}
The `unmask` action allows Immuta to build predicates that can be used to query data that is consistently masked at rest in the remote database; it does not dynamically mask data at query time.

To dynamically mask data, use [Immuta’s standard masking policies](https://documentation.immuta.com/2024.3/secure-your-data/authoring-policies-in-secure/reference-guides/data-policies#masking-policies).
{% endhint %}

This endpoint accepts a set of values and a directive to either mask or unmask them.

#### Request Body

Your service will need to parse and process the following body parameters:

| Parameter | Type                      | Description                                                    |
| --------- | ------------------------- | -------------------------------------------------------------- |
| `action`  | Enum(`MASK`, `UNMASK`)    | Either `MASK` or `UNMASK` the values                           |
| `values`  | Map(`string`, `String[]`) | A map of columns containing an array of masked/unmasked values |

Below is an example request payload to **mask** values in the `ssn` and `ccn` columns:

```json
{
  "action": "MASK",
  "values": {
    "ssn": ["123-4567-890", "098-4567-321"],
    "ccn": ["0000-1111-2222-3333", "5555-6666-7777-8888", "9999-0000-1111-2222"]
  }
}
```

Below is an example request payload to **unmask** values in the `ssn` and `ccn` columns:

```json
{
  "action": "UNMASK",
  "values": {
    "ssn": ["AX6YYTURNHtyDkjm", "ue6AUyQYgWiTLAUT"],
    "ccn": ["oDpEKiFutdWG46A2", "Tdz3BEVjjfVLrW6x", "nVANLwkgoLUAe4NQ"]
  }
}
```

#### Response Body

Your service will need to return a map of values that corresponds to the columns and values that were specified in the request. It is important that your service returns the same column keys and that the position of each masked/unmasked value in your response corresponds to the masked/unmasked value from the request.

For example, the following request

```bash
curl --location --request POST 'https://your.external.mask.service/' \
--data-raw '{
  "action": "MASK",
  "values": {
    "ssn": ["123-4567-890", "098-4567-321"],
    "ccn": ["0000-1111-2222-3333", "5555-6666-7777-8888", "9999-0000-1111-2222"]
  }
}'
```

could return the following body:

```json
{
  "ssn": ["AX6YYTURNHtyDkjm", "ue6AUyQYgWiTLAUT"],
  "ccn": ["oDpEKiFutdWG46A2", "Tdz3BEVjjfVLrW6x", "nVANLwkgoLUAe4NQ"]
}
```

Notice that both `ssn` and `ccn` columns are present and that each of them contains the exact number of values specified in the request. Immuta will fail to validate responses to its request under the following circumstances:

1. The response contains column keys that were not present in the request.
2. The response is missing column keys that were present in the request.
3. The response doesn't contain the exact number of values for each of the corresponding column keys in the request.

## Examples

Below are some very simplistic implementation examples of a service with `mask()` and `unmask()` functions:

{% tabs %}
{% tab title="Node.js" %}

```javascript
const mask = (value) => {
  // TODO: implement your custom masking logic and return a masked value
};
const unmask = (value) => {
  // TODO: implement your custom unmasking logic and return an unmasked value
};
// Use as a POST handler:
const externalMaskingHandler = async (request, response) => {
  const { action, values } = request.payload;
  const result = {};
  Object.keys(values).forEach(column => {
      result[column] = values[column].map(action === 'MASK' ? mask : unmask);
  });
  return result;
};
```

{% endtab %}

{% tab title="Python/Flask" %}

````python
from flask import Flask, request

app = Flask()

def mask(value):
    # TODO: implement your custom masking logic and return a masked value
    pass

def unmask(value):
    # TODO: implement your custom unmasking logic and return an unmasked value
    pass

@app.route("/path/to/my/services", methods=["POST"])
def external_masking_handler():
    payload = request.get_json()
    action = payload.get("action")
    values = payload.get("values")
    result = {}
    for k, v in values:
        result[k] = [mask(value) if action == "MASK" else unmask(value) for value in v]
    return result

</div>

</div>

    ```
````

{% endtab %}
{% endtabs %}
