The privacy permissions flow easily allows users to consent to sharing their 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.
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
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.
To enable the Privacy Permission flow in your dApp, you need to implement the following steps:
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.
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 QuadClientonSign 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.
This works the same way for onboarding signatures to ensure wallet ownership
consthandleSign=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 updateif (account) {constsignature=awaitsignMessageAsync({ 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 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.
// Quadrata Clientimport { QuadAttribute, PrivacyConsentScopeParamKey, PrivacyConsentScopeParams} from'@quadrata/client-react';interfaceAttributeOnboardStatusDto { 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; }; }; };}functiongetAttributesToClaim(onboardStatus:any, isBypassMint:boolean) {constattributesToClaim= [];for (constattributeNamein onboardStatus) {const { status,mintedOnchain } = onboardStatus[attributeName];if ( (status !==AttributeStatus.READY&& status !=='NOT_APPLICABLE') || (!isBypassMint &&!mintedOnchain && status ===AttributeStatus.READY) ) {attributesToClaim.push(attributeName asQuadAttribute); } }return attributesToClaim;}functioncheckConsentNeeded(privacyStatus:any) {if (privacyStatus) {for (constprivacyScopeKeyin privacyStatus) {const { status } = privacyStatus[privacyScopeKey];if (status !=='ALLOWED') {// if any permission is not allowed, all of the desired // permissions need to be requested againreturntrue; } } }returnfalse;}functionparseOnboardStatusResponse( resp:AttributeOnboardStatusDto, isBypassMint:boolean=false) {const { data: { onboardStatus,privacyStatus,offeringStatus } } = resp;constattributesToClaim=getAttributesToClaim(onboardStatus, isBypassMint);constisConsentNeeded=checkConsentNeeded(privacyStatus);if (offeringStatus) {// merge attribute to attest from offeringStatus into attributesToClaimconstattributesToAttest=getAttributesToClaim(offeringStatus,true);for (constnameof attributesToAttest) {if (!attributesToClaim.includes(name)) {attributesToClaim.push(name); } } }return { attributesToClaim, isConsentNeeded };}// Check which attributes to claim for a given walletconstapiAttributesOnboardStatus=async () => {const { NEXT_PUBLIC_QUADRATA_API_URL } =process.env;constattributes= requiredAttributes.map((attr) =>attr.toLowerCase()).join(',');constprivacyScopes=requiredPrivacyScopes.join(','); const url = `${NEXT_PUBLIC_QUADRATA_API_URL}/api/v2/attributes/onboard_status?wallet=${account}&chainId=${chainId}&attributes=${attributes}&privacyScopes=${privacyScopes}`;
constheaders= {'Content-Type':'application/json', Authorization:`Bearer ${accessToken}`, };constresponse=awaitfetch(url, { method:'GET', headers });if (!response.ok) {thrownewError('Onboard status failed'); }return (awaitresponse.json()) asAttributeOnboardStatusDto;};constresponse=awaitapiAttributesOnboardStatus();// isConsentNeeded is a boolean indicating if the consent flow is neededconst { isConsentNeeded } =parseOnboardStatusResponseForClient(resp, isBypassMint);let privacyScopesToRequest = [];if (isConsentNeeded) {// if isConsentNeeded is true, all required privacy scopes should be passed in privacyScopesToRequest = requiredPrivacyScopes;}
See the Full Example for a full integration example
Privacy Permission Integration Example
import { ... PrivacyConsentScopeParamKey, PrivacyConsentScopeParams} from'@quadrata/client-react';// QuadClient configconstquadConfig: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};// Privacy permissions being requested for user consent// Update these to your dApp's requirements// PrivacyConsentScopeParamKey[] is an array of available params that your dApp // is requesting from the userconstprivacyScopes:PrivacyConsentScopeParamKey[] = [PrivacyConsentScopeParams.ADR,PrivacyConsentScopeParams.DOB,PrivacyConsentScopeParams.EM,PrivacyConsentScopeParams.FN,PrivacyConsentScopeParams.LN];// ComponentexportconstMyComponent:React.FC<{ accessToken:string }> = ({ accessToken }) => {// Stateconst [signature,setSignature] =useState<string>();const [signatureConsent,setSignatureConsent] =useState<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();consthandleSign=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 updateif (account) {constsignature=awaitsignMessageAsync({ message });if (isConsent) {// Sets the user consent signaturesetSignatureConsent(signature); } else {// Sets the user wallet signature for normal OnboardingsetSignature(signature); } } };// Requesting user PIIreturn ( <QuadClient ...accessToken={accessToken}account={account}config={quadConfig}onHide={onHide}onSign={handleSign}privacyScopes={privacyScopes ||undefined}signature={siganture}signatureConsent={signatureConsent} > <CustomLoader /> </QuadClient> );};