
# Prévisualiser une politique d'organisation

`POST /v1/organizations/{org_id}/policies/{policy_kind}:preview` · autorisation `organizations:write`

Évalue un objet de paramètres candidat sans l'enregistrer. Le corps est le même que pour [créer ou remplacer une politique](/docs/api/management/organization-policies/put), et le serveur le vérifie avec les mêmes règles et le même contrôle de forfait ; un candidat que `PUT` rejetterait est donc rejeté ici avec le même code de problème. Rien n'est écrit : ni politique, ni entrée d'audit, ni événement. La requête ne prend ni `If-Match` ni `Idempotency-Key`, et la réponse ne contient aucun `ETag`.

La réponse compte les utilisateurs auxquels le candidat s'applique (`basis` `users_in_scope`), et non les utilisateurs qui seraient bloqués, avec une exception : `phishing_resistant` (voir ci-dessous). Les conditions connues seulement à la connexion, ou que le service de politique ne détient pas, ne sont pas filtrées ; `not_evaluated` les liste afin que le nombre ne soit jamais lu comme plus élevé qu'il ne l'est.

| Type | Utilisateurs comptés | `not_evaluated` |
|---|---|---|
| `authentication_methods` | Membres actifs de l'organisation | `credentials` |
| `mfa` (`required_for` `admins` ou `all`) | Membres actifs de l'organisation | `mfa_enrollment` ; aussi `admin_scope` pour `admins` |
| `phishing_resistant` (`required_for` `admins` ou `all`) | Membres actifs de l'organisation sans identifiant WebAuthn accepté (`basis` `users_without_webauthn`, voir ci-dessous) | `credential_policy` ; aussi `admin_scope` pour `admins` |
| `sso_enforcement` `org_context` | Membres actifs de l'organisation, moins `bypass_user_ids` | `federated_sign_in` |
| `sso_enforcement` `verified_domains` | Membres actifs, plus utilisateurs actifs de l'environnement disposant d'une adresse e-mail sur un domaine vérifié de cette organisation, moins `bypass_user_ids` | `federated_sign_in` |
| `session` | Membres actifs de l'organisation | aucun |
| `ip_allowlist` | Membres actifs de l'organisation | `client_ip` ; aussi `admin_scope` pour `admins` |

Pour `phishing_resistant`, le nombre et l'échantillon désignent les utilisateurs concernés qui ne peuvent pas aujourd'hui se connecter de manière résistante au hameçonnage : utilisateurs sans identifiant WebAuthn actif (une clé d'accès, ou une clé de sécurité utilisée comme premier facteur) que la liste d'AAGUID accepte. La liste est celle du candidat, ou celle de l'environnement lorsque celle du candidat est vide ; avec une liste, un identifiant ne compte que si son modèle y figure et si son attestation a été vérifiée. Les identifiants en attente, révoqués ou suspectés de clonage ne comptent pas. `credential_policy` signifie que l'exigence d'attestation de l'environnement, la règle de passkey liée à l'appareil et le statut du modèle d'authentificateur ne sont pas appliqués ; le nombre peut donc être inférieur au nombre d'utilisateurs qui seraient bloqués. Lorsque le périmètre compte plus de 10 000 utilisateurs, la réponse bascule sur `users_in_scope` avec `passkey_enrollment`. Si les données d'identifiants ne peuvent pas être lues, la requête échoue avec `503` et aucun nombre n'est renvoyé.

`required_for` `none` et `mode` `off` ne comptent personne. Un membre actif est un membre dont l'adhésion est active et dont l'utilisateur n'est pas bloqué ; chaque utilisateur n'est compté qu'une fois. `admin_scope` signifie que `admins` est compté comme l'ensemble des membres, comme l'applique aujourd'hui la connexion. Les utilisateurs d'une autre organisation ou d'un autre environnement ne sont jamais comptés.

## Requête

| Champ | Type | Obligatoire | Description |
|---|---|---|---|
| `org_id` | path | oui | Identifiant de l'organisation (`org_…`). |
| `policy_kind` | path | oui | Type de politique : `authentication_methods`, `mfa`, `phishing_resistant`, `sso_enforcement`, `session` ou `ip_allowlist`. Toute autre valeur renvoie `404`. |
| `settings` | object | oui | L'objet de paramètres candidat du type, avec les mêmes champs que pour `PUT`. |

Corps de la requête :

```json
{
  "settings": {
    "required_for": "all",
    "allowed_factors": [
      "totp",
      "webauthn"
    ],
    "enrollment_grace_days": 7
  }
}
```

## Réponse

`200 OK`. `sample` contient au plus 20 utilisateurs de l'ensemble compté, triés par identifiant, avec l'identifiant utilisateur uniquement.

| Champ | Type | Description |
|---|---|---|
| `organization_id` | string | Identifiant de l'organisation (`org_…`). |
| `policy_kind` | string | Le type prévisualisé. |
| `basis` | string | Ce que `affected_count` compte : `users_in_scope` ou, pour `phishing_resistant`, `users_without_webauthn`. De nouvelles valeurs peuvent être ajoutées. |
| `affected_count` | integer | Nombre d'utilisateurs que `basis` compte. |
| `sample` | array | Au plus 20 objets avec `user_id` (`usr_…`). |
| `not_evaluated` | array | Conditions que le comptage ne filtre pas : `admin_scope`, `credentials`, `mfa_enrollment`, `passkey_enrollment`, `federated_sign_in`, `client_ip`, `credential_policy`. De nouvelles valeurs peuvent être ajoutées. |

```json
{
  "organization_id": "org_01j9xk3m7q8r2s4t6v8w0y2z4a",
  "policy_kind": "mfa",
  "basis": "users_in_scope",
  "affected_count": 2,
  "sample": [
    {
      "user_id": "usr_01j9xk3m7q8r2s4t6v8w0y2z4b"
    },
    {
      "user_id": "usr_01j9xk3m7q8r2s4t6v8w0y2z4c"
    }
  ],
  "not_evaluated": [
    "mfa_enrollment"
  ]
}
```

## Erreurs

| HTTP | code | Quand |
|---|---|---|
| 400 | `unknown_field` | Le corps contient un champ absent du schéma. |
| 403 | `plan_feature_unavailable` | Le type ou la liste d'AAGUID exige un forfait supérieur ; le problème contient `feature` et `required_plan`. |
| 404 | `not_found` | L'organisation n'existe pas ou le type est inconnu. |
| 409 | `domain_not_verified` | `sso_enforcement` avec `mode` `verified_domains` nécessite un domaine vérifié sur l'organisation. |
| 409 | `connection_test_required` | Activer `sso_enforcement` (depuis `off` ou depuis l'absence de politique) nécessite une connexion de test réussie avec la configuration actuelle d'au moins un des `connection_ids` au cours des dernières 24 heures. Lancez un test de connexion (`POST /v1/connections/{connection_id}:test`) puis réessayez. |
| 409 | `organization_pending_deletion` | L'organisation attend une suppression en libre-service. |
| 422 | `validation_failed` | Un champ de paramètres est inconnu ou invalide ; `errors[]` pointe vers `/settings/…`. |
| 422 | `policy_weaker_than_parent` | Le paramètre est plus faible que celui de l'environnement ; `errors[]` pointe vers le champ plus faible. |
| 422 | `at_least_one_method` | `authentication_methods` ne laisse aucune méthode de connexion. |
| 401 | `unauthenticated` | L'identifiant est absent, invalide ou révoqué. |
| 403 | `insufficient_permissions` | L'identifiant ne dispose pas de l'autorisation requise. |
| 429 | `rate_limited` | La limite de débit a été dépassée ; réessayez après la période `Retry-After`. |
| 503 | `service_unavailable` | Une dépendance est temporairement indisponible (pour `phishing_resistant`, aussi lorsque les données d'identifiants ne peuvent pas être lues) ; réessayez après la période `Retry-After`. |

## Exemple

```bash
curl -X POST https://api.eu.keenauth.com/v1/organizations/org_01j9xk3m7q8r2s4t6v8w0y2z4a/policies/mfa:preview \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"settings":{"required_for":"all","allowed_factors":["totp","webauthn"]}}'
```

```bash
keenauth organizations policies preview org_01j9xk3m7q8r2s4t6v8w0y2z4a mfa \
  --data '{"settings":{"required_for":"all","allowed_factors":["totp","webauthn"]}}'
```
