Skip to main content

Wallet link handling

Credential issuance and presentation requests must be opened in a wallet, such as the Microsoft Authenticator app. This guide explains how to handle opening wallet links from your application.

Wallet links use the URL scheme of openid-vc://. For the links to be opened by the Microsoft Authenticator app on the mobile device, the URL scheme must be assigned to the Microsoft Authenticator app by the device operating system.

If another wallet application is installed after installation of the Microsoft Authenticator app, the URL scheme will likely be assigned to the new wallet application, which may cause issuances and presentations to fail. For example, a credential that requires a Face Check will not work with other wallet apps.

The only way to fix this, presently, is to uninstall and reinstall Microsoft Authenticator, which restores the association of the URL scheme with the Microsoft Authenticator app.

In the future, Verified Orchestration hopes mobile operating systems provide a way to choose which wallet application opens the openid-vc:// scheme URLs.

note

We are currently working with Microsoft to provide a more robust solution for this issue.

Issuance and presentation links work the same way, so for the purposes of this guide, we will refer to both as "wallet links".

Issuance and presentation request responses can include a qrCode fields in addition to the url field. The QR code is a scannable representation of the URL that can be opened via a mobile device's QR code scanner feature, or directly by the using the scanner within the Microsoft Authenticator app's "Verified IDs" tab.

At the time of writing, wallet links expire after 5 minutes (specified by the expiry field). They are not intended to be stored for use later, as opening an expired wallet link will result in an error.

Essential user-interface steps

Once the issuance or presentation request is created, the first step will depend on the device type.

  1. On the desktop, display the QR code for the recipient to scan with their mobile device to launch into the mobile flow.
  2. On a mobile device, open the wallet link directly.
  3. Regardless of the entry point, the wallet expiry should be handled by the application:
    • Display a message to the user that the wallet link has expired.
    • Provide a way to retry the issuance or presentation.
  4. Subscribe to the issuance or presentation events (or alternatively poll for completion).
  5. Handle the issuance or presentation events (or completion):
    • Issuance or presentation link is opened by the wallet, such as the Microsoft Authenticator app.
    • Issuance or presentation completes successfully.
    • Issuance or presentation fails to complete successfully.

Other user-interface steps

Issuance may require additional steps, such as:

  • Photo capture prior to issuance where the credential requires a photo for use with Face Check.
  • PIN code entry between the issuance link being opened and the credential being added to the wallet.

Sample code

The following React code snippets demonstrate how to handle wallet links your apps:

  • Display a QR code for desktop devices.
  • Open the link on mobile devices.
  • Link to the Microsoft Authenticator web page on desktop, the App Store or Google Play Store on mobile.
  • Display and handle expiry of the wallet link.
  • Provide a way to retry the link if it expires.
Sample component to handle wallet links for desktop and mobile devices
export function AuthenticatorResponse({ url, qrCode, expiry, __typename }) {
const isMobile = /Android|iPhone/i.test(navigator.userAgent)
const expiryMinutes = Math.floor((expiry - Date.now() / 1000) / 60)
const type = __typename === 'IssuanceResponse' ? 'issuance' : 'presentation'

const [isExpired, setIsExpired] = useState(false)
useEffect(() => {
if (!expiry) return
const timer = setTimeout(() => setIsExpired(true), new Date(expiry * 1000).getTime() - Date.now())
return () => clearTimeout(timer)
}, [expiry])

return isExpired ? (
<Alert>
The issuance request has expired; <Button onClick={() => window.location.reload()}>Retry</Button>
</Alert>
) : isMobile ? (
<MobileAuthenticatorUrl authenticatorUrl={url} expiryMinutes={expiryMinutes} type={type} />
) : (
<DesktopAuthenticatorQrCode qrCode={qrCode ?? undefined} expiryMinutes={expiryMinutes} type={type} />
)
}
Sample component to open the wallet link on mobile devices
const appleAppStoreLink = 'https://apps.apple.com/app/microsoft-authenticator/id983156458'
const googlePlayStoreLink = 'https://play.google.com/store/apps/details?id=com.azure.authenticator'

export function MobileAuthenticatorUrl({ authenticatorUrl, expiryMinutes, type }) {
const isAndroid = /Android/.test(navigator.userAgent)
const storeName = isAndroid ? 'Google Play' : 'App Store'
const storeLink = isAndroid ? googlePlayStoreLink : appleAppStoreLink

const openAuthenticator = useCallback(() => {
if (authenticatorUrl) window.location.href = authenticatorUrl
}, [authenticatorUrl])

useEffect(() => {
openAuthenticator()
}, [openAuthenticator])

return (
<>
<Button onClick={openAuthenticator}>{type === 'issuance' ? 'Open Credential' : 'Present Credentials'}</Button>
<Info>
<>
The {type === 'issuance' ? 'credential' : 'presentation'} must be opened using{' '}
<Link href={storeLink} target="_blank">
Microsoft Authenticator
</Link>
{expiryMinutes && `. The link will expire in ${expiryMinutes} minutes.`}
</>
</Info>
</>
)
}
Sample component to display the QR code for desktop devices
export function DesktopAuthenticatorQrCode({ qrCode, expiryMinutes, type }: DesktopAuthenticatorQrCodeProps) {
return (
<>
<img alt={`${type === 'issuance' ? 'Issuance' : 'Presentation'} QR Code`} src={qrCode} />
<Info>
Scan this QR code on your phone to {type === 'issuance' ? 'open the credential' : 'present credentials'} in{' '}
<Link href="https://www.microsoft.com/en-au/security/mobile-authenticator-app" target="_blank">
Microsoft Authenticator
</Link>
{expiryMinutes && `. The QR code will expire in ${expiryMinutes} minutes.`}
</Info>
</>
)
}