forgejo: the bounded credential adapter — one token per lease, deleted exactly #42
No reviewers
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Meta/Campaign
Meta/Epic
Meta/Session
Priority/Critical
Priority/High
Priority/Low
Priority/Medium
Reviewed/Confirmed
Reviewed/Curated
Reviewed/Duplicate
Reviewed/Invalid
Reviewed/Won't Fix
Scope/Campaign
Status/Abandoned
Status/Blocked
Status/Conflicted
Status/In Progress
Status/In Review
Status/Need Grooming
Status/Need More Info
Status/Ready
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Ting/Vedanta!42
Loading…
Reference in a new issue
No description provided.
Delete branch "forge/29-credential-adapter"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Session #20's last leaf, under OpenSpec 20.
src/forgejo.rs, sibling ofsrc/kanidm.rs.Two findings from the live API that shaped this
Checked against the deployed instance's own OpenAPI description — Forgejo 16.0.3 — rather than from memory.
A Forgejo token has no expiry.
CreateAccessTokenOptiontakesname,scopesandrepositories, and nothing else. TheAccessTokenthat comes back carries no expiry either. So boundedness is not something this adapter can ask for — it is a property the lease enforces by deleting the exact token when it ends.That is a real divergence from #8: Kanidm's mint refuses without an explicit expiry, and this one has no such gate available. The type therefore carries no expiry field, so no caller can believe a Forge token expires on its own, and the mint test asserts none is sent — inventing one would be a lie on the wire.
Repository restriction is native.
repositoriesis Forgejo's own least-scope mechanism, so OpenSpec 61's "repository-restricted" is a field rather than a convention to emulate.Design decisions worth arguing with
The admin path, not the self-service one.
/users/{name}/tokensauthenticates as the account it mints for — which would mean Vedanta holding the queen's own credential./admin/users/{name}/tokenslets one operator identity mint on her behalf without ever possessing what she authenticates with.An empty
repositoriesis refused before sending. Forgejo accepts it and mints a token reaching everything the account can, which is the opposite of a per-lease credential. Same for an emptyscopes. The refusal belongs here, where it can say why.404 on delete is success; 403 is not. A revocation that ran once must be able to finish twice, so an already-absent token is
observed_before: falseand the operation succeeds — the external result is identical either way, and which happened is kept rather than flattened, exactly askanidm.rsdoes for invalidation. But treating a refusal as "already gone" would report a credential dead while it is live, which is the one lie this must never tell.A mint response without the value is terminal. The value is returned exactly once; if that response did not carry it, nothing can recover it, so re-reading a path that will never return a secret is not a recovery.
Verification
Gate green: fmt stable,
clippy --workspace --all-targets -D warningsclean,cargo test --workspacepassing — 113 lib tests, up from 103.Ten new tests against a loopback capture double that records method, path, body and the credential presented — so claims are about what the adapter did, not what it returned:
observed_before: falseTokenRecordhas no value field to returnNot here
No live proof. Per this record's delivery class, the live least-scope proof against a deployed Forgejo is a separate operator-gated record. Nothing here has authenticated to a real instance — the wire shapes come from the published OpenAPI description, and the first apply is what confirms the admin path's permission model in practice.
Closes #29
Refs #20