임베드 서명
<bf-signing>은 Bona Fide의 서명 페이지를 페이지 안의 샌드박스 iframe에 렌더링하고, 문서에 맞게 크기를 조정하며,
서명자의 동작을 DOM 이벤트로 알려 줍니다. 프레임워크에 의존하지 않으며, 가벼운 React 및 Vue 래퍼가 포함되어 있습니다.
-
delivery: "embedded"로 봉투를 만들어 수신자에게 이메일이 가지 않도록 합니다. -
서명자가 페이지를 열면 서버에서 세션을 발급하고, 사이트의 출처를
allowedOrigins에 넣습니다. 임베드 페이지는 여기에 없는 상위 페이지와는 통신하지 않습니다.// Your backend (never expose the API key to the browser)const res = await fetch(`https://api.bonafidesign.com/v1/envelopes/${envelopeId}/sessions`, {method: 'POST',headers: { Authorization: `Bearer ${process.env.BONA_FIDE_API_KEY}`, 'Content-Type': 'application/json' },body: JSON.stringify({ recipient: recipientId, mode: 'embedded', allowedOrigins: ['https://app.example.com'] }),});const { token } = await res.json(); // 'bfs_…', single-use, 10 minutes by default -
토큰으로 요소를 렌더링합니다.
<script type="module" src="https://js.bonafidesign.com/v1"></script><bf-signing session="bfs_…" theme="auto" min-height="480" max-height="90vh"></bf-signing><script>const el = document.querySelector('bf-signing');el.addEventListener('completed', (e) => {// e.detail = { type: 'completed', envelope: 'env_…' }window.location.href = '/onboarding/next';});el.addEventListener('session_expiring', () => refreshSession());el.addEventListener('error', (e) => console.error(e.detail.code, e.detail.message));</script>import { BonaFideSigning } from '@bona-fide/embed/react';export function SignContract({ token }: { token: string }) {return (<BonaFideSigningsession={token}theme="auto"minHeight={480}maxHeight="90vh"onCompleted={(e) => console.log('envelope completed', e.envelope)}onDeclined={(e) => console.log('declined', e.reason)}onSessionExpiring={() => refreshSession()}onError={(e) => console.error(e.code, e.message)}/>);}<script setup lang="ts">import { BonaFideSigning } from '@bona-fide/embed/vue';defineProps<{ token: string }>();const done = (e: { envelope: string }) => console.log('envelope completed', e.envelope);</script><template><BonaFideSigning :session="token" theme="auto" :min-height="480" max-height="90vh" @completed="done" /></template> -
서버에서 확인합니다. 브라우저 이벤트는 UI 신호로만 취급하고, 결과는
envelope.completed웹훅(또는GET /v1/envelopes/{id})으로 판단하세요.
| 속성(React / Vue prop) | 기본값 | 설명 |
|---|---|---|
session |
bfs_… 토큰. 변경하면 프레임을 다시 로드합니다 |
|
base-url(baseUrl) |
https://sign.bonafidesign.com/s/ |
배포 환경의 호스팅 서명 경로 |
theme |
auto |
auto, light, dark 중 하나 |
locale |
서명 페이지에 전달됩니다 | |
min-height, max-height(minHeight, maxHeight) |
480, 90vh |
픽셀 또는 vh. 프레임은 두 값 사이에서 문서에 맞춰 커지고, 그 이상은 내부에서 스크롤됩니다 |
timeout |
30000 |
페이지를 기다리는 밀리초. 넘으면 코드가 load_timeout인 error가 발생합니다 |
frame-title |
Document signing |
iframe의 접근성 제목 |
요소에는 reload() 메서드도 있습니다. 예를 들어 새 세션을 발급한 후에 사용합니다.
이벤트
섹션 제목: “이벤트”이벤트는 버블링되고, 섀도 루트를 넘어 전파되며, 페이로드를 event.detail에 담습니다. bf-event는 모든 이벤트에서 발생합니다.
| 이벤트 | detail |
|---|---|
ready |
{ type: 'ready' }: 서명 페이지가 로드되었습니다 |
field_completed |
{ type, field }: 필드가 채워졌습니다 |
signed |
{ type, recipient }: 이 수신자가 제출했습니다 |
completed |
{ type, envelope }: 봉투가 완료되었습니다 |
declined |
{ type, reason? } |
session_expiring |
{ type, expiresAt }: 곧 새 세션을 발급하세요 |
error |
{ type, code, message }. load_timeout과 invalid_base_url 포함 |
React prop은 onReady, onFieldCompleted, onSigned, onCompleted, onDeclined, onSessionExpiring,
onError, onEvent입니다. Vue는 이벤트 이름(@completed, @session_expiring 등)과 event를 emit합니다.
보안 모델
섹션 제목: “보안 모델”- 토큰은 iframe의 URL 프래그먼트(
#session=…)로 전달되므로 서버나Referer헤더에 절대 도달하지 않습니다. 서명 페이지는 토큰을 읽은 뒤 URL과 기록에서 지우고, 메모리 내 액세스 토큰으로 교환합니다. 쿠키는 사용하지 않습니다. - iframe은 샌드박스 처리되며(
allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads)referrerpolicy="strict-origin"이 설정됩니다. - 페이지는 메시지가 요소 자체의 iframe에서 왔고, 그리고
base-url출처에서 왔으며, 그리고 네임스페이스가 지정된bona-fide.embed/1메시지로 파싱될 때만 받아들입니다. 그 밖의 메시지는 모두 무시됩니다. - 서명 페이지는 정확한 출처로만(
*는 절대 사용하지 않음), 그리고 그 출처가 세션의allowedOrigins에 있을 때만 메시지를 보냅니다. API도 허용되지 않은 출처에서 오는 임베드 세션의 호출을 거부합니다.