
# 로컬 모의 서버

`keenauth mock serve`는 네트워크 접근이 없는 CI 작업과 로컬 개발을 위해 OpenID Connect, SAML 2.0, SCIM 2.0 서버와 `/v1` 관리 API의 메모리 내 버전을 사용자의 컴퓨터에서 실행합니다. KeenAuth 환경, API 키, 인터넷 연결이 필요하지 않습니다. 모든 데이터는 메모리에만 보관되며 프로세스가 종료되면 삭제됩니다.

모의 서버는 KeenAuth가 아닙니다. 아래에 나열된 흐름만 구현하고, 모든 요청을 자동으로 승인하며, 실제 서비스보다 훨씬 적게 검사합니다. **실제 동작의 기준은 호스팅된 `development` 환경뿐입니다.** 릴리스하기 전에 그 환경을 대상으로 엔드투엔드 테스트를 실행하십시오.

## 서버 시작

```sh
keenauth mock serve
```

기본적으로 서버는 `127.0.0.1:4010`에서 수신합니다. 이는 다른 컴퓨터가 접근할 수 없는 루프백 주소입니다. `--port`로 포트를 바꿀 수 있습니다(`0`이면 빈 포트를 고릅니다). `--host`로 주소를 바꿀 수 있으며, 루프백이 아닌 주소를 지정하면 모의 서버가 네트워크에 노출되고 CLI가 경고를 출력합니다. Ctrl+C로 중지합니다.

이 명령은 자격 증명을 읽지 않으며 KeenAuth로 아무것도 보내지 않습니다.

## 엔드포인트

| 경로 | 용도 |
|---|---|
| `/.well-known/openid-configuration` | OIDC 디스커버리 문서(`/.well-known/oauth-authorization-server`에서도 제공) |
| `/.well-known/jwks.json` | 공개 테스트 키 집합(키 ID `keenauth-test-1`) |
| `/oauth2/authorize` | 인가 엔드포인트: PKCE(`S256`)를 사용하는 인가 코드 흐름 |
| `/oauth2/token` | 토큰 엔드포인트: `authorization_code` 및 `refresh_token` grant |
| `/oauth2/userinfo` | UserInfo 엔드포인트(`openid` 스코프가 있는 Bearer 액세스 토큰) |
| `/saml-idp/<application_id>/metadata` | SAML 2.0 아이덴티티 프로바이더 메타데이터 |
| `/saml-idp/<application_id>/sso` | SAML 싱글 사인온 엔드포인트(HTTP-Redirect 및 HTTP-POST 바인딩) |
| `/scim/v2/<directory_id>/…` | SCIM 2.0 엔드포인트: `Users`, `Groups`, `ServiceProviderConfig`, `ResourceTypes`, `Schemas` |
| `/v1/…` | 메모리 내 관리 API: 관리 API의 OpenAPI 문서에 있는 모든 작업 |

디스커버리 문서와 SAML 메타데이터의 엔드포인트 URL은 서버가 수신하는 로컬 주소를 가리킵니다. `<application_id>`와 `<directory_id>`는 원하는 대로 정할 수 있는 이름입니다(`A-Z`, `a-z`, `0-9`, `-`, `_`로 이루어진 1~64자). 미리 등록할 필요는 없습니다.

## 동작 방식

- **동의 화면도, 로그인 페이지도 없습니다.** 유효한 인가 요청은 모두 고정된 가상의 테스트 사용자로 즉시 승인되며, `code`, `state`, `iss`와 함께 리디렉션됩니다. 사용자의 `sub`는 `usr_01m3vnbwg0e8mbe7h7xxp6rmkc`, 이메일 주소는 `test.user@example.test`(인증됨), 이름은 `Test User`입니다.
- **등록된 클라이언트가 없습니다.** 모든 `client_id`를 허용합니다. 클라이언트 시크릿은 검사하지 않습니다. `redirect_uri`는 절대 `https` URI이거나 루프백 호스트(`127.0.0.1`, `[::1]`, `localhost`)의 `http` URI여야 하며, 프래그먼트가 없어야 합니다. 잘못된 `redirect_uri`나 누락된 `client_id`는 리디렉션 대신 오류 페이지를 반환합니다.
- **PKCE가 필수입니다.** `code_challenge_method=S256`과 함께 `code_challenge`를 보내야 합니다. `plain`은 거부됩니다. 토큰 요청에는 일치하는 `code_verifier`를 보내야 합니다.
- **코드는 한 번만 사용할 수 있으며** 60초 후에 만료됩니다. 같은 코드를 두 번째로 제시하면 `invalid_grant`로 실패하고, 그 코드로 발급된 모든 토큰이 취소됩니다.
- **토큰.** 액세스 토큰은 RFC 9068 JWT(`typ` `at+jwt`)이며 한 시간 동안 유효합니다. `sub`, `client_id`, `scope`, 무작위 `jti`를 포함합니다. 대상(audience)은 인가 요청의 `resource` 매개변수이며, 보내지 않았다면 `https://test.keenauth.invalid/oauth2/userinfo`입니다. `openid` 스코프를 지정하면 ID 토큰도 받습니다(`nonce`와 `auth_time`을 포함하며, `email`과 `profile` 스코프가 있으면 `email`과 `name` 클레임도 포함합니다). `offline_access`를 지정하면 리프레시 토큰을 받습니다.
- **리프레시 토큰은 회전합니다.** 갱신할 때마다 새 리프레시 토큰이 반환됩니다. 이전 토큰을 제시하면 실패하며 체인 전체가 취소됩니다. 갱신으로 스코프를 좁힐 수는 있지만 넓힐 수는 없습니다.
- **UserInfo**는 이 프로세스가 발급했고, 만료되거나 취소되지 않았으며, `openid` 스코프를 가진 액세스 토큰만 받아들입니다.

## 토큰과 검증기

토큰은 [오프라인 테스트 키트](/docs/sdk/testing)의 공개 테스트 키로 서명되며, 로컬 주소가 아니라 발급자 `https://test.keenauth.invalid`를 가집니다. 최상위 도메인 `.invalid`는 예약되어 있으므로 운영 환경의 검증기는 이 토큰을 절대 받아들이지 않습니다. SDK 검증기는 테스트 코드에서 명시적으로 켠 테스트 모드에서만 이 토큰을 받아들입니다.

```go
// Go: accept tokens from the mock server in a test.
v, err := keenauthtest.NewVerifier(verify.Config{Audience: "https://test.keenauth.invalid/oauth2/userinfo"})
if err != nil {
	t.Fatal(err)
}
claims, err := v.VerifyAccessToken(ctx, accessToken, nil)
```

TypeScript(`createTestVerifier`)와 Python(`keenauth.testing.verifier`)에서도 같은 테스트 모드를 사용합니다. 디스커버리 문서의 `issuer`가 가져온 URL과 같아야 한다고 요구하는 일반 OIDC 클라이언트 라이브러리는 모의 서버의 디스커버리 문서를 거부합니다. 이 경우 엔드포인트 URL을 직접 설정하거나 `development` 환경을 대상으로 테스트하십시오.

## 예제: curl로 전체 흐름 실행

이 예제에는 `curl`, `openssl`, `jq`가 필요합니다. 먼저 다른 터미널에서 서버를 시작하십시오.

```sh
BASE=http://127.0.0.1:4010
REDIRECT=http://127.0.0.1:8080/callback
VERIFIER=$(openssl rand -hex 32)
CHALLENGE=$(printf '%s' "$VERIFIER" | openssl dgst -sha256 -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=')

# 1. Authorize: the server answers with a redirect that carries the code.
LOCATION=$(curl -s -o /dev/null -w '%{redirect_url}' --get "$BASE/oauth2/authorize" \
  --data-urlencode response_type=code --data-urlencode client_id=app_local \
  --data-urlencode redirect_uri="$REDIRECT" --data-urlencode 'scope=openid email offline_access' \
  --data-urlencode state=xyz --data-urlencode code_challenge="$CHALLENGE" \
  --data-urlencode code_challenge_method=S256)
CODE=$(printf '%s' "$LOCATION" | sed -E 's/.*[?&]code=([^&]+).*/\1/')

# 2. Exchange the code for tokens.
TOKENS=$(curl -s "$BASE/oauth2/token" -d grant_type=authorization_code -d client_id=app_local \
  --data-urlencode code="$CODE" --data-urlencode redirect_uri="$REDIRECT" -d code_verifier="$VERIFIER")

# 3. Call UserInfo with the access token.
curl -s "$BASE/oauth2/userinfo" -H "Authorization: Bearer $(printf '%s' "$TOKENS" | jq -r .access_token)"

# 4. Refresh.
curl -s "$BASE/oauth2/token" -d grant_type=refresh_token -d client_id=app_local \
  --data-urlencode refresh_token="$(printf '%s' "$TOKENS" | jq -r .refresh_token)"
```

3단계는 테스트 사용자를 출력합니다:

```json
{"email":"test.user@example.test","email_verified":true,"sub":"usr_01m3vnbwg0e8mbe7h7xxp6rmkc"}
```

## SAML 아이덴티티 프로바이더

개발 중인 SAML 서비스 프로바이더의 아이덴티티 프로바이더로 모의 서버를 사용할 수 있습니다. 서비스 프로바이더가 메타데이터 URL을 가리키게 하고 로그인하면, 모의 서버가 테스트 사용자에 대한 서명된 응답을 돌려줍니다. 경로는 KeenAuth가 이후 릴리스에서 계획하고 있는 호스팅 SAML 아이덴티티 프로바이더를 따릅니다.

- **메타데이터.** `GET /saml-idp/<application_id>/metadata`는 엔티티 ID `https://test.keenauth.invalid/saml-idp/<application_id>`, 서명 인증서, `persistent` 및 `emailAddress` NameID 형식, 두 바인딩용 SSO 엔드포인트를 담은 `EntityDescriptor`를 반환합니다. 서명된 AuthnRequest는 요구되지 않습니다(`WantAuthnRequestsSigned="false"`). 요청의 서명은 무시됩니다.
- **SSO.** AuthnRequest는 HTTP-Redirect 바인딩(`GET`, `SAMLRequest`에 DEFLATE + base64) 또는 HTTP-POST 바인딩(`POST`, base64)으로 보냅니다. 로그인 페이지는 없습니다. 유효한 요청은 즉시 테스트 사용자로 승인되며, `SAMLResponse`(와 `RelayState`, 최대 80바이트)를 서비스 프로바이더의 ACS URL로 스스로 전송하는 HTML 페이지를 받습니다.
- **AuthnRequest에 반드시 포함해야 하는 항목.** `ID`, `Version="2.0"`, `IssueInstant`, `saml:Issuer`(서비스 프로바이더의 엔티티 ID), `AssertionConsumerServiceURL`. 모의 서버에는 등록된 서비스 프로바이더가 없기 때문입니다. ACS URL은 절대 `https` URL이거나 루프백 호스트의 `http` URL이어야 하며, 프래그먼트가 없어야 합니다. `ProtocolBinding`이 있다면 HTTP-POST여야 하고, `Destination`이 있다면 SSO URL이어야 합니다. 그 밖의 요청은 `400`을 받으며 어디로도 전송되지 않습니다. DTD는 거부됩니다.
- **응답.** RSA-SHA256과 배타적 정규화로 두 번 서명됩니다. 먼저 어설션, 그다음 응답 전체입니다. `InResponseTo`는 요청의 `ID`, `Destination`과 주체 확인의 `Recipient`는 ACS URL, `Audience`는 요청의 `Issuer`입니다. 어설션은 발급 시점부터 5분 동안 유효합니다. NameID는 요청이 `emailAddress`를 요구하지 않는 한 `persistent`(애플리케이션마다 안정적인 값)입니다. 속성은 `email`, `given_name`, `family_name`, `name`입니다.

**서명 키는 공개되어 있습니다.** 공개된 레이블에서 파생되므로, 누구나 이 인증서로 검증되는 응답에 서명할 수 있습니다. SHA-256 지문은 `fcc2fc37b9ddb800721283156f9eb26ff1b80f099d996a65650f8cb469a9be81`이고, 주체에는 `TEST ONLY`라고 적혀 있습니다. 서비스 프로바이더의 개발 및 테스트 설정에만 등록하고, 운영 환경에는 절대 등록하지 마십시오. KeenAuth는 자체 SAML 연결에서 이를 강제합니다. 스테이징과 운영 환경에서는 메타데이터를 가져오거나 인증서를 추가할 때, 그리고 로그인할 때 이 키와 `.invalid` 아래의 모든 엔티티 ID를 거부합니다. 개발 환경은 이를 허용합니다.

### 예제: curl로 SAML 로그인

이 예제에는 `curl`과 `openssl`이 필요합니다. 먼저 다른 터미널에서 서버를 시작하십시오.

```sh
BASE=http://127.0.0.1:4010
APP=app_local
ACS=http://127.0.0.1:8080/saml/acs

# 1. IdP metadata: entity ID, signing certificate and SSO URL for your service provider.
curl -s "$BASE/saml-idp/$APP/metadata" -o idp-metadata.xml

# 2. Send an AuthnRequest with the HTTP-POST binding (base64, no compression).
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
REQUEST=$(printf '<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_req1" Version="2.0" IssueInstant="%s" AssertionConsumerServiceURL="%s"><saml:Issuer>https://sp.example.com</saml:Issuer></samlp:AuthnRequest>' "$NOW" "$ACS" | openssl base64 -A)
curl -s "$BASE/saml-idp/$APP/sso" --data-urlencode SAMLRequest="$REQUEST" --data-urlencode RelayState=xyz -o form.html

# 3. The page posts SAMLResponse to the ACS URL by itself; decode it to look at the signed response.
sed -n 's/.*name="SAMLResponse" value="\([^"]*\)".*/\1/p' form.html | openssl base64 -d -A > response.xml
grep -o 'InResponseTo="[^"]*"' response.xml | head -1
```

마지막 명령은 `InResponseTo="_req1"`을 출력합니다. 서비스 프로바이더는 `idp-metadata.xml`의 인증서로 `response.xml`을 검증합니다.

## SCIM 2.0 엔드포인트

SCIM 엔드포인트는 프로비저닝 커넥터나 사용자와 그룹을 만드는 스크립트처럼, SCIM 클라이언트를 네트워크 없이 테스트하는 데 사용합니다. 각 `<directory_id>`는 프로세스가 살아 있는 동안 유지되는, 별도의 빈 디렉터리입니다.

- **인증.** 모든 요청은 `Authorization: Bearer keenauth-mock-scim-token`이 필요합니다. 그 밖의 요청은 `WWW-Authenticate: Bearer` 질의와 함께 `401`을 받습니다. 값은 고정되어 있고 공개되어 있으며, KeenAuth의 SCIM 토큰이 아닙니다.
- **사용자와 그룹.** `/Users`와 `/Groups`에 대한 `POST`, `GET`, `PUT`, `PATCH`, `DELETE`(RFC 7644)입니다. `userName`과 그룹의 `displayName`은 필수이며, 대소문자를 구분하지 않고 디렉터리 안에서 고유해야 합니다. 중복되면 `scimType` `uniqueness`와 함께 `409`를 받습니다. 그룹 구성원은 같은 디렉터리의 사용자여야 합니다. 사용자를 삭제하면 그 사용자는 그룹에서도 제거됩니다. `password`는 받아들여지지만 버려집니다.
- **필터와 페이지.** `filter`는 `and`로 연결된 `eq`, `ne`, `co`, `sw`, `ew`, `pr`을 지원합니다(예: `userName eq "ada@example.test"`). 또한 `startIndex`와 `count`(최대 200)를 지원합니다. `attributes`와 `excludedAttributes`는 최상위 속성을 선택합니다. `POST /Users/.search`와 `/Groups/.search`는 같은 매개변수를 본문으로 받습니다.
- **PATCH**는 `active`, `name.givenName`, `emails[type eq "work"].value`, `members[value eq "<id>"]`, 엔터프라이즈 확장 속성 같은 경로와 함께 `add`, `remove`, `replace`를 지원합니다. 한 요청의 작업들은 모두 적용되거나 전혀 적용되지 않습니다.
- **버전.** 모든 리소스는 `meta.version`과 `ETag` 헤더(`W/"1"`, `W/"2"`, …)를 가집니다. `PUT`, `PATCH`, `DELETE`의 `If-Match`는 버전이 바뀌었으면 `412`를 반환합니다. `GET`의 `If-None-Match`는 `304`를 반환합니다.
- **디스커버리.** `ServiceProviderConfig`, `ResourceTypes`, `Schemas`는 정확히 이 동작을 설명합니다. `/Bulk`와 `/Me`는 KeenAuth와 마찬가지로 `501`을 반환합니다.

### 예제: curl로 사용자 프로비저닝

이 예제에는 `curl`과 `jq`가 필요합니다. 먼저 다른 터미널에서 서버를 시작하십시오.

```sh
SCIM=http://127.0.0.1:4010/scim/v2/dir_local
AUTH='Authorization: Bearer keenauth-mock-scim-token'
CT='Content-Type: application/scim+json'

# 1. Create a user; the response carries the id, meta.version and an ETag header.
USER_ID=$(curl -s "$SCIM/Users" -H "$AUTH" -H "$CT" -d '{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "userName": "ada@example.test",
  "name": {"givenName": "Ada", "familyName": "Lovelace"},
  "emails": [{"value": "ada@example.test", "type": "work", "primary": true}]
}' | jq -r .id)

# 2. Find it with a filter.
curl -s -G "$SCIM/Users" -H "$AUTH" --data-urlencode 'filter=userName eq "ada@example.test"' | jq .totalResults

# 3. Deactivate it with PATCH, guarded by the version you read.
curl -s -X PATCH "$SCIM/Users/$USER_ID" -H "$AUTH" -H "$CT" -H 'If-Match: W/"1"' -d '{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
  "Operations": [{"op": "replace", "path": "active", "value": false}]
}' | jq -c '{active, version: .meta.version}'

# 4. Create a group with the user as a member, then delete the user.
curl -s "$SCIM/Groups" -H "$AUTH" -H "$CT" -d '{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
  "displayName": "Engineering",
  "members": [{"value": "'"$USER_ID"'"}]
}' | jq -c '.members[0] | {display, type}'
curl -s -o /dev/null -w '%{http_code}\n' -X DELETE "$SCIM/Users/$USER_ID" -H "$AUTH"
```

명령은 `1`, `{"active":false,"version":"W/\"2\""}`, `{"display":"ada@example.test","type":"User"}`, `204`를 출력합니다.

## 관리 API (`/v1`)

관리 API 엔드포인트는 KeenAuth 관리 API를 호출하는 코드(SDK, CLI, 배포 스크립트)를 네트워크 없이 CI에서 테스트할 때 사용합니다. 경로는 호스팅된 것과 같습니다. 호스팅된 기본 URL 대신 `http://127.0.0.1:4010/v1`을 사용하면 나머지는 그대로입니다. 엔드포인트는 CLI에 내장된 관리 API의 OpenAPI 문서에서 생성되므로, 해당 버전 API의 모든 작업이 응답합니다.

- **인증.** 모든 요청은 `Authorization: Bearer keenauth-mock-management-token`이 필요합니다. 그 밖의 요청은 알 수 없는 경로에서도 `WWW-Authenticate: Bearer` 질의와 함께 `401`을 받습니다. 값은 고정되어 있고 공개되어 있으며, KeenAuth 관리 키가 아닙니다.
- **응답.** 각 작업은 문서가 선언한 성공 상태(`200`, `201`, `202`, `204`; 두 다운로드 작업은 해석되지 않는 `Location`과 함께 `302`로 응답합니다)로 응답합니다. 본문은 문서의 성공 응답 `example`이며, 아직 없다면 응답 스키마로부터 만든 가장 작은 유효한 예제입니다. 필수 필드만 포함하고, null 허용 필드는 `null`, 열거형은 첫 번째 값, 문자열은 `pattern`에 맞는 값을 사용합니다.
- **단순한 상태.** 레코드를 만들 수 있는 컬렉션(`POST /v1/users`, `/v1/organizations`, `/v1/applications`, `/v1/organizations/<org_id>/roles` 등)은 레코드를 보관합니다. 만든 레코드는 자신의 경로와 목록의 `GET`으로 반환되고, `PATCH`와 `PUT`으로 바뀌며, `DELETE`는 하위 항목과 함께 제거합니다. 존재하지 않는 레코드 아래의 경로는 `404`입니다. 식별자는 KeenAuth 접두사 형식(`usr_…`, `org_…`, `app_…`)을 따르며 문서의 패턴과 일치합니다. 문서에 없으면 접두사는 매개변수 이름에서 유도됩니다. 환경마다 한 번만 존재하는 설정(예: `/v1/identity-settings`)은 `PATCH`나 `PUT`으로 쓴 내용을 유지합니다. API로 만들 수 없는 리소스(세션, 보고서 등)는 항상 예제로 응답합니다.
- **목록.** `limit`(1~100, 기본값 20), 커서로 레코드 식별자를 쓰는 `after` 또는 `before`, `order`(기본값 `desc`, 최신순)가 동작합니다. 응답에는 `list_metadata`가 포함됩니다. `status` 같은 필터는 적용되지 않습니다.
- **오류**는 문서와 같이 `application/problem+json`이며, `code`와 `Idp-Request-Id` 헤더에도 들어 있는 `request_id`를 포함합니다. 알 수 없는 경로는 `404 not_found`, 경로에 없는 메서드는 `Allow` 헤더와 함께 `405 method_not_allowed`, 유효한 JSON(또는 객체)이 아닌 본문은 `400 malformed_request`, 목록에 있는 작업에서 JSON이 아닌 `Content-Type`은 `415`, 필수 필드 누락은 `errors[]`와 함께 `422 validation_failed`, 1 MiB를 넘는 본문은 `413`, 잘못된 `limit`이나 커서는 `400`입니다.

**`development` 환경과의 차이.** 모의 서버는 본문이 필수 필드를 가진 JSON 객체인지만 검사합니다. 필드 형식, 길이, 열거값, 고유성은 검사하지 않으며, 잘못된 타입의 필드는 무시됩니다. `If-Match`, `Idempotency-Key`와 그 밖의 헤더는 무시하고 `ETag`를 반환하지 않습니다. 권한, 요금제 한도, 할당량, 속도 제한, 리전은 검사하지 않고, 개인 데이터를 가리지 않으며, 이메일을 보내지 않고, 작업을 실행하지 않습니다(접수된 작업은 생성된 그대로 남습니다). 환경은 하나뿐입니다. 레코드는 프로세스가 사는 동안만 존재하며 최대 10,000개입니다. 실제 API의 동작을 알아보려면 `development` 환경을 사용하십시오.

### 예제: curl로 만들기, 읽기, 변경, 삭제

이 예제에는 `curl`과 `jq`가 필요합니다. 먼저 다른 터미널에서 서버를 시작하십시오.

```sh
API=http://127.0.0.1:4010/v1
AUTH='Authorization: Bearer keenauth-mock-management-token'
CT='Content-Type: application/json'

# 1. Create an organization.
ORG_ID=$(curl -s "$API/organizations" -H "$AUTH" -H "$CT" -d '{"name": "Acme"}' | jq -r .id)

# 2. Read it back and list.
curl -s "$API/organizations/$ORG_ID" -H "$AUTH" | jq -c '{id: (.id | .[0:4]), name}'
curl -s "$API/organizations" -H "$AUTH" | jq -c '{count: (.data | length), list_metadata}'

# 3. Rename it with a merge patch.
curl -s -X PATCH "$API/organizations/$ORG_ID" -H "$AUTH" -H 'Content-Type: application/merge-patch+json' -d '{"name": "Acme Inc"}' | jq -r .name

# 4. Delete it; a second read is a 404 problem.
curl -s -o /dev/null -w '%{http_code}\n' -X DELETE "$API/organizations/$ORG_ID" -H "$AUTH"
curl -s "$API/organizations/$ORG_ID" -H "$AUTH" | jq -c '{status, code}'

# 5. Without the token.
curl -s "$API/organizations" | jq -c '{status, code}'
```

명령은 `{"id":"org_","name":"Acme"}`, `{"count":1,"list_metadata":{"after":null,"before":null}}`, `Acme Inc`, `204`, `{"status":404,"code":"not_found"}`, `{"status":401,"code":"unauthenticated"}`를 출력합니다.

## 지원하지 않는 기능

테스트 클록, DPoP, PAR, 요청 객체, 디바이스 흐름, 클라이언트 자격 증명, 토큰 교환, 로그아웃이나 취소, 호스팅 페이지는 없습니다. SAML 아이덴티티 프로바이더에는 싱글 로그아웃, 암호화된 어설션, 아티팩트 바인딩, IdP 시작 로그인이 없습니다. SCIM 엔드포인트에는 대량 작업, 정렬, `/Me`, 중첩 그룹이 없으며, 필터는 `or`, `not`, 괄호, `gt`/`ge`/`lt`/`le`를 지원하지 않습니다. 이것들은 `development` 환경에서 테스트하십시오.
