Skip to main content

GET /subjects/:id/required-actions and PUT…

GET /subjects/:id/required-actions and PUT /subjects/:id/required-actions​

The read requires view-users, the write manage-users. Sets a subject's required actions wholesale — an action left out of the list is one the caller clears, not one left alone.

If-Match is mandatory here, not optional. This route replaces an authorization-bearing list whole, so a stale write reinstates exactly what another administrator has just removed; the header comes from the matching GET, which answers an ETag over the same list. Absent, the request is refused with 428 Precondition Required and nothing is changed; stale, with 412. The list is read under the same lock the replacement runs under, so two callers sent at once are serialised — the second sees what the first wrote rather than matching the same pre-write state.

On grace, who was created through POST /subjects and so carries update-password and nothing else. The read first, for the ETag the write needs:

curl -sS -D - \
-H "Authorization: Bearer $ADMIN_TOKEN" \
http://localhost:3000/admin/tenants/demo/subjects/01a0d7ef-2be7-7a2e-bbc5-4646e22c3169/required-actions
HTTP/1.1 200 OK
x-request-id: 01a0d7ef-47de-7e63-b9e4-f21b023efd84
etag: "b6a877586a3e8e6eec8a74fd3ec35d6463a777d4a4f379aa095140acd8edcc10"
content-type: application/json; charset=utf-8
content-length: 31

{"actions":["update-password"]}

(This read predates the cache-control: no-store header and was not re-run.)

The same write twice: without the header, then with it.

curl -sS -X PUT \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"actions": ["configure-totp"]}' \
http://localhost:3000/admin/tenants/demo/subjects/01a0d7ef-2be7-7a2e-bbc5-4646e22c3169/required-actions

curl -sS -D - -X PUT \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-H 'If-Match: "b6a877586a3e8e6eec8a74fd3ec35d6463a777d4a4f379aa095140acd8edcc10"' \
-d '{"actions": ["configure-totp"]}' \
http://localhost:3000/admin/tenants/demo/subjects/01a0d7ef-2be7-7a2e-bbc5-4646e22c3169/required-actions

The reply to the second is the set as it now stands — update-password is gone, cleared by being left out — and a fresh ETag for the next write:

{"type":"about:blank","title":"Precondition Required","status":428,"detail":"If-Match is required to replace a subject’s required actions","instance":"01a0d7ef-47f4-770e-84d7-b7570de8508e"}

HTTP/1.1 200 OK
x-request-id: 01a0d7ef-62d6-749d-815d-05486997038e
etag: "eef964de74a02f6ce5f1ed1b1aa5e6a4f65aee68bfd955a4db04374af8ec513b"
content-type: application/json; charset=utf-8
content-length: 30

{"actions":["configure-totp"]}

(Not re-run for the same reason as the read above.)

Replaying the first ETag, now one generation stale, changes nothing:

{"type":"about:blank","title":"Precondition Failed","status":412,"detail":"If-Match no longer matches","instance":"01a0d7ef-62f8-7819-a402-6a7987acd136"}