---
description: >-
  Configure the onboarding flow to ask for permissions to share privacy data
  with your dApp.
---

# 5. Privacy Data Permissions

<figure><img src="https://1584030040-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLAjafMHYws8zl3GIV6qF%2Fuploads%2FSx0CuDgfUMgZGdCTWdJv%2Fimage.png?alt=media&#x26;token=5fe9969d-cfc5-47b8-ad13-2faf2778ffc4" alt=""><figcaption></figcaption></figure>

The privacy permissions flow easily allows users to consent to sharing their [privacy data](../../request-privacy-data/list-of-privacy-data) with your dApp in a few simple, secure steps.

When a user clicks to "Allow" Privacy Permissions, a request for a signature is presented, which the user must sign with their connected wallet in order to authorize consent for Privacy Data sharing.

<figure><img src="https://1584030040-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLAjafMHYws8zl3GIV6qF%2Fuploads%2FYnvOnb57WIornmIR51h1%2Fimage.png?alt=media&#x26;token=57dae1e4-7c2c-4a77-9e5d-1c06d64ab57a" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
In order to use this feature, application owners must have a working implementation of the Quadrata Client. \
\
A full example and integration steps can be found here: [6.-full-example](6.-full-example "mention")
{% endhint %}

{% hint style="info" %}
All of our UI libraries support both **Javascript** and **Typescript** environments.

Types, interfaces, helper functions, and object maps can be found in and imported from the `@quadrata/core-react` and `@quadrata/client-react` NPM packages.
{% endhint %}



To enable the Privacy Permission flow in your dApp, you need to implement the following steps:

1. [Import Privacy Libs](#1.-import-privacy-consent-libs)
2. [Set the Privacy Permissions requested](#2.-set-the-privacy-permissions-requested)
3. [Handle Privacy Consent Signatures With State](#3.-handle-privacy-consent-signatures-with-state)
4. [Determine If Privacy Permissions Flow Is Needed ](#4.-determine-if-privacy-permissions-flow-is-needed)

### 1. Import Privacy Consent Libs

```typescript
import {
    ...
    PrivacyConsentScopeParamKey,
    PrivacyConsentScopeParams
} from '@quadrata/client-react';
```



### 2. Set the Privacy Permissions requested

Set the `privacyScopes` that will be requested from the user for consent. These need to be passed into `QuadClient` as a component prop.

```typescript
const privacyScopes: PrivacyConsentScopeParamKey[] = [
    PrivacyConsentScopeParams.ADR,
    PrivacyConsentScopeParams.DOB,
    PrivacyConsentScopeParams.EM,
    PrivacyConsentScopeParams.FN,
    PrivacyConsentScopeParams.LN
];
```

{% hint style="info" %}
Adding this property to your component will enable the Privacy Permission flow and will prompt the onboarding individual for consent to share their Privacy Data.
{% endhint %}

{% hint style="success" %}
You can find a full list of Privacy Permission Parameters that you can add to your `privacyScopes` on the "[Request Privacy Data](../../request-privacy-data)"."[List of Privacy Data](../../request-privacy-data/list-of-privacy-data)" page.
{% endhint %}



### 3. Handle Privacy Consent Signatures With State

Add the `state` related code to your dApp to manage the privacy consent signature along with the normal onboarding signature.

```typescript
const [signature, setSignature] = useState<string>();
const [signatureConsent, setSignatureConsent] = useState<string>();
```

Update the `handleSign` method to manage the user consent signature that will be available once the user signs, granting access to their Privacy Data.

A boolean flag is passed as the second attribute to the `QuadClient` `onSign` event handler. This boolean represents whether or not the signature is for privacy consent or not.

When your dApp receives the consent signature, it needs to be passed back in as a component prop to `QuadClient`. To do this, update the state so the component re-renders.

{% hint style="info" %}
This works the same way for onboarding signatures to ensure wallet ownership
{% endhint %}

```typescript
const handleSign = async (message: string, isConsent: boolean) => {
    // User clicked the sign button
    // Signing the message and updating state.
    // Will automatically navigate to the next step upon signature update
    if (account) {
        const signature = await signMessageAsync({ message });
        if (isConsent) {
            setSignatureConsent(signature);
        } else {
            setSignature(signature);
        }
    }
};
```

##

### **4. Determine If Privacy Permissions Flow Is Needed**

If a user has already allowed access to the Privacy Data that you are requesting, you may not want to put them into the onboarding flow a second time (unless there are unclaimed attributes).

To help you make this decision, the [API Onboard Status](3.-api-onboard-status) endpoint has been modified to accept optional `privacyScopes` as a query parameter. If provided, the response payload will contain which permissions have already been allowed, and which permissions you need to ask the user for.

```typescript
// Quadrata Client
import {
    QuadAttribute,
    PrivacyConsentScopeParamKey,
    PrivacyConsentScopeParams
} from '@quadrata/client-react';

interface AttributeOnboardStatusDto {
    data: {
        type: 'attributes';
        onboardStatus:{
            [attributeName: string]: {
                status: string;
                onboardedAt?: number;
                mintedOnchain?: boolean;
            };
        };
        offeringStatus?: {
            [attributeName: string]: {
                status: string;
                verifiedAt?: number;
            };
        };
        privacyStatus?: {
            [privacyPermission: string]: {
                status: string;
                allowedAt?: number;
                revokedAt?: number;
                revokedReason?: string;
            };
        };
    };
}

function getAttributesToClaim(onboardStatus: any, isBypassMint: boolean) {
    const attributesToClaim = [];
    for (const attributeName in onboardStatus) {
        const { status, mintedOnchain } = onboardStatus[attributeName];
        if (
            (status !== AttributeStatus.READY && status !== 'NOT_APPLICABLE') ||
            (!isBypassMint && !mintedOnchain && status === AttributeStatus.READY)
        ) {
            attributesToClaim.push(attributeName as QuadAttribute);
        }
    }
    return attributesToClaim;
}

function checkConsentNeeded(privacyStatus: any) {
    if (privacyStatus) {
        for (const privacyScopeKey in privacyStatus) {
            const { status } = privacyStatus[privacyScopeKey];
            if (status !== 'ALLOWED') {
                // if any permission is not allowed, all of the desired 
                // permissions need to be requested again
                return true;
            }
        }
    }
    return false;
}

function parseOnboardStatusResponse(
    resp: AttributeOnboardStatusDto,
    isBypassMint: boolean = false
) {
    const { data: { onboardStatus, privacyStatus, offeringStatus } } = resp;

    const attributesToClaim = getAttributesToClaim(onboardStatus, isBypassMint);
    const isConsentNeeded = checkConsentNeeded(privacyStatus);

    if (offeringStatus) {
        // merge attribute to attest from offeringStatus into attributesToClaim
        const attributesToAttest = getAttributesToClaim(offeringStatus, true);
        for (const name of attributesToAttest) {
            if (!attributesToClaim.includes(name)) {
                attributesToClaim.push(name);
            }
        }
    }

    return { attributesToClaim, isConsentNeeded };
}

// Check which attributes to claim for a given wallet
const apiAttributesOnboardStatus = async () => {
    const { NEXT_PUBLIC_QUADRATA_API_URL } = process.env;
    const attributes = requiredAttributes
        .map((attr) => attr.toLowerCase())
        .join(',');
    const privacyScopes = requiredPrivacyScopes.join(',');
    const url = `${NEXT_PUBLIC_QUADRATA_API_URL}/api/v2/attributes/onboard_status?wallet=${account}&chainId=${chainId}&attributes=${attributes}&privacyScopes=${privacyScopes}`;
    const headers = {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${accessToken}`,
    };
    const response = await fetch(url, { method: 'GET', headers });
    if (!response.ok) {
        throw new Error('Onboard status failed');
    }
    return (await response.json()) as AttributeOnboardStatusDto;
};

const response = await apiAttributesOnboardStatus();

// isConsentNeeded is a boolean indicating if the consent flow is needed
const { isConsentNeeded } = parseOnboardStatusResponseForClient(resp, isBypassMint);

let privacyScopesToRequest = [];
if (isConsentNeeded) {
    // if isConsentNeeded is true, all required privacy scopes should be passed in
    privacyScopesToRequest = requiredPrivacyScopes;
}
```

{% hint style="info" %}
See the [Full Example](6.-full-example) for a full integration example
{% endhint %}

## **Privacy Permission Integration Example**

<pre class="language-tsx"><code class="lang-tsx">import {
    ...
    PrivacyConsentScopeParamKey,
    PrivacyConsentScopeParams
} from '@quadrata/client-react';

// QuadClient config
const quadConfig: QuadClientConfig = {
    _debug: true, // Set to 'false' for production environment
    apiUrl: process.env.NEXT_PUBLIC_QUADRATA_API_URL!,
    environment: QuadClientEnvironment.SANDBOX, // set to QuadClientEnvironment.PRODUCTION for production environment
    protocolName: 'NewCo', // Replace with your company name
};

<strong>// Privacy permissions being requested for user consent
</strong>// Update these to your dApp's requirements
// PrivacyConsentScopeParamKey[] is an array of available params that your dApp 
// is requesting from the user
const privacyScopes: PrivacyConsentScopeParamKey[] = [
    PrivacyConsentScopeParams.ADR,
    PrivacyConsentScopeParams.DOB,
    PrivacyConsentScopeParams.EM,
    PrivacyConsentScopeParams.FN,
    PrivacyConsentScopeParams.LN
];

// Component
export const MyComponent: React.FC&#x3C;{ accessToken: string }> = ({ accessToken }) => {
    // State
    const [signature, setSignature] = useState&#x3C;string>();
    const [signatureConsent, setSignatureConsent] = useState&#x3C;string>();
    
    // Hooks
    // In this example we use rainbowkit and wagmi libraries to manage Web3 
    // connectivity. You might use any other library.
    const { address: account, isDisconnected } = useAccount();
      
    const handleSign = async (message: string, isConsent: boolean) => {
        // User clicked the sign or allow button
        // Signing the message and updating state.
        // Will automatically navigate to the next step upon signature update
        if (account) {
            const signature = await signMessageAsync({ message });
            if (isConsent) {
                // Sets the user consent signature
                setSignatureConsent(signature);
            } else {
                // Sets the user wallet signature for normal Onboarding
                setSignature(signature);
            }
        }
    };
    
    // Requesting user PII
    return (
        &#x3C;QuadClient
            ...
            accessToken={accessToken}
            account={account}
            config={quadConfig}
            onHide={onHide}
            onSign={handleSign}
            privacyScopes={privacyScopes || undefined}
            signature={siganture}
            signatureConsent={signatureConsent}
        >
            &#x3C;CustomLoader />
        &#x3C;/QuadClient>
    );
};
</code></pre>
