
# 애플리케이션 생성

`POST /v1/applications` · 권한 `applications:write`

호출 키가 속한 환경에 애플리케이션(OAuth/OIDC 클라이언트)을 생성합니다. `key`, `name`, `app_type`, `party`는 필수이며, 그 밖의 필드에는 애플리케이션 유형에서 파생된 안전한 기본값이 적용됩니다. 응답에는 `ETag`가 포함되며, 수정하거나 삭제할 때 이 값을 `If-Match`로 보내야 합니다. 이 작업은 클라이언트 비밀을 생성하지 않습니다. 비밀과 키는 애플리케이션 자격 증명 엔드포인트에서 관리합니다.

## 요청

**본문** (`application/json`). 알 수 없는 필드는 거부됩니다. 필수 필드는 `key`, `name`, `app_type`, `party`뿐입니다.

| 필드 | 유형 | 필수 | 설명 |
|---|---|---|---|
| `key` | string | 예 | 안정적인 구성 키이며, `^[a-z0-9_-]{1,64}$` 형식이고 환경 내에서 고유해야 합니다. 생성 시 필수이며, 이후에는 변경할 수 없습니다. |
| `name` | string | 예 | 표시 이름이며, 1~100자입니다. |
| `app_type` | string | 예 | `web`, `spa`, `native`, `m2m`, `device` 또는 `management`입니다. 생성 시 필수이며, 이후에는 변경할 수 없습니다. |
| `party` | string | 예 | `first_party` 또는 `third_party`입니다. 생성 시 필수이며, `first_party`에서 `third_party`로만 변경할 수 있습니다. |
| `owner_org_id` | string 또는 null | 아니요 | 소유 조직의 `org_…` ID입니다. `m2m` 애플리케이션에만 사용하며, 변경할 수 없습니다. |
| `status` | string | 아니요 | `active` 또는 `suspended`입니다. 일시 중지된 애플리케이션은 새 토큰을 발급받을 수 없습니다. |
| `allowed_web_origins` | string[] | 아니요 | 허용되는 CORS 출처(`https://host[:port]`)이며, 최대 20개이고 와일드카드는 사용할 수 없습니다. |
| `organization_usage` | string | 아니요 | `deny`, `allow` 또는 `require`입니다. `organization` 인가 매개변수(`org_…` ID 또는 조직의 슬러그)를 제어합니다. `deny`는 이를 거부하고, `allow`는 선택 사항으로 만들며, `require`는 필수로 만듭니다. 토큰에는 사용자의 멤버십이 확인된 후에만 `org_id`가 포함됩니다. |
| `org_access_mode` | string | 아니요 | 조직 로그인 접근 방식입니다. `all_organizations`(기본값, 모든 조직의 모든 멤버), `enabled_organizations`(애플리케이션이 활성화된 조직만) 또는 `assigned_only`(추가로 할당된 멤버십 또는 디렉터리 그룹만)입니다. 자세한 내용은 [조직 애플리케이션](../organization-applications/get.md)을 참조하십시오. |
| `registration_mode` | string 또는 null | 아니요 | `open`, `invite_only` 또는 `closed`입니다. 비워 두면 환경 설정을 상속합니다. |
| `registration_source` | string | 아니요 | 생성 시 `dashboard` 또는 `management_api`입니다(기본값 `management_api`). 변경할 수 없습니다. 응답에는 `dcr` 또는 `cimd`도 표시될 수 있습니다. |
| `verification_status` | string | 아니요 | `verified` 또는 `unverified`입니다. |
| `security_profile` | string | 아니요 | `baseline`입니다(현재 쓰기 가능한 유일한 프로필). |
| `token_endpoint_auth_method` | string | 아니요 | `private_key_jwt`, `client_secret_basic`, `client_secret_post` 또는 `none`이며, `app_type`에서 허용되어야 합니다. |
| `grant_types` | string[] | 아니요 | 허용되는 권한 부여 유형이며, `app_type`이 허용하는 유형의 부분 집합이어야 합니다. |
| `default_api_id` | string 또는 null | 아니요 | 요청에 `resource`가 없을 때 사용하는 API의 `api_…` ID입니다. |
| `require_par` | boolean | 아니요 | 푸시된 인가 요청(PAR)을 필수로 합니다. |
| `require_state` | boolean | 아니요 | `state`를 필수로 합니다. |
| `require_nonce` | boolean | 아니요 | `nonce`를 필수로 합니다. |
| `pkce_required` | boolean | 아니요 | PKCE를 필수로 합니다. 기밀 웹 애플리케이션에 한해서만 비활성화할 수 있습니다. PKCE가 없으면 `nonce`만이 인가 코드 주입에 대한 방어 수단이 되므로, 개발 환경이 아닌 경우 `require_nonce`를 켜 두어야 합니다(그렇지 않으면 `/require_nonce`에서 `validation_failed`가 발생합니다). |
| `skip_consent` | boolean | 아니요 | 동의 화면을 건너뜁니다. 자사(first-party) 애플리케이션에만 적용됩니다. |
| `token_binding_policy` | string | 아니요 | `none` 또는 `dpop_required`입니다. |
| `login_host` | string 또는 null | 아니요 | 환경의 로그인 호스트 중 하나입니다(소문자 DNS 이름). |
| `default_max_age_s` | integer 또는 null | 아니요 | 초 단위의 기본 `max_age`입니다. |
| `min_acr` | string 또는 null | 아니요 | 최소 인증 보증 수준입니다. `urn:idp:acr:1fa`, `urn:idp:acr:mfa` 또는 `urn:idp:acr:phr`입니다. |
| `access_token_lifetime_s` | integer 또는 null | 아니요 | 액세스 토큰 수명이며, 60~86400초입니다. |
| `refresh_idle_s` | integer 또는 null | 아니요 | 초 단위의 리프레시 토큰 유휴 수명입니다. `refresh_absolute_s`를 초과할 수 없습니다. |
| `refresh_absolute_s` | integer 또는 null | 아니요 | 초 단위의 리프레시 토큰 절대 수명입니다. |
| `refresh_reuse_interval_s` | integer | 아니요 | 리프레시 토큰 재사용 허용 구간이며, 0~30초입니다(기본값 5). |
| `backchannel_logout_uri` | string 또는 null | 아니요 | 백채널 로그아웃 URI(`https`)입니다. |
| `frontchannel_logout_uri` | string 또는 null | 아니요 | 프런트채널 로그아웃 URI(`https`)입니다. |
| `frontchannel_logout_session_required` | boolean | 아니요 | 프런트채널 로그아웃 URI에 `iss`와 `sid`를 추가합니다. 환경의 프런트채널 로그아웃 설정이 꺼져 있는 상태에서 `true`로 설정하면 422가 반환됩니다. 기본값은 `false`입니다. |
| `initiate_login_uri` | string | 아니요 | OIDC 로그인 시작 URI입니다(OpenID Connect Core 1.0 §4). IdP가 시작한 SAML 로그인 후 브라우저는 `iss`와 함께 이 URI로 이동하며, 애플리케이션은 `state`와 PKCE를 사용해 자체 인가 코드 흐름을 시작합니다. 소문자 DNS 호스트를 가진 절대 `https` URL이어야 하며, 프래그먼트나 사용자 정보가 없어야 하고, 최대 2048바이트입니다. `m2m` 및 `management` 애플리케이션에는 허용되지 않습니다. 이 URI가 있는 애플리케이션만 IdP가 시작한 로그인의 대상이 될 수 있습니다. |
| `id_token_org_claims` | boolean | 아니요 | `true`(기본값)이면 조직 로그인의 ID 토큰에 `org_id`, `org_slug`, `org_roles`가 포함됩니다. `false`이면 ID 토큰에 조직 클레임이 포함되지 않습니다. 액세스 토큰에는 영향이 없으며, ID 토큰에는 권한이 포함되지 않습니다. |
| `policy_uri` | string 또는 null | 아니요 | 개인정보 처리방침 URI(`https`)이며, 동의 화면에 표시됩니다. |
| `tos_uri` | string 또는 null | 아니요 | 서비스 이용약관 URI(`https`)입니다. |
| `client_uri` | string 또는 null | 아니요 | 애플리케이션 홈페이지 URI(`https`)입니다. |
| `jwks` | object 또는 null | 아니요 | 인라인 JWK 세트(`{"keys":[…]}`)입니다. 공개 키 1~5개, 최대 16 KiB이며, 비공개 키 멤버는 거부됩니다. `jwks_uri`와 함께 사용할 수 없습니다. |
| `jwks_uri` | string 또는 null | 아니요 | 원격 JWK 세트 URI(`https`)입니다. `jwks`와 함께 사용할 수 없습니다. |
| `software_id` | string 또는 null | 아니요 | DCR 애플리케이션에만 사용합니다. |
| `software_version` | string 또는 null | 아니요 | DCR 애플리케이션에만 사용합니다. |
| `m2m_token_lifetime_s` | integer 또는 null | 아니요 | `m2m` 애플리케이션의 토큰 수명이며, 60~86400초입니다. |
| `redirect_uris` | string[] | 아니요 | 허용되는 리디렉션 URI이며, 최대 50개입니다. `m2m` 및 `management`에서는 비어 있어야 합니다. |
| `post_logout_redirect_uris` | string[] | 아니요 | 허용되는 로그아웃 후 리디렉션 URI이며, 최대 50개입니다. |
| `connection_ids` | string[] | 아니요 | 애플리케이션에서 활성화된 연결(`conn_…`)이며, 순서가 있습니다. 이 순서가 버튼 표시 순서가 됩니다. 최대 100개입니다. |
| `allowed_first_factors` | string[] | 아니요 | 애플리케이션에서 허용되는 1차 인증 요소입니다. `password`, `passkey`, `email_otp`, `magic_link`, `sms_otp`, `social`, `enterprise` 중에서 선택하며, 중복은 허용되지 않습니다. 이 순서대로 반환됩니다. 빈 목록(기본값)은 애플리케이션에 추가 제한이 없음을 의미합니다. 이 집합은 환경의 `allowed_first_factors`와 교차 적용되며, 사용 가능한 방법이 남지 않으면 `422 policy_conflict`가 반환됩니다. |
| `allowed_response_modes` | string[] | 아니요 | 인가 요청과 푸시된 인가 요청에서 애플리케이션이 사용할 수 있는 `response_mode` 값입니다. `query`, `form_post` 중 하나 또는 둘 다를 중복 없이 지정할 수 있으며, 이 순서대로 반환됩니다. 기본값은 `["query"]`입니다. 폼 POST로 인가 응답을 받는 애플리케이션은 `form_post`를 명시적으로 추가해야 합니다. 목록에 없는 `response_mode`를 사용하는 인가 요청은 거부되며, `response_mode`가 없는 요청은 `query`를 사용합니다. 빈 목록을 지정하면 422가 반환됩니다. JARM 모드(`jwt`, `query.jwt`, `fragment.jwt`, `form_post.jwt`)는 아직 지원되지 않습니다. 동적 클라이언트 등록 또는 클라이언트 ID 메타데이터 문서를 통해 등록된 애플리케이션은 `["query"]`를 받습니다. |

## 응답

애플리케이션과 `ETag` 헤더를 포함한 `201 Created`가 반환됩니다.

```json
{
  "id": "app_6f8d0cjgbmf0gtn2rg6pscp3cg",
  "client_id": "app_6f8d0cjgbmf0gtn2rg6pscp3cg",
  "version": 1,
  "created_at": "2026-10-05T10:00:01Z",
  "updated_at": "2026-10-05T10:00:01Z",
  "key": "portal",
  "name": "Portal",
  "app_type": "web",
  "party": "first_party",
  "status": "active",
  "redirect_uris": ["https://portal.example.com/cb"],
  "...": "all other fields from the field reference"
}
```

## 오류

| HTTP | `code` | 발생 조건 |
|---|---|---|
| 401 | `unauthenticated` | 키가 없거나, 잘못되었거나, 취소되었습니다. |
| 403 | `insufficient_permissions` | 키에 필요한 권한이 없습니다. |
| 409 | `conflict` | 같은 `key`를 가진 애플리케이션이 이미 있습니다. |
| 422 | `validation_failed` | 필드가 잘못되었습니다. `errors[].pointer`가 해당 필드를 가리킵니다(예: `/redirect_uris/0`). |
| 422 | `policy_conflict` | `allowed_first_factors`에 환경의 1차 인증 요소 집합과 공통되는 방법이 없습니다. |
| 400 | `unknown_field` | 본문에 스키마에 없는 필드가 있습니다. |
| 403 | `quota_exceeded` | 애플리케이션에 대한 요금제 한도에 도달했습니다. |

## 예시

```bash
curl -X POST https://api.eu.keenauth.com/v1/applications \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"key":"portal","name":"Portal","app_type":"web","party":"first_party","redirect_uris":["https://portal.example.com/cb"]}'
```

## 응답 필드

`id`와 `client_id`는 애플리케이션 ID(`app_…`)입니다. `version`은 `ETag`로도 반환됩니다. `created_at`, `updated_at`, `last_token_issued_at`, `last_token_asn`은 읽기 전용입니다. 위에 나열된 쓰기 가능한 필드는 모두 반환됩니다. 클라이언트 비밀은 이 리소스에 포함되지 않습니다.
