Presentation
This page shows you how to present credentials with React and the Apollo Client. For detailed info on credential presentation, refer to the Presentation guide.
Authentication
All applications that request presentations must authenticate users and provide an identity reference to obtain an presentation access token. Refer to the Authorization guide for further info. The React examples below run within an authenticated application.
Presentation steps
Presentation is a multi-step process described in the Presentation guide.
1. Acquire an access token
Obtain a limited access token for presentations from your secure backend API. Refer to the Limited access tokens guide for more info.
const { loading, error, accessToken } = useVerifiedOrchestrationSessionToken(getLimitedAccessToken, 'presentation')
return error ? (
<Alert variant="error">{error}</Alert>
) : (
<Button loading={loading} onClick={() => createPresentationRequest()}>
Present
</Button>
)
2. Create a presentation request
Create a presentation request specifying the requested credentials and optionally:
- whether revoked credentials can be presented
- callback info, if presentation event data should also be sent to a server-side endpoint
The CreatePresentationRequest mutation returns the presentation request details, including a URL to open in the browser or a QR code to scan.
const presentationRequest = graphql(`
mutation CreatePresentationRequest($request: PresentationRequestInput!) {
createPresentationRequest(request: $request) {
... on PresentationResponse {
requestId
url
qrCode
expiry
}
... on RequestErrorResponse {
error {
code
message
innererror {
code
message
target
}
}
}
}
}
`)
const credentialTypes = ['VerifiedContractor', 'TrueIdentity']
const allowRevokedConfiguration = {
configuration: {
validation: {
allowRevoked: true,
},
},
}
const [createPresentationRequest, { data, loading, reset }] = useMutation(presentationRequest, {
variables: {
request: {
includeQRCode: true,
requestedCredentials: credentialTypes.map((credentialType) => ({ type: credentialType, ...allowRevokedConfiguration })),
registration: { clientName: 'Example client', purpose: 'Present credentials for presentation example' },
},
},
})
3. Display QR code or open link
If the user is on a mobile device, open the presentation link in the browser. Otherwise, display the QR code for the user to scan.
const [createPresentationRequest, { data, loading, reset }] = useMutation(presentationRequest, {
variables: {
request: {
includeQRCode: true,
requestedCredentials: credentialTypes.map((credentialType) => ({ type: credentialType, ...allowRevokedConfiguration })),
registration: { clientName: 'Example client', purpose: 'Present credentials for presentation example' },
},
},
onCompleted: (data) => {
if (!data) return
if ('error' in data.createPresentationRequest) {
setError(data.createPresentationRequest)
return
}
const isMobile = /Android|iPhone/i.test(navigator.userAgent)
if (isMobile) window.location.href = data.createPresentationRequest.url
else setQrCodeOpen(true)
},
onError: setError,
})
return (
<>
<Button loading={loading} onClick={() => createPresentationRequest()}>
Issue
</Button>
<QrCodeModal open={qrCodeOpen} onClose={reset} qrCode={data.createIssuanceResponse.qrCode} />
</>
)
4. Handle presentation events
The PresentationEvent subscription returns event data during the presentation process. The requestStatus
field can be used to determine the current state of the presentation:
request_retrieved
: the credential holder has opened the presentation linkpresentation_verified
: the credential holder has presented the requested credentials
const presentationEventSubscription = graphql(`
subscription PresentationEvent($requestId: ID!) {
presentationEvent(where: { requestId: $requestId }) {
event {
requestStatus
}
presentation {
id
presentedAt
identity {
id
identifier
issuer
name
}
presentedCredentials {
issuer
type
claims
credentialState
domainValidation
}
issuances {
id
status
issuedAt
expiresAt
revokedAt
contract {
id
name
description
credentialTypes
display {
card {
title
issuedBy
backgroundColor
textColor
description
logo {
uri
description
}
}
}
}
}
partners {
id
did
name
credentialTypes
linkedDomainUrls
}
}
}
}
`)
useSubscription(presentationEventSubscription, {
variables: { requestId: data.createPresentationResponse?.requestId ?? '' },
skip: !data.createPresentationResponse,
onError: setError,
onData: ({ data: { data } }) => {
if (!data?.presentationEvent) return
const { requestStatus } = data.presentationEvent.event
if (requestStatus === PresentationRequestStatus.RequestRetrieved) handlePresentationRetrieved()
else if (requestStatus === PresentationRequestStatus.PresentationVerified) handlePresentation((data.presentationEvent.presentation)
},
})
// example of consuming presentation data
// map presented credentials to the corresponding issuance or partner by credential type
const handlePresentation = (presentation: Presentation) => {
const { presentedCredentials, identity, partners } = presentation
const credentialsWithIssuanceOrPartner = presentedCredentials.map((presentedCredential) => ({
presentedCredential,
issuance: presentation.issuances.find((issuance) => issuance.contract.credentialTypes.includes(presentedCredential.type[1])),
partner: partners.find((partner) => partner.credentialTypes.includes(presentedCredential.type[1])),
}))
reset()
setPresentationData({ identity, credentialsWithIssuanceOrPartner })
}