Skip to content

Prepare for the Identity cutover (napbat/identity#77) - #51

Merged
coconutbird merged 1 commit into
mainfrom
chore/identity-cutover
Nov 2, 2026
Merged

coconutbird merged 1 commit into
mainfrom
chore/identity-cutover

Conversation

@coconutbird

Copy link
Copy Markdown
Member

What breaks without it

Identity PR napbat/identity#77 makes a refresh token fail with invalid_grant after the account's security stamp rotates. Password, email, MFA changes and "revoke all sessions" rotate the stamp. Production Identity minted a new token in that case. The new one does not.

After the swap, every semctl command (and every MCP tool call) for that user fails until they log in again. semctl showed only the raw OAuth body:

Error: refresh 400 Bad Request: {"error":"invalid_grant","error_description":"The specified refresh token is no longer valid.", ...}

What changed

  • src/auth/mod.rs: when /connect/token refuses a refresh with invalid_grant, semctl now says the stored login is no longer accepted and tells the user to run semctl auth login. Other token errors keep the old message. The change is client-side only, so it works with the current production Identity and with the new one.
  • src/auth/tests.rs: two regression tests (the invalid_grant path, and that other errors stay unchanged). The test token server now takes a status line.

The other semctl calls to Identity need no change:

  • POST /connect/device and the device_code grant: client semctx-cli (public, implicit consent), scopes semctx.api offline_access. The token audience is semctx-api. Device verification has no consent step, and the request has no openid, so prompt/max_age do not apply.
  • GET /v1/tenants with that semctx-api token: this endpoint accepts a delegated user token ([AcceptsDelegatedUserToken]).

Checks run

Against napbat/identity-server:cutover (d77ff9c), with the semctx-cli client from bootstrap/identity-oauth.json, using the real semctl binary:

  • semctl auth login --no-open: device flow completes and the tenant is selected automatically (GET /v1/tenants → 200). Token: aud=semctx-api, scope=semctx.api offline_access, client_id=semctx-cli.
  • semctl auth tenants → 200. With the stored token forced to expire, the refresh succeeds and the refresh token rotates.
  • After PATCH /v1/account/password (and, in a separate run, POST /v1/account/sessions/revoke): the old access token on GET /v1/tenants → 401 with the envelope body. The forced refresh → invalid_grant, and semctl prints the new message.

Repository gate: cargo fmt --check, cargo clippy --workspace --all-targets --locked -- -D warnings, cargo nextest run --workspace --locked (571 passed, 2 skipped), RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps --document-private-items --locked, and git diff --check all pass.

Identity (napbat/identity#77) refuses a refresh token with invalid_grant after a password, email, MFA, or session-revocation change rotates the account's security stamp. semctl reported the raw OAuth error body. It now tells the user to run `semctl auth login`.
@coconutbird
coconutbird merged commit 46a39b9 into main Nov 2, 2026
6 checks passed
@coconutbird
coconutbird deleted the chore/identity-cutover branch November 8, 2026 20:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant