# Off-Chain Access Control

To restrict access to specific features or functionalities on your website based on the KYC/AML status of the user, you need to implement off-chain access control.

<figure><img src="/files/lsZ0ONGAhVXC90qbLCpl" alt=""><figcaption><p>User verification flow</p></figcaption></figure>

## Steps

1. Generate an API key/secret pair from the [Authento Dashboard](https://dashboard.authento.io) under the *Settings* tab
2. \[Backend] Configure the server to request and handle KYC/AML data from Authento.
3. \[Frontend] Implement access control based on the status response returned from your backend.
4. \[Backend] (Optional) Set up handlers for verification webhooks.

## Backend

You have to set up a server which requests and handles KYC/AML data from Authento. The exact implementation depends on your use case. For example, you could configure your server to

* Request KYC/AML status by wallet address upon user sign-in.
* Confirm KYC/AML status when a user makes a withdrawal request.

Once the KYC/AML status is retrieved, it should be processed on your backend to derive the permissions which should be granted to the user.

{% hint style="danger" %}
The API response from Authento contains sensitive information and should NOT be directly relayed to the end user.

Instead, the API response should be used to derive user permissions server-side, and only such permissions should be sent back to the client.
{% endhint %}

For details on how KYC/AML status can be retrieved using the Authento API, please refer to the [endpoints](/authento-api/api-reference/endpoints.md) section.

#### Backend examples

{% tabs %}
{% tab title="Node.js" %}
{% code lineNumbers="true" %}

```javascript
const crypto = require("crypto");
const express = require("express");

const app = express();

// TODO: Configure your Authento settings
const apiKey = "YOUR_API_KEY";
const apiSecret = "YOUR_API_SECRET";
const basicUserInfoEndpoint = "https://api.authento.io/userinfo/basic";

/**
 * Returns the KYC status of an address retrieved using Authento API
 * @api {get} /userinfo/basic?address=:address
 * @apiParam {String} address EVM compatible address
 * @apiSuccess 
 *   {String="unverified","processing","info_required",verified","rejected"}
 *   status KYC status of the address
 * @apiError {String} detail Error detail
 */
app.get("/userinfo/basic", async (req, res) => {
  try {
    const address = req.query.address;

    if (!address) {
      throw new Error("Invalid address");
    }

    const url = `${basicUserInfoEndpoint}?address=${address}`;
    const timestamp = Date.now();
    const signature = crypto
      .createHmac("sha256", apiSecret)
      .update(`${timestamp}GET${url}`)
      .digest("hex");

    const { status, type } = await fetch(url, {
      headers: {
        "X-AUTHENTO-APIKEY": apiKey,
        "X-AUTHENTO-SIGNATURE": signature,
        "X-AUTHENTO-TS": timestamp,
      },
    })
      .then((response) => response.json())
      .then((data) => data.result);

    // TODO: Implement additional filters based on user attributes such
    //  as nationality/age/pep/riskScore as needed.
    //  Fields other than 'status' should NOT be included
    //  in the response as they might contain sensitive information.
    res.json({ status, type });
  } catch {
    res.status(400).json({ detail: "Failed to retrieve basic user info" });
  }
});

app.listen(3000, () => {
  console.log("Server listening on port 3000");
});
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import time
import hmac
import requests
from fastapi import FastAPI, HTTPException

app = FastAPI()

# TODO: Configure your Authento settings
API_KEY = "YOUR_API_KEY"
API_SECRET = "YOUR_API_SECRET"
ENDPOINT = "https://api.authento.io/userinfo/basic"


@app.get("/userinfo/basic")
async def get_basic_userinfo(address: str):
    """
    Returns the KYC status of an address retrieved using Authento API
    @api {get} /userinfo/basic?address=:address
    @apiParam {String} address EVM compatible address
    @apiSuccess 
        {String="unverified","processing","info_required",verified","rejected"}
        status KYC status of the address
    @apiError {String} detail Error detail
    """
    try:
        timestamp = int(time.time() * 1000)
        payload = f'{timestamp}GET{ENDPOINT}?address={address}'.encode()
        signature = hmac.new(API_SECRET.encode(), payload,
                             'sha256').hexdigest()
        headers = {
            "X-AUTHENTO-APIKEY": API_KEY,
            "X-AUTHENTO-SIGNATURE": signature,
            "X-AUTHENTO-TS": str(timestamp),
        }
        params = {
            "address": address
        }
        response = requests.get(ENDPOINT, headers=headers, params=params)
        user_data = response.json()
        # TODO: Implement additional filters based on user attributes such as
        #  nationality/age/pep/riskScore as needed.
        #  Fields other than 'status' should NOT be included
        #  in the response as they might contain sensitive information.
        return {
            "status": user_data['result']['status'],
            "type": user_data['result']['type']
        }
    except:
        raise HTTPException(
            status_code=400, detail="Failed to retrieve basic user info")
```

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

## Frontend

Aside from restricting user access based on KYC status returned from your backend, your frontend should be set up to initialize Authento KYC on users' demand. You can do this using the authento-react package, or you can do this manually.

### Authento-react (recommended)

This package contains custom react hooks which makes it very simple to integrate Authento into your platform. For detailed instructions or usage examples, please refer to [this section](/authento-api/authento-react/getting-started.md).

### Manual integration

Take the following steps to initialize the Authento verification process manually:

1. Create a signature for the following typed structured data:

   ```javascript
   const domain = { name: YOUR_DOMAIN_NAME };
   const types = {
     Message: [
       { name: "content", type: "string" },
       { name: "for", type: "string" },
       { name: "timestamp", type: "string" },
       { name: "nonce", type: "string" },
     ],
   };
   const primaryType = "Message";
   const message = {
     content:
       "Sign this message to prove your ownership of this address " +
       "and proceed with the identity verification process.",
     for: domainName,
     timestamp: CURRENT_TIMESTAMP,
     nonce: NONCE,
   };
   ```

   * `YOUR_DOMAIN_NAME`: Your domain name registered with Authento. Please contact us if you are unsure of its value.
   * `CURRENT_TIMESTAMP`:  The number of milliseconds elapsed since the Unix epoch as a string.
   * `NONCE`: Randomly generated string as a safeguard against replay attacks. You can use uuid or other packages to generate this.<br>

   On the [Wagmi](https://wagmi.sh/core/actions/signTypedData) library, the signature can be generated as follows:

   ```
   const signature = await signTypedData({
     domain,
     message,
     primaryType,
     types,
   })
   ```

{% hint style="info" %}
Remember to refresh `CURRENT_TIMESTAMP` and `NONCE` just before the signature is generated.
{% endhint %}

2. Upon successful generation of the signature, direct the user to\
   \
   `https://www.authento.io/verify/${YOUR_DOMAIN_NAME}`\
   \
   using a modal or a popup with the following query parameters:
   * `address`: The EVM address to verify
   * `ts`: CURRENT\_TIMESTAMP as defined above
   * `nonce`: NONCE as defined above
   * `signature`: The signature generated
   * `levelType`: "BASIC" or "POA"

{% hint style="success" %}
The signature generated will be verified on Authento server to ensure the user's ownership of the address. There's no need to worry about malicious users attempting to register addresses for which they do not own the private key
{% endhint %}

2. Users will then proceed with the verification process on the newly opened modal or popup.

## Webhooks Handling

In address-based verification, you are not assumed to be managing your own user database. In case you do, however, you can ensure that your user data is up to date by setting up an endpoint to handle and process webhooks data. For more information on webhooks, please refer to[ this section](/authento-api/integration-guidance/webhooks.md).

## Examples

* [Node.js backend](https://github.com/Authento/usage-examples/tree/main/address-based/backend/nodejs)
* [Python.js backend](https://github.com/Authento/usage-examples/tree/main/address-based/backend/python)
* [React/Next.js frontend + backend](https://github.com/Authento/usage-examples/tree/main/address-based/off-chain-access-control)


---

# 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://authento.gitbook.io/authento-api/integration-guidance/address-based-verification/off-chain-access-control.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.
