5. Privacy Data Permissions
Configure the onboarding flow to ask for permissions to share privacy data with your dApp.
Last updated
import {
...
PrivacyConsentScopeParamKey,
PrivacyConsentScopeParams
} from '@quadrata/client-react';const privacyScopes: PrivacyConsentScopeParamKey[] = [
PrivacyConsentScopeParams.ADR,
PrivacyConsentScopeParams.DOB,
PrivacyConsentScopeParams.EM,
PrivacyConsentScopeParams.FN,
PrivacyConsentScopeParams.LN
];const [signature, setSignature] = useState<string>();
const [signatureConsent, setSignatureConsent] = useState<string>();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);
}
}
};// 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;
}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
};
// 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 user
const privacyScopes: PrivacyConsentScopeParamKey[] = [
PrivacyConsentScopeParams.ADR,
PrivacyConsentScopeParams.DOB,
PrivacyConsentScopeParams.EM,
PrivacyConsentScopeParams.FN,
PrivacyConsentScopeParams.LN
];
// Component
export const MyComponent: React.FC<{ accessToken: string }> = ({ accessToken }) => {
// State
const [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();
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 (
<QuadClient
...
accessToken={accessToken}
account={account}
config={quadConfig}
onHide={onHide}
onSign={handleSign}
privacyScopes={privacyScopes || undefined}
signature={siganture}
signatureConsent={signatureConsent}
>
<CustomLoader />
</QuadClient>
);
};