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
- Your page pre-opens a popup window from a click handler.
- Your page requests a verification redirect URL.
- The popup navigates to the returned URL.
- The user completes MitID verification.
- Your popup callback page verifies the JWT.
- The popup sends the result back to the opener window.
- 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()
})Recommended outcome codes
If you want parity with SDK behavior, use these codes in your own popup integration:
| Code | When to use it |
|---|---|
UNDER_AGE | The JWT is valid, but aldersverificeringdk_verification_result is false |
POPUP_BLOCKED | window.open() returns null |
POPUP_CLOSED | The user closes the popup before your callback page finishes |
POPUP_TIMEOUT | You decide the popup took too long and abort the flow |
NETWORK_ERROR | The create request fails before the popup can start |
TOKEN_INVALID | The callback returned a JWT that failed signature or claim checks |
UNKNOWN_ERROR | Any 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
messageevent - Sending success back to the opener before the JWT has been verified