
# 로그인 단계 실행

`POST /fapi/v1/transactions/current/steps/{step}` · 환경의 로그인 호스트에서 호출

현재 트랜잭션의 한 단계를 실행합니다. 이 버전은 `identifier` 및 `password` 단계를 받습니다. 자격 증명 단계가 실패하면
항상 같은 `401 invalid_credentials`를 반환하므로, 응답만으로는 계정이 존재하는지 드러나지 않습니다. 비밀번호 단계로 트랜잭션이
완료되면 세션 쿠키 `__Host-idp_session`(필요한 경우 `__Host-idp_device`도)가 설정되고 트랜잭션 쿠키는 제거됩니다.

## 요청

헤더:

| 헤더 | 필수 | 설명 |
| --- | --- | --- |
| `Idp-Client` | 예 | SDK 식별자입니다. |
| `Origin` | 예 | 정확히 `https://<sign-in host>`이어야 합니다. |
| `Idp-Csrf-Token` | 예 | 트랜잭션 객체의 `csrf_token`입니다. |

경로: `step`, 단계 이름(`identifier`, `password`, `sso_start`).

`otp_send` 단계에서 `next_steps[].methods`은 유효 정책이 허용하는 비밀번호 없는 방식(`email_otp`, `magic_link`)을 나열합니다. 클라이언트는 이 방식만 제공해야 합니다.

본문(필드는 단계에 따라 다르며 `next_steps[].fields`에 목록이 있습니다):

| 필드 | 유형 | 설명 |
| --- | --- | --- |
| `identifier` | 문자열 | 이메일 주소, 전화번호 또는 사용자 이름(`identifier` 단계), 최대 320자입니다. |
| `password` | 문자열 | 비밀번호(`password` 단계), 최대 128자입니다. |
| `connection_id` | 문자열 | `sso_connections[].connection_id` 값(`sso_start` 단계)입니다. |

## 응답

단계 실행 후의 트랜잭션과 함께 `200 OK`을 반환합니다. `completed` 트랜잭션에는 `csrf_token`이 없습니다.

환경에서 두 번째 인증 요소를 요구하지만 사용자가 아직 등록하지 않은 경우, 환경의 `mfa.enrollment_grace_days` 기간 안에 있는 사용자는 그대로 로그인할 수 있으며 `completed` 응답에는 `mfa_enrollment`이 포함됩니다.
`required_by`(RFC 3339 형식, 유예 기간의 종료 시점이며 서버가 계산함)과 `methods`(`webauthn`, `totp`: 사용자가 지금 등록할 수 있는 요소를 화면 순서대로 나열함)입니다. 날짜를 표시하고 사용자가 계속 진행하도록 하십시오. 등록은 계정 설정에서 추가할 수 있습니다. 유예 기간이 없거나, 사용자가 정책을 이미 충족했거나, 즉시 등록이 필요한 경우에는 이 필드가 없습니다. 애플리케이션의 `min_acr`과 요청된 `acr_values`은 유예 기간으로 미뤄지지 않습니다.

## 기업 SSO(홈 영역 탐색)

`identifier` 단계가 기업 연결에 속한 도메인의 이메일 주소를 받으면, 로그인은 해당 연결을 통해 계속 진행됩니다. 탐색에는 이메일 주소의 `@` 뒤 부분만 사용합니다.

- 연결이 하나인 경우: 트랜잭션은 `federated_redirect` 상태로 바뀌고 응답에는 ID 공급자 시작 주소인 `redirect_url`이 포함됩니다. 최상위 창을 해당 주소로 이동하십시오. 이 주소에는 일회용 토큰이 들어 있으므로 저장하거나 기록하지 마십시오.
- 연결이 여러 개인 경우: 트랜잭션은 `identifier_pending` 상태에 머물고, 응답은 `sso_connections`(`connection_id`, `display_name`, `protocol`) 목록을 반환합니다. `display_name`을 텍스트로 표시하고, 사용자가 고른 항목은 `sso_start` 단계(`identifier` 및 `connection_id`)로 보내십시오. 해당 도메인에 제공되지 않는 연결을 선택하면 `422 validation_failed`이 반환됩니다.
- 연결이 없거나 탐색을 일시적으로 사용할 수 없는 경우: 일반 로그인이 계속 진행되므로, 장애 때문에 다른 로그인 방식이 막히지 않습니다. `sso_start`은 사용자의 명시적 선택이며, 이 경우 대신 `503 temporarily_unavailable`을 반환합니다.

로그인 요청이 SSO를 요구하는 조직을 지정하면, 트랜잭션은 해당 조직의 연결만 제공합니다. 비밀번호 단계와 이메일 코드 단계는 제공되지 않으며, 패스키는 조직에 비상(break-glass) 계정이 있는 경우에만 사용할 수 있습니다. 트랜잭션을 만들 때 유일한 연결이 이미 시작되거나(`federated_redirect`과 `redirect_url`) 여러 연결이 `sso_connections`에 나열됩니다. 선택 내용은 `sso_start`과 `connection_id` 단계로만 보내십시오. 여기서 `identifier`은 선택 사항입니다. 이 요구 사항이 있으면 탐색은 다른 방식으로 대체되지 않습니다. 기업 로그인에 도달할 수 없으면 단계는 `503 temporarily_unavailable`을 반환합니다. 이메일 도메인이 확인된 도메인에 대해 SSO를 요구하는 조직에 속한 경우에도 같은 방식이 적용됩니다.

ID 공급자 인증 후 브라우저는 로그인 호스트의 `/u/sso/complete`으로 돌아오며, 호스팅 페이지가 로그인을 마무리합니다.

## 기업 로그인을 기존 계정에 연결하기

기업 연결이 이메일 주소를 보증하지 않고, 해당 연결이 주장하는 주소가 환경의 어떤 계정에서 이미 인증되어 있는 경우, 기업 로그인은 세션을 열지 않습니다. 브라우저는 `account_link`을 포함한 새 트랜잭션과 함께 호스팅 로그인 페이지로 돌아옵니다. 해당 연결이 조직에 속하면 `connection_name`과 `organization_name`도 포함됩니다. 사용자에게 이미 계정이 있으며 연결하려면 그 계정으로 로그인해야 한다고 안내하십시오. 계정 이름은 절대 표시하지 마십시오. 이 트랜잭션에서는 `sso_start`과 `magic_link`이 제공되지 않으며, `identifier` 단계는 기업 연결로 라우팅하지 않습니다.

사용자는 기존 계정의 고유 방식으로 로그인합니다. 두 번째 인증 요소가 등록된 사용자에게는 해당 요소를 요청합니다. 트랜잭션을 완료하는 단계는 세션을 열지 않습니다. 연결은 계정에 연결되며, 응답은 같은 연결에 대한 `federated_redirect` 상태와 `redirect_url`을 가진 새 트랜잭션입니다. 최상위 창을 해당 주소로 이동하십시오. 이후 기업 로그인은 평소처럼 마무리됩니다. 기업 로그인을 시작할 수 없으면 새 트랜잭션은 `identifier_pending` 상태가 되고 일반 로그인이 이어집니다.

다른 계정으로 완료하거나, 등록된 두 번째 인증 요소 없이 완료하거나, 연결 요청보다 오래된 로그인으로 완료하면 아무것도 연결되지 않으며 세션도 열리지 않습니다. 이 경우 `409 transaction_state_conflict`을 반환합니다. 만료되었거나(10분) 이미 사용된 연결 요청은 `410 transaction_expired`을 반환합니다. 연결 요청은 결과와 관계없이 한 번만 사용할 수 있습니다. 연결을 거부하려면 `POST /fapi/v1/transactions/current:cancel`을 호출하십시오.

## 조직 정책

인가 요청이 조직에 바인딩된 경우, 조직의 로그인 정책은 첫 번째 요소 이후와 세션을 열기 직전에 다시 확인됩니다.

- 클라이언트의 IP 주소가 조직의 허용 목록에 없습니다: `403 ip_not_allowed`.
- 트랜잭션의 인증 요소가 조직 정책을 충족하지 않습니다(조직이 허용하지 않는 방식, 누락된 요소, 또는 조직의 AAGUID 허용 목록에 없는 보안 키나 패스키): `403 no_allowed_method`.
- 조직 정책을 읽을 수 없습니다: `503 temporarily_unavailable`. 로그인은 안전한 방향(fail closed)으로 실패하며, 정책 없이 계속 진행되지 않습니다.

세션은 열리지 않습니다. 트랜잭션 완료 시 거부되면 트랜잭션 쿠키가 제거됩니다. 첫 번째 요소 이후의 `503`은 트랜잭션을 그대로 두며, 해당 단계는 다시 시도할 수 있습니다. 오류 본문에는 조직, 사용자, IP 주소가 표시되지 않습니다.

## DPoP에 바인딩된 헤드리스 트랜잭션

`DPoP` 헤더로 시작한 트랜잭션의 경우, 쿠키를 사용하지 않고 같은 키로 새 증명(`htm` `POST`, `htu`은 단계 URL)을 `Idp-Csrf-Token` 대신 보내십시오. 트랜잭션을 완료하는 단계는 `authorization_response`을 한 번만 반환합니다.

```json
{
  "state": "completed",
  "authorization_response": { "code": "<code>", "state": "<your state>", "iss": "https://example.keenauth.app" },
  "next_steps": [],
  "schema_version": 1,
  "expires_at": "2026-11-02T10:15:00Z"
}
```

`state`과 `iss`이 일치하는지 확인한 다음, 같은 키로 만든 DPoP 증명과 함께 `/oauth2/token`에서 코드를 `code_verifier`로 교환하십시오. 다른 키의 증명은 `invalid_grant`을 반환합니다. 인가 요청이 코드로 끝나지 못하는 경우(동의, 법적 약관, 조직 선택 또는 새 로그인이 필요하거나 접근이 거부된 경우)에는 단계가 `403 authorization_failed`을 반환하고, `error` 멤버에 OAuth 오류 코드가 담깁니다. 이 경우 코드는 발급되지 않습니다.

## 오류

| 상태 | 코드 | 발생 조건 |
| --- | --- | --- |
| 401 | `invalid_credentials` | 식별자 또는 비밀번호가 허용되지 않습니다. |
| 403 | `origin_not_allowed` | `Origin` 또는 `Idp-Csrf-Token`이 없거나 잘못되었습니다. |
| 403 | `ip_not_allowed` | 클라이언트의 IP 주소가 조직의 허용 목록에 없습니다. |
| 403 | `no_allowed_method` | 로그인이 조직 정책을 충족하지 않습니다. |
| 403 | `test_feature_not_in_production` | `otp_verify` 또는 `email_2fa`이 운영 환경에서 고정 테스트 코드 `424242`를 받았습니다. 시도는 잘못된 코드와 같이 집계됩니다. |
| 404 | `not_found` | 단계 이름이 알려진 단계가 아닙니다. |
| 409 | `transaction_state_conflict` | 현재 상태에서 이 단계를 사용할 수 없습니다. |
| 409 | `session_limit_reached` | 사용자가 세션 한도에 도달했으며 새 세션이 거부됩니다. |
| 410 | `transaction_expired` | 트랜잭션이 종료되었습니다. |
| 422 | `validation_failed` | 필드가 없거나 올바르지 않습니다. |
| 503 | `temporarily_unavailable` | `sso_start`이 기업 로그인에 연결하지 못했거나 조직 정책을 읽을 수 없습니다. 다시 시도하십시오. |
| 401 | `invalid_dpop_proof` | `DPoP` 증명이 없거나 올바르지 않거나 재사용되었습니다. |
| 403 | `authorization_failed` | DPoP에 바인딩된 트랜잭션은 완료되었지만 인가 요청이 코드 없이 끝났습니다. |

## 예시

```bash
curl -X POST https://example.keenauth.app/fapi/v1/transactions/current/steps/identifier \
  -H "Idp-Client: my-app/1.0.0 (node/22.0.0)" \
  -H "Origin: https://example.keenauth.app" \
  -H "Idp-Csrf-Token: $CSRF_TOKEN" \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{"identifier":"jane@example.com"}'
```

## 개인정보

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