
# 패스키 등록

`POST /fapi/v1/me/passkeys` · 환경의 로그인 호스트에서 호출

[`POST /fapi/v1/me/passkeys/registration-options`](./registration-options)로 시작한 등록을 완료합니다. 브라우저의 응답을 검증하고, 패스키 또는 보안 키를 저장한 뒤 이를 반환합니다. 사용자에게는 새 로그인 방법에 관한 보안 이메일이 전송되며, 이 이메일은 끌 수 없습니다. 인증된 사용자의 `__Host-idp_session` 쿠키가 필요하며, 쿠키가 없거나 만료되었거나 취소된 경우 `401 unauthenticated`를 반환합니다. 다른 출처에 있는 자사 애플리케이션은 대신 프런트 API 액세스 토큰(DPoP 증명과 함께 `Authorization: DPoP <token>`)을 보낼 수 있습니다. 자세한 내용은 [프런트 API 액세스 토큰](/docs/api/fapi/front-api-token)을 참조하십시오.

세션 규칙은 시작 호출과 같습니다. 최근 10분 이내에 검증되어야 하며, 사용자가 두 번째 인증 요소를 가지고 있다면 두 번째 요소로 열린 세션이어야 합니다.

## 요청

헤더:

| 헤더 | 필수 | 설명 |
| --- | --- | --- |
| `Idp-Client` | 예 | SDK 식별자. |
| `Origin` | 예 | 정확히 `https://<sign-in host>`이어야 합니다. |
| `Content-Type` | 예 | `application/json`. |

본문:

| 필드 | 타입 | 필수 | 설명 |
| --- | --- | --- | --- |
| `ceremony_id` | string | 예 | 시작 호출의 `ceremony_id`. |
| `credential` | object | 예 | `navigator.credentials.create`의 결과를 JSON(`PublicKeyCredential.toJSON()`)으로 나타낸 값. 최대 16 KiB. |

## 응답

새 인증기를 담은 `201 Created`:

```json
{
  "id": "cred_01j9zzzzzzzzzzzzzzzzzzzzzz",
  "type": "passkey",
  "label": "iCloud Keychain",
  "status": "active",
  "synced": true,
  "created_at": "2026-10-12T09:00:00Z",
  "last_used_at": null
}
```

## 오류

거부된 등록은 실패한 시도로 집계되지 않습니다. 새 등록으로 다시 시작하십시오.

| 상태 | 코드 | 발생 조건 |
| --- | --- | --- |
| 400 | `malformed_request` | 본문이 올바른 JSON이 아닙니다. |
| 400 | `unknown_field` | 본문에 `ceremony_id`와 `credential` 외의 필드가 있습니다. |
| 401 | `unauthenticated` | 유효한 세션 쿠키가 없습니다. |
| 401 | `step_up_required` | 세션이 너무 오래되었거나 두 번째 인증 요소가 필요합니다. `WWW-Authenticate` 헤더의 값으로 다시 인증하십시오. |
| 403 | `origin_not_allowed` | `Origin`이 없거나 잘못되었습니다. |
| 409 | `credential_already_registered` | 이 인증기는 환경에 이미 등록되어 있습니다. |
| 409 | `credential_limit_reached` | 사용자가 이미 최대 개수의 패스키와 보안 키를 보유하고 있습니다. |
| 413 | `payload_too_large` | 본문이 너무 큽니다. |
| 415 | `unsupported_media_type` | `Content-Type`이 `application/json`이 아닙니다. |
| 422 | `validation_failed` | `/ceremony_id`: 등록이 알 수 없거나, 만료되었거나, 이미 사용되었거나, 다른 세션의 등록입니다. `/credential`: 응답이 없거나 검증에 실패했습니다. |
| 422 | `device_bound_passkey_required` | 환경은 한 기기에만 남아 있는 패스키만 허용합니다. |
| 422 | `aaguid_not_allowed` | 환경이 이 인증기 모델을 허용하지 않습니다. |
| 422 | `attestation_untrusted` | 환경은 신뢰할 수 있는 증명을 요구하며, 이 증명은 신뢰할 수 없습니다. |

## 예시

```bash
curl -X POST https://example.keenauth.app/fapi/v1/me/passkeys \
  -H "Idp-Client: my-app/1.0.0 (node/22.0.0)" \
  -H "Origin: https://example.keenauth.app" \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{"ceremony_id": "CQAAAAAAAAAAAAAAAAAAAA", "credential": {"id": "…", "rawId": "…", "type": "public-key", "response": {"clientDataJSON": "…", "attestationObject": "…"}}}'
```

## 개인정보

이 엔드포인트는 개인정보를 다룹니다: 자격 증명. [KeenAuth API의 개인정보](/docs/policies/personal-data)에서 저장되는 항목, 보관 기간, 삭제 방법을 설명합니다. 어떤 필드에도 민감정보(건강, 생체 정보, 종교, 정치적 견해, 성적 지향)를 보내지 마십시오.
