Gå til hovedindhold
Custom Integrations

Popup Flow

Keep the current page visible by loading the raw verification flow in a popup window.

Popup Flow

Popup flow is the manual equivalent of startVerificationWithPopup().

It uses the same /api/v1/oidc/create endpoint as redirect flow, but loads the returned URL inside a popup window and sends the result back with postMessage.

For the exact endpoint tables, callback parameters, and popup message payloads, see API Reference.

Flow overview

  1. Your page pre-opens a popup window from a click handler.
  2. Your page requests a verification redirect URL.
  3. The popup navigates to the returned URL.
  4. The user completes MitID verification.
  5. Your popup callback page verifies the JWT.
  6. The popup sends the result back to the opener window.
  7. The opener updates the UI and continues checkout.

1. Pre-open the popup

Pre-open the popup inside the user action. This reduces the chance that the browser blocks it.

import { createVerificationRedirect } from './unqverify-api'

const publicKey = 'pk_test_your_key'
const ageToVerify = 18
const redirectUri = 'https://shop.example.com/verify-popup'
const targetOrigin = 'https://shop.example.com'

async function startPopupVerification(): Promise<void> {
  const popup = window.open('', 'unqverify-popup', 'width=500,height=650')

  if (!popup) {
    throw new Error('Popup blocked')
  }

  const redirectUrl = await createVerificationRedirect({
    publicKey,
    ageToVerify,
    redirectUri,
  })

  popup.location.href = redirectUrl
}

2. Listen for the popup result on the opener page

This message shape mirrors the SDK's popup contract.

function handlePopupMessage(event: MessageEvent): void {
  if (event.origin !== 'https://shop.example.com') {
    return
  }

  if (event.data?.type === 'UNQVERIFY_RESULT') {
    const payload = event.data.payload as {
      aldersverificeringdk_verification_age: number
    }

    console.log('Verified age:', payload.aldersverificeringdk_verification_age)
    return
  }

  if (event.data?.type === 'UNQVERIFY_FAILURE') {
    console.error('Popup verification failed', event.data.code, event.data.error)
  }
}

window.addEventListener('message', handlePopupMessage)

3. Process the popup callback page

On the popup callback page, verify the returned JWT before sending anything to the opener.

import {
  storeVerificationToken,
  validateVerificationToken,
} from './unqverify-token'

const targetOrigin = 'https://shop.example.com'

async function handlePopupCallback(): Promise<void> {
  const token = new URL(window.location.href).searchParams.get('jwt')

  if (!token) {
    throw new Error('No JWT found in URL')
  }

  const result = await validateVerificationToken(token)

  if (!result.ok) {
    window.opener?.postMessage(
      {
        type: 'UNQVERIFY_FAILURE',
        code: result.code,
        error: result.message,
      },
      targetOrigin,
    )

    window.close()
    return
  }

  storeVerificationToken(token, result.payload.exp)

  window.opener?.postMessage(
    {
      type: 'UNQVERIFY_RESULT',
      payload: result.payload,
    },
    targetOrigin,
  )

  window.close()
}

handlePopupCallback().catch((error) => {
  window.opener?.postMessage(
    {
      type: 'UNQVERIFY_FAILURE',
      code: 'UNKNOWN_ERROR',
      error: error instanceof Error ? error.message : String(error),
    },
    targetOrigin,
  )

  window.close()
})

If you want parity with SDK behavior, use these codes in your own popup integration:

CodeWhen to use it
UNDER_AGEThe JWT is valid, but aldersverificeringdk_verification_result is false
POPUP_BLOCKEDwindow.open() returns null
POPUP_CLOSEDThe user closes the popup before your callback page finishes
POPUP_TIMEOUTYou decide the popup took too long and abort the flow
NETWORK_ERRORThe create request fails before the popup can start
TOKEN_INVALIDThe callback returned a JWT that failed signature or claim checks
UNKNOWN_ERRORAny other technical error

Security guidance

  • Post results back to an explicit targetOrigin, never *
  • Only trust messages from your own callback origin
  • Verify the JWT before sending success data to the opener window
  • Keep the popup callback page minimal: verify, notify, close

The popup flow still ends with browser-side token storage in these examples. That helps the storefront update immediately, but it does not replace a server-side trust decision. Treat the popup result as input to your backend validation flow, not as proof on its own.

Common mistakes

  • Opening the popup outside a user click handler
  • Posting the result back to * instead of a specific origin
  • Letting the opener trust any incoming message event
  • Sending success back to the opener before the JWT has been verified

Next step

Use the same JWT validation and storage helpers as the SDK

On this page