> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# iframe 신분증 인증: 임베디드 웹 KYC

> Didit iframe으로 사이트에 KYC, KYB, AML, 라이브니스 감지를 임베드하세요. UniLink 노코드 또는 API 세션 지원. 인증 건당 $0.33부터, 월 $10 무료, 계약 없음.



iframe 연동 프롬프트
        프롬프트를 클립보드에 복사



      
      

\<pre>

\`Embed Didit identity verification into my web page using an iframe.

\## Two Options

\### Option A: UniLink (no backend — fastest setup)
Copy your UniLink URL from Didit Console > Workflows > Copy Link.
It looks like: https://verify.didit.me/u/<BASE64_WORKFLOW_ID>
Embed it directly:

\<iframe
src="https://verify.didit.me/u/YOUR_WORKFLOW_ID_BASE64"
style="width: 100%; height: 700px; border: none; border-radius: 12px;"
allow="camera; microphone; geolocation; clipboard-write; fullscreen; autoplay; encrypted-media; digital-credentials-get"
/>

No backend needed. Results appear in your Didit Console dashboard.

\### Option B: API session (full control)
1\. Backend creates session: POST /v3/session/ → response includes "url"
2\. Frontend embeds that "url" as the iframe src
3\. Listen for postMessage events from the iframe (event.origin === 'https://verify.didit.me', or your white-label domain)
4\. Backend receives the full, signed decision via webhook (status.updated)

\## postMessage contract (key events — full table below)
Every message is an object: { type: "didit:<event>", data: { ... }, timestamp: <ms epoch> }
Completion event: type === "didit:completed", data: { sessionId, status }
Other events: didit:ready { sessionId }, didit:started, didit:step_started { step },
didit:step_completed { step, nextStep }, didit:step_changed { step, previousStep },
didit:status_updated { status }, didit:error { error, step }, didit:cancelled, didit:close_request,
didit:layout { contentHeight, colorScheme: "light" | "dark", dir: "ltr" | "rtl" }
The payload is NESTED under event.data.data (event.data is the envelope).
For your own iframe, add sdk_presentation=embedded with the URL API to receive didit:layout.

\## Iframe size (redesigned flow)
\- Window 1024px and wider: 960px by 660px (at most 90% of the window)
\- Window 541px to 1023px: 500px wide, height = didit:layout contentHeight (700px until the first message, at most 90% of the window height)
\- Window 540px and below: full screen

\## Frontend Code (Option B — React)
function VerificationIframe({ verificationUrl }) {
useEffect(() => {
function handleMessage(event) {
  if (event.origin !== 'https://verify.didit.me') return; // use your white-label domain if configured
  const { type, data } = event.data || {};
  if (type === 'didit:completed') {
    // data.sessionId, data.status: "Approved" | "Declined" | "In Review" // open string — other session statuses possible in edge flows
    // Trust the webhook on your backend for the canonical decision.
  }
}
window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
}, []);

return (
\<iframe
  src={verificationUrl}
  style={{ width: '100%', height: '700px', border: 'none', borderRadius: '12px' }}
  allow="camera; microphone; geolocation; clipboard-write; fullscreen; autoplay; encrypted-media; digital-credentials-get"
/>
);
}

\## Required client-side permissions
If you front your site with a reverse proxy (nginx, Cloudflare, etc.) make sure it does not
strip the Permissions-Policy / Feature-Policy headers — camera and microphone access must be
allowed for your origin or the iframe will be unable to start the camera.

\## Required iframe Permissions
allow="camera; microphone; geolocation; clipboard-write; fullscreen; autoplay; encrypted-media; digital-credentials-get"
camera: document and face capture. microphone: optional audio during video capture.
geolocation: the location step. clipboard-write: the copy buttons.
digital-credentials-get: sharing a digital ID from a wallet such as Google Wallet.

\## Backend: Create Session
POST https://verification.didit.me/v3/session/
Headers: { "x-api-key": DIDIT_API_KEY, "Content-Type": "application/json" }
Body: { "workflow_id": DIDIT_WORKFLOW_ID, "vendor_data": userId }
201 response fields (all 10, per OpenAPI):
\{ "session_id", "session_number", "session_token", "url", "vendor_data", "metadata",
"status", "workflow_id", "workflow_version", "callback" }
Use response.url as the iframe src — never build the URL from session_token by hand.

\## Rate Limits
600 session-create requests per minute per API key (falls back to bearer token, then client IP), plus a shared 300 write-requests/minute budget across all endpoints. HTTP 429 with Retry-After when exceeded.

\## Environment Variables
\- DIDIT_API_KEY (backend only)
\- DIDIT_WORKFLOW_ID (backend only)

\## Docs
\- Iframe guide: https://docs.didit.me/integration/web-sdks/incontext-iframe
\- Create Session: https://docs.didit.me/sessions-api/create-session
\- Webhooks: https://docs.didit.me/integration/webhooks`

\</pre>



      
      

붙여넣을 곳

iframe을 사용해 Didit 인증 흐름을 애플리케이션 안에 직접 삽입하세요. 사용자를 사이트 밖으로 리디렉션하지 않으므로 끊김 없는 경험을 제공합니다.## 개요| 기능 | UniLink(백엔드 없음) | API 세션(백엔드 있음) |
| - | - | - |
| 설정 시간 | \< 1분 | 5-10분 |
| 백엔드 필요 | ❌ 아니요 | ✅ 예 |
| 사용자 지정 `vendor_data` | ❌ 아니요 | ✅ 예 |
| 사용자 지정 `metadata` | ❌ 아니요 | ✅ 예 |
| 세션별 콜백 | ❌ 아니요 | ✅ 예 |
| 세션 추적 | ❌ 아니요 | ✅ 예 |***## UniLink iframe(백엔드 불필요)시작하는 가장 빠른 방법입니다. UniLink URL을 복사해 붙여넣기만 하면 됩니다.### 필요한 것1) Didit 워크플로([Didit 콘솔](https://business.didit.me)에서 생성)
2) UniLink URL(워크플로에서 **Copy Link** 클릭)### 구현```html theme={null}
<iframe 
  src="https://verify.didit.me/u/YOUR_WORKFLOW_ID_BASE64"
  style="width: 100%; height: 700px; border: none;" 
  allow="camera; microphone; geolocation; clipboard-write; fullscreen; autoplay; encrypted-media; digital-credentials-get"
></iframe>
```> ⚠️ **필수**: 라이브니스 감지 중 카메라에 접근하려면 `allow` 속성이 반드시 필요합니다.### 주요 이점- **1분 미만 설정**: 백엔드도, API 키도 필요 없습니다
- **설정 불필요**: 콜백 URL은 워크플로 설정에서 지정됩니다
- **MVP에 적합**: 바로 시작할 수 있습니다***## API 세션 iframe(백엔드 필요)세션별 맞춤 설정이 필요한 고급 연동을 위한 방식입니다.### 사용 시기- 세션마다 사용자 지정 `vendor_data` 또는 `metadata`를 전달
- 세션마다 다른 콜백 URL 설정
- 인증이 시작되기 전에 서버에서 세션 추적
- 세션을 사용자 ID와 연결### 구현**1단계: 세션 생성(백엔드)**세션 생성 응답에는 **`url`**이라는 필드가 있습니다. 이 값을 iframe의 `src`로 사용하세요.```javascript theme={null}
// Node.js / Express
app.post('/api/create-verification', async (req, res) => {
  const { userId } = req.body;

  const response = await fetch('https://verification.didit.me/v3/session/', {
    method: 'POST',
    headers: {
      'x-api-key': process.env.DIDIT_API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      workflow_id: process.env.DIDIT_WORKFLOW_ID,
      vendor_data: userId,
      callback: 'https://yourapp.com/verification-callback',
      metadata: { source: 'web-app', timestamp: Date.now() },
    }),
  });

  if (!response.ok) {
    return res.status(response.status).send(await response.text());
  }

  // 201 per OpenAPI: { session_id, session_number, session_token, url, vendor_data,
  //                    metadata, status, workflow_id, workflow_version, callback }
  const session = await response.json();
  res.json({ sessionId: session.session_id, verificationUrl: session.url });
});
```**2단계: iframe에 삽입(프런트엔드)**```html theme={null}
<iframe 
  id="didit-verification"
  src="" <!-- Set dynamically from API response -->
  style="width: 100%; height: 650px; border: none;" 
  allow="camera; microphone; geolocation; clipboard-write; fullscreen; autoplay; encrypted-media; digital-credentials-get"
></iframe>

<script>
  async function startVerification() {
    const response = await fetch('/api/create-verification', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ userId: 'user-123' })
    });
    
    const { verificationUrl } = await response.json();
    document.getElementById('didit-verification').src = verificationUrl;
  }
</script>
```***## 이벤트 수신(postMessage)인증 페이지는 이벤트를 부모 창으로 전송합니다. 모든 메시지는 다음 구조를 가집니다:```typescript theme={null}
{
  type: 'didit:<event>',   // e.g. 'didit:completed'
  data: { /* payload */ }, // event-specific; may be undefined
  timestamp: 1718000000000 // ms epoch
}
```payload는 **중첩**되어 있습니다. `event.data.sessionId`가 아니라 `event.data.data.sessionId`를 읽으세요:```javascript theme={null}
window.addEventListener('message', (event) => {
  // Use your white-label domain here if you have one configured
  if (event.origin !== 'https://verify.didit.me') return;

  const { type, data } = event.data || {};

  if (type === 'didit:ready') {
    console.log('Session ID:', data.sessionId); // available as soon as the page loads
  }
  if (type === 'didit:completed') {
    console.log('Done:', data.sessionId, data.status); // e.g. "Approved" | "Declined" | "In Review"
    // UI hint only — the canonical decision arrives on your backend via webhook
  }
});
```주요 이벤트는 다음과 같습니다: `didit:ready`(`{ sessionId }`), `didit:started`, `didit:step_started` / `didit:step_completed` / `didit:step_changed`(`{ step, ... }`), `didit:status_updated`(`{ status }`), `didit:completed`(`{ sessionId, status }`), `didit:cancelled`, `didit:error`(`{ error, step? }`), `didit:close_request`, 그리고 `didit:layout`(`{ contentHeight, colorScheme, dir }`, [아래](#diditlayout으로-iframe-크기-조정) 참조). [JavaScript SDK 이벤트 참조](https://docs.didit.me/ko/integration/web-sdks/javascript-sdk.md#이벤트-참조)에는 타입이 지정된 콜백으로 제공되는 인증 이벤트가 나열되어 있습니다. 버전 0.5.0부터 SDK는 레이아웃 메시지를 `onEvent`로 전달하지 않고 내부에서 처리합니다.### `didit:layout`으로 iframe 크기 조정인증 페이지는 `didit:layout` 메시지로 필요한 공간의 크기를 알려줍니다. 페이지가 로드될 때 한 번, 그리고 콘텐츠 높이, 테마 또는 텍스트 방향이 바뀔 때마다 다시 전송합니다:```typescript theme={null}
{
  type: 'didit:layout',
  data: {
    contentHeight: 612,    // CSS px
    colorScheme: 'light',  // 'light' | 'dark'
    dir: 'ltr'             // 'ltr' | 'rtl'
  },
  timestamp: 1718000000000
}
```| 필드 | 타입 | 설명 |
| - | - | - |
| `contentHeight` | `number` | 상단 바를 포함한 페이지 콘텐츠의 자연 높이(CSS 픽셀 단위) |
| `colorScheme` | `'light' \| 'dark'` | 페이지가 표시되는 테마입니다. 화이트 라벨 색상을 사용하면 기기 설정이 아니라 배경색이 테마를 결정합니다 |
| `dir` | `'ltr' \| 'rtl'` | 페이지 언어의 텍스트 방향입니다 |> [!NOTE]
>
> 자체 iframe에서 `didit:layout`을 받으려면 iframe의 인증 URL에 `sdk_presentation=embedded`를 추가하세요. 버전 0.5.0부터 [JavaScript SDK](https://docs.didit.me/ko/integration/web-sdks/javascript-sdk.md)가 이 프레젠테이션을 자동으로 설정합니다. 프레젠테이션 파라미터가 없으면 페이지는 독립형 레이아웃을 사용하며 레이아웃 메시지를 보내지 않습니다.이 호스트는 [권장 크기](#iframe-크기)를 따릅니다. 너비 1024px 이상에서는 고정 카드, 540px 이하에서는 전체 화면, 그 사이에서는 너비 500px의 iframe이 페이지가 보고하는 높이를 사용합니다:```html theme={null}
<iframe
  id="didit-verification"
  class="didit-iframe"
  title="Didit verification"
  allow="camera; microphone; geolocation; clipboard-write; fullscreen; autoplay; encrypted-media; digital-credentials-get"
></iframe>

<style>
  /* 541px to 1023px wide: 500px wide and as tall as the content */
  .didit-iframe {
    display: block;
    width: 500px;
    max-width: 100%;
    height: var(--didit-content-height, 700px); /* 700px until the first didit:layout */
    max-height: 90vh;
    max-height: 90dvh;
    margin: 0 auto;
    border: none;
    border-radius: 16px;
  }

  /* 1024px and wider: the computer card */
  @media (min-width: 1024px) {
    .didit-iframe {
      width: 960px;
      max-width: 90vw;
      height: 660px;
    }
  }

  /* 540px and below: full screen */
  @media (max-width: 540px) {
    .didit-iframe {
      position: fixed;
      inset: 0;
      width: 100%;
      height: 100vh;
      height: 100dvh;
      max-height: none;
      border-radius: 0;
    }
  }
</style>

<script>
  const DIDIT_ORIGIN = 'https://verify.didit.me'; // or your white-label domain
  const iframe = document.getElementById('didit-verification');

  // verificationUrl: the session `url` from your backend, or your UniLink
  const src = new URL(verificationUrl);
  src.searchParams.set('sdk_presentation', 'embedded');
  window.addEventListener('message', (event) => {
    if (event.origin !== DIDIT_ORIGIN || event.source !== iframe.contentWindow) return;
    const { type, data } = event.data || {};
    if (type !== 'didit:layout') return;
    const height = data?.contentHeight;
    if (typeof height !== 'number' || !Number.isFinite(height) || height <= 0) return;

    // Only the 541px to 1023px size reads it; the other two sizes are fixed
    iframe.style.setProperty('--didit-content-height', `${height}px`);
  });
  iframe.src = src.toString();
</script>
```541px에서 1023px 사이에서는 이 예시가 레이아웃 메시지가 도착하기 전까지 초기 높이 700px를 사용하며, 보이는 창 높이의 90%를 넘지 않도록 제한합니다. 데스크톱과 휴대폰 크기는 즉시 적용됩니다. 휴대폰에서는 iframe이 창 전체를 덮으므로, 호스트에서 인증을 벗어날 수 있는 방법을 제공하세요. iframe 주변에 배치하는 컨트롤은 `colorScheme`과 `dir`을 사용해 페이지의 테마와 텍스트 방향에 맞추세요.사용자의 연결이 끊기면 페이지에 "No internet connection" 화면이 표시되며, 연결이 복구되면 자동으로 닫힙니다. 사용자가 이 화면에서 **Exit Verification**을 선택하면 페이지는 `error: "network_error"`를 담은 `didit:error`를 보낸 뒤 `didit:cancelled`를 보냅니다. `didit:cancelled`를 받으면 iframe을 닫으세요.***## 모달 구현페이지 위에 겹쳐 표시되는 세련된 모달 경험을 위한 방법입니다:### HTML/CSS/JS```html theme={null}
<!DOCTYPE html>
<html>
<head>
  <style>
    .didit-modal-overlay {
      display: none;
      position: fixed;
      inset: 0;
      background: rgba(0, 0, 0, 0.6);
      z-index: 9999;
      backdrop-filter: blur(4px);
    }
    
    .didit-modal-container {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: 90%;
      max-width: 480px;
      height: 85vh;
      max-height: 750px;
      background: white;
      border-radius: 16px;
      overflow: hidden;
      box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
    }
    
    .didit-modal-close {
      position: absolute;
      top: 12px;
      right: 12px;
      z-index: 10;
      width: 32px;
      height: 32px;
      border: none;
      background: rgba(0, 0, 0, 0.1);
      border-radius: 50%;
      cursor: pointer;
      font-size: 18px;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    
    .didit-modal-close:hover {
      background: rgba(0, 0, 0, 0.2);
    }
    
    .didit-iframe {
      width: 100%;
      height: 100%;
      border: none;
    }
  </style>
</head>
<body>
  <button onclick="openVerification()">Verify Identity</button>

  <div id="didit-modal" class="didit-modal-overlay">
    <div class="didit-modal-container">
      <button class="didit-modal-close" onclick="closeVerification()">✕</button>
      <iframe 
        id="didit-iframe"
        class="didit-iframe"
        allow="camera; microphone; geolocation; clipboard-write; fullscreen; autoplay; encrypted-media; digital-credentials-get"
      ></iframe>
    </div>
  </div>

  <script>
    const UNILINK_URL = 'https://verify.didit.me/u/YOUR_WORKFLOW_ID_BASE64';
    
    function openVerification() {
      document.getElementById('didit-iframe').src = UNILINK_URL;
      document.getElementById('didit-modal').style.display = 'block';
      document.body.style.overflow = 'hidden';
    }
    
    function closeVerification() {
      document.getElementById('didit-iframe').src = '';
      document.getElementById('didit-modal').style.display = 'none';
      document.body.style.overflow = '';
    }
    
    // Close on escape key
    document.addEventListener('keydown', (e) => {
      if (e.key === 'Escape') closeVerification();
    });
    
    // Close on overlay click
    document.getElementById('didit-modal').addEventListener('click', (e) => {
      if (e.target.id === 'didit-modal') closeVerification();
    });
  </script>
</body>
</html>
```***## React 컴포넌트모달 기반 인증을 위한 재사용 가능한 React 컴포넌트입니다:```tsx theme={null}
import { useState, useEffect } from 'react';

interface DiditInContextProps {
  /**
   * Either the `url` returned by POST /v3/session/ (API session)
   * or your UniLink URL copied from the Console (Workflows → Copy Link).
   * Don't derive the UniLink yourself — the path segment is a 22-character
   * base64url encoding of the workflow UUID's raw bytes, not btoa(workflowId).
   */
  verificationUrl: string;
  isOpen: boolean;
  onClose: () => void;
  onComplete?: (sessionId: string, status: string) => void;
}

export function DiditInContext({ 
  verificationUrl, 
  isOpen, 
  onClose,
  onComplete 
}: DiditInContextProps) {
  const [iframeSrc, setIframeSrc] = useState<string>('');

  useEffect(() => {
    if (isOpen) {
      setIframeSrc(verificationUrl);
      document.body.style.overflow = 'hidden';
    } else {
      setIframeSrc('');
      document.body.style.overflow = '';
    }
  }, [isOpen, verificationUrl]);

  // Listen for postMessage from iframe
  // Messages look like: { type: 'didit:<event>', data: {...}, timestamp }
  useEffect(() => {
    const handleMessage = (event: MessageEvent) => {
      // Use your white-label domain here if you have one configured
      if (event.origin !== 'https://verify.didit.me') return;

      if (event.data?.type === 'didit:completed' && onComplete) {
        onComplete(event.data.data?.sessionId, event.data.data?.status);
        onClose();
      }
    };

    window.addEventListener('message', handleMessage);
    return () => window.removeEventListener('message', handleMessage);
  }, [onComplete, onClose]);

  // Handle escape key
  useEffect(() => {
    const handleEscape = (e: KeyboardEvent) => {
      if (e.key === 'Escape' && isOpen) onClose();
    };
    
    document.addEventListener('keydown', handleEscape);
    return () => document.removeEventListener('keydown', handleEscape);
  }, [isOpen, onClose]);

  if (!isOpen) return null;

  return (
    <div 
      className="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center"
      onClick={(e) => e.target === e.currentTarget && onClose()}
    >
      <div className="relative w-[90%] max-w-[480px] h-[85vh] max-h-[750px] bg-white rounded-2xl overflow-hidden shadow-2xl">
        <button 
          onClick={onClose}
          className="absolute top-3 right-3 z-10 w-8 h-8 rounded-full bg-black/10 hover:bg-black/20 flex items-center justify-center"
          aria-label="Close"
        >
          ✕
        </button>
        <iframe 
          src={iframeSrc}
          className="w-full h-full border-0"
          allow="camera; microphone; geolocation; clipboard-write; fullscreen; autoplay; encrypted-media; digital-credentials-get"
          title="Didit Verification"
        />
      </div>
    </div>
  );
}
```### 사용법```tsx theme={null}
import { useState } from 'react';
import { DiditInContext } from './DiditInContext';

function App() {
  const [isVerificationOpen, setIsVerificationOpen] = useState(false);
  
  return (
    <>
      <button onClick={() => setIsVerificationOpen(true)}>
        Verify Identity
      </button>
      
      <DiditInContext
        verificationUrl="https://verify.didit.me/u/YOUR_WORKFLOW_ID_BASE64" // UniLink from Console, or the API session `url`
        isOpen={isVerificationOpen}
        onClose={() => setIsVerificationOpen(false)}
        onComplete={(sessionId, status) => {
          console.log(`Verification ${status}: ${sessionId}`);
          if (status === 'Approved') {
            // Navigate to success page
          }
        }}
      />
    </>
  );
}
```***## Vue 컴포넌트```vue theme={null}
<template>
  <Teleport to="body">
    <div 
      v-if="isOpen"
      class="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center"
      @click.self="$emit('close')"
    >
      <div class="relative w-[90%] max-w-[480px] h-[85vh] max-h-[750px] bg-white rounded-2xl overflow-hidden shadow-2xl">
        <button 
          @click="$emit('close')"
          class="absolute top-3 right-3 z-10 w-8 h-8 rounded-full bg-black/10 hover:bg-black/20 flex items-center justify-center"
        >
          ✕
        </button>
        <iframe 
          :src="iframeSrc"
          class="w-full h-full border-0"
          allow="camera; microphone; geolocation; clipboard-write; fullscreen; autoplay; encrypted-media; digital-credentials-get"
        />
      </div>
    </div>
  </Teleport>
</template>

<script setup lang="ts">
import { computed, watch, onMounted, onUnmounted } from 'vue';

const props = defineProps<{
  isOpen: boolean;
  // The `url` from POST /v3/session/, or your UniLink URL copied from the
  // Console (Workflows → Copy Link). Don't derive the UniLink with btoa() —
  // its path segment is base64url of the workflow UUID's raw bytes.
  verificationUrl: string;
}>();

const emit = defineEmits<{
  close: [];
  complete: [sessionId: string, status: string];
}>();

const iframeSrc = computed(() => (props.isOpen ? props.verificationUrl : ''));

// Handle escape key
const handleEscape = (e: KeyboardEvent) => {
  if (e.key === 'Escape' && props.isOpen) emit('close');
};

// Handle postMessage — messages look like { type: 'didit:<event>', data: {...}, timestamp }
const handleMessage = (event: MessageEvent) => {
  // Use your white-label domain here if you have one configured
  if (event.origin !== 'https://verify.didit.me') return;
  if (event.data?.type === 'didit:completed') {
    emit('complete', event.data.data?.sessionId, event.data.data?.status);
  }
};

onMounted(() => {
  document.addEventListener('keydown', handleEscape);
  window.addEventListener('message', handleMessage);
});

onUnmounted(() => {
  document.removeEventListener('keydown', handleEscape);
  window.removeEventListener('message', handleMessage);
});

// Lock body scroll when open
watch(() => props.isOpen, (isOpen) => {
  document.body.style.overflow = isOpen ? 'hidden' : '';
});
</script>
```***## 구성### 필수 권한캡처, 위치, 복사, 미디어 호환성에 사용하는 전체 권한 목록은 다음과 같습니다:```html theme={null}
allow="camera; microphone; geolocation; clipboard-write; fullscreen; autoplay; encrypted-media; digital-credentials-get"
```| 권한 | 용도 및 권한이 없을 때의 동작 |
| - | - |
| `camera` | 문서 스캔과 얼굴 캡처에 사용합니다. 권한이 없으면 카메라를 시작할 수 없고, 사용자에게 카메라 접근 안내가 표시됩니다 |
| `microphone` | 문서 및 얼굴 동영상 캡처 중 선택적으로 오디오를 녹음합니다. 권한이 없으면 오디오 없이 캡처가 계속됩니다 |
| `geolocation` | 위치 단계에서 기기의 위치를 읽습니다. 권한이 없으면 브라우저에서 위치를 허용했더라도 사용자에게 **위치 접근이 꺼져 있습니다**가 표시됩니다 |
| `clipboard-write` | 세션 참조 값이나 인증 링크를 복사하며, 앱 내 브라우저 화면에서도 사용합니다. 권한이 없으면 복사 작업이 실패할 수 있으며, 사용자는 화면에 보이는 텍스트나 브라우저 주소를 직접 복사해야 합니다 |
| `fullscreen` | 미디어 컨트롤이 전체 화면을 요청할 수 있게 합니다. 권한이 없으면 해당 컨트롤이 iframe 안에 머무릅니다 |
| `autoplay` | 브라우저 규칙에 따라 안내 동영상과 카메라 미리보기를 자동으로 재생할 수 있게 합니다. 권한이 없으면 재생이 사용자 동작을 기다릴 수 있습니다 |
| `encrypted-media` | 보호된 미디어 호환성을 위해 유지됩니다. 현재 캡처 단계에서는 필요하지 않으며, 권한이 없으면 보호된 미디어 재생이 차단됩니다 |
| `digital-credentials-get` | 브라우저의 Digital Credentials API를 통해 Google Wallet과 같은 지갑에서 디지털 신분증을 공유합니다. 권한이 없으면 브라우저가 iframe 안의 지갑 요청을 거부하므로, 사용자가 그 안에서 지갑의 신분증을 공유할 수 없습니다 |`allow` 속성은 권한을 위임하지만, 사용자의 선택이나 호스트 페이지의 더 엄격한 `Permissions-Policy` 헤더를 무시하지는 않습니다.### iframe 크기인증 흐름은 자신이 놓인 iframe의 크기에 맞춰 배치됩니다. 표시될 창에 맞게 iframe 크기를 설정하세요:| 창 너비 | 권장 iframe 크기 |
| - | - |
| 1024px 이상(컴퓨터) | 960px × 660px이며, 창 너비와 높이의 최대 90%입니다. 모든 단계에서 이 크기를 유지하세요 |
| 541px \~ 1023px | 너비는 500px이고 높이는 콘텐츠에 맞춥니다. 높이는 [`didit:layout` 메시지](#diditlayout으로-iframe-크기-조정)의 `contentHeight` 값을 사용하며, 창 높이의 최대 90%입니다 |
| 540px 이하(휴대폰) | 전체 화면: 창의 전체 너비와 보이는 전체 높이를 사용하며, 둥근 모서리 없이 표시합니다 |중간 범위에서는 첫 `didit:layout` 메시지가 도착하기 전까지 500px × 700px로 시작하며, 높이 상한은 동일하게 적용합니다. [`didit:layout` 예제](#diditlayout으로-iframe-크기-조정)는 메시지가 도착하기 전이라도 컴퓨터와 휴대폰 크기를 포함해 세 가지 크기를 모두 적용합니다.### 콘텐츠 보안 정책(CSP)CSP를 사용하는 경우 `frame-src` 지시어에 Didit 도메인을 추가하세요:```
Content-Security-Policy: frame-src https://verify.didit.me;
```***## 기기 간 인증InContext iframe은 기기 간 인증을 자동으로 지원합니다:1) 사용자가 데스크톱에서 인증을 시작합니다
2) 카메라를 사용할 수 없으면 QR 코드가 표시됩니다
3) 사용자가 모바일에서 QR 코드를 스캔합니다
4) 사용자가 모바일에서 인증을 완료합니다
5) 데스크톱 iframe에 결과가 자동으로 업데이트됩니다이 과정은 자동으로 진행되며, 별도의 설정은 필요하지 않습니다.***## 문제 해결### 카메라가 작동하지 않음1) iframe에 전체 [권한 목록](#필수-권한)이 포함되어 있는지 확인하세요
2) 사이트가 HTTPS로 제공되는지 확인하세요
3) 사용자가 도메인에 카메라 권한을 허용했는지 확인하세요
4) iframe에서 카메라 접근이 실패하면 [리디렉션](https://docs.didit.me/ko/integration/web-sdks/web-redirect.md) 방식을 사용해 보세요### iframe이 로드되지 않음1) 콘텐츠 보안 정책(CSP) 문제가 있는지 확인하세요
2) `frame-src` 지시어에 `https://verify.didit.me`를 추가하세요
3) 브라우저 콘솔에서 오류를 확인하세요### postMessage 메시지가 수신되지 않음1) 올바른 출처(`https://verify.didit.me` 또는 화이트 라벨 도메인)에서 오는 메시지를 수신하도록 리스닝하고 있는지 확인하세요
2) iframe을 열기 전에 이벤트 리스너를 추가했는지 확인하세요
3) 이벤트 이름과 형식을 확인하세요. 타입에는 접두사가 붙고(`verification_complete`가 아니라 `didit:completed`), 페이로드는 `event.data.data` 안에 중첩되어 있습니다***## 예제 저장소> [card] [GitHub 저장소](https://github.com/didit-protocol/iframe-example)
>
> GitHub에서 소스 코드와 예제를 확인하세요***


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.