Client contract (rewrite pack) — keystoneauth → Keystone and services — branch stable/2026.2
Branch
stable/2026.2@1cd5400202c6— repository https://opendev.org/openstack/keystoneauth — analysed on 2026-10-28 Service used for the proof: Keystonestable/2026.2@e6dc5ae079e6(../reverse-spec-keystone/), run in-process
keystoneauth is the client of every OpenStack API. A rewrite of Keystone (OpenKeel Keystone), or of any other service, must answer what keystoneauth expects. Otherwise the CLIs, the SDKs, keystonemiddleware and the services themselves (Nova towards Placement, for example) stop working. This file lists these requirements and provides a suite of 9 scenarios recorded against the real Keystone.
1. Contents of _rewrite/
| File | Content |
|---|---|
[exchanges_stable-2026.2.json](_rewrite/exchanges_stable-2026.2.json) | the 9 scenarios (name, expected result, result, detail) and the 26 HTTP exchanges between keystoneauth and Keystone (requests, headers, bodies, responses) |
[response-schemas_stable-2026.2.json](_rewrite/response-schemas_stable-2026.2.json), [response-schemas_stable-2026.2.md](_rewrite/response-schemas_stable-2026.2.md) | schemas of the Keystone responses observed through keystoneauth |
[observed-errors_stable-2026.2.md](_rewrite/observed-errors_stable-2026.2.md) | errors received (401 on authentication and on a revoked token) |
How it was produced: conformance_capture.py scenarios with the keystoneauth adapter (skill generate-reverse-spec).
- A throw-away Keystone: SQLite,
db_sync,fernet_setup,credential_setup,receipt_setup,bootstrap. - Served in-process at
http://keystone.testthrough wsgi-intercept. - Each scenario uses keystoneauth's public API, as a service or a CLI would.
The test data (password secretpassword, throw-away tokens) are fictitious.
2. Scenarios (proof: 9/9 as expected)
| # | Scenario | What it proves | Exchanges |
|---|---|---|---|
| 1 | generic-password-discovery-project-scope | discovery on / (300 accepted), v3 authentication, catalog lookup (identity, public) | GET / 300, POST /v3/auth/tokens 201 |
| 2 | v3-password-domain-and-system-scope | scope.domain and scope.system.all | 2 × POST /v3/auth/tokens 201 |
| 3 | v3-unscoped-then-token-rescope | "scope": "unscoped", then the token method with X-Auth-Token | 2 × 201 |
| 4 | session-endpoint-filter-list-users | relative URL resolved through the catalog; no extra discovery when the catalog URL already ends with the version | GET /v3/users 200 |
| 5 | v3-password-wrong-password | 401 → keystoneauth1.exceptions.Unauthorized | POST /v3/auth/tokens 401 |
| 6 | session-reauth-after-revocation | 401 on a revoked token → invalidate, re-authenticate, replay once | GET 401, POST 201, GET 200 |
| 7 | v3-application-credential | application_credential method; token scoped to the credential's project | POST …/application_credentials 201, POST /v3/auth/tokens 201 |
| 8 | v3-multifactor-receipt-then-totp | **401 + Openstack-Auth-Receipt** → MissingAuthMethods (methods=['password']), then password + totp in one request | 401 receipt, 201 |
| 9 | discovery-version-document | parsing of the Keystone version document (v3.14) | GET / 300 |
3. What keystoneauth requires from Keystone
| Requirement | Source |
|---|---|
POST /v3/auth/tokens returns 201 (any 2xx works [inferred]), the **X-Subject-Token** header and a JSON body with **token**; otherwise InvalidResponse | keystoneauth1/identity/v3/base.py:240-262, keystoneauth1/access/access.py:45 |
token fields read by AccessInfoV3: expires_at, issued_at, user (id, name, domain), project/domain/system, roles, catalog (type, name, id, endpoints[].interface/region_id/region/url/id), audit_ids, OS-TRUST:trust, application_credential, is_domain | keystoneauth1/access/access.py |
accepts "scope": "unscoped" (string) and {"system": {"all": true}} | keystoneauth1/identity/v3/base.py:186-227 |
MFA: 401 **+ Openstack-Auth-Receipt header** + body receipt.methods, receipt.expires_at, required_auth_methods | keystoneauth1/exceptions/http.py:505-510, keystoneauth1/exceptions/auth.py:23-32 |
401 (not 403 or 404) for a revoked or expired X-Auth-Token on any route, so that re-authentication happens | keystoneauth1/session.py:1128-1136 |
version document on / and /v3: versions.values[] (or versions[], or version), each with id, status, links[rel=self] | keystoneauth1/discover.py:66-160 |
errors: {"error": {"message", "details"}} (Keystone), x-openstack-request-id, Retry-After | keystoneauth1/exceptions/http.py:445-531 |
4. What keystoneauth requires from every service (OpenKeel Placement included)
- Version document at the root or at the versioned URL, with
min_version/max_versionfor microversions. Without one, the catalog URL is used as is (version hack) orDiscoveryFailureis raised. - Microversion header
OpenStack-API-Version: <service_type> <x.y>, with the service type as keystoneauth names it (placement,volumefor Cinder…). - Retriable statuses: 429 and 503, with
Retry-Afterhonoured by the callers that enable retries. - Error bodies
{"errors": [{"title", "detail"}]}(API-SIG) or{"error": {"message"}}. - Redirects with
Location; the method is kept.
5. Conformance strategy for OpenKeel
- Run the same 9 scenarios against the Go Keystone. Point the adapter at its URL instead of the in-process app (
run_scenarios: replaceinitialize_applicationwith a reset of the Go service and its URL). Expected: the same outcomes, the same sequences of (method, route, status), and the presence ofX-Subject-Token,Openstack-Auth-ReceiptandWWW-Authenticateon the same responses. - Validate the response bodies against
response-schemas_stable-2026.2.json, and against the schemas declared in Keystone (../reverse-spec-keystone/_rewrite/). - Then the suites of the clients: osc-placement (928 commands,
../reverse-spec-osc-placement/), and Tempest identity.
6. Gaps
| Gap | Impact |
|---|---|
| federation (OIDC, SAML, WebSSO), OAuth1/OAuth2, Kerberos, tokenless: not run (they need an external IdP or mTLS) | requirements read from the code only; to cover with an OIDC IdP in the test environment |
| microversion discovery of a service other than Keystone not exercised (Keystone has no microversions) | covered by osc-placement (negotiation) and Placement's gabbi |
| v2 plugins not exercised | obsolete, Keystone v2 removed |
7. Checklist (OpenKeel Keystone seen from keystoneauth)
- [ ] 300 + version document on
/, 200 on/v3 - [ ] 201 +
X-Subject-Token+tokenonPOST /v3/auth/tokens;unscopedandsystem.allscopes - [ ] 401 + receipt for incomplete MFA; 401 for a revoked token on every route
- [ ] the 9 scenarios as expected
Source: reverse-spec-keystoneauth/07-rewrite-pack_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.