identity: scope-as-set + scope narrowing on refresh (RFC 6749 §6, +6 tests)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 44s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 44s
Each access token now carries its own effective scope (<= the grant's max).
refresh/3 requests a narrower scope; the request must be a subset of the
grant scope, else {error, invalid_scope} and the refresh token is NOT
consumed (client may retry, §5.2). refresh/2 keeps full scope; scope stays
opaque (atom or list) for issue so all prior atom-scope tests are unchanged.
Also files a Blocker: PKCE S256 is blocked on erlang substrate bugs (binary
=:= always true; crypto:hash ignores binary content). token 24/24, 130/130.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,7 +19,7 @@ through the event log, all authorization questions delegated to `acl-on-sx`.
|
||||
|
||||
## Status (rolling)
|
||||
|
||||
`bash lib/identity/conformance.sh` → **124/124** (all four phases complete)
|
||||
`bash lib/identity/conformance.sh` → **130/130** (4 phases + ext: scope narrowing)
|
||||
|
||||
## Ground rules
|
||||
|
||||
@@ -78,9 +78,9 @@ lib/identity/api.sx ── (identity/login) (identity/grant?) (identity/revoke)
|
||||
- [x] tests: audit completeness, cross-instance subject mapping
|
||||
|
||||
## Extensions (base roadmap complete; deepen the engine)
|
||||
- [ ] PKCE S256 method (RFC 7636 §4.2) — SHA256 challenge derivation, not just `plain`
|
||||
- [~] PKCE S256 method (RFC 7636 §4.2) — BLOCKED on erlang substrate (see Blockers)
|
||||
- [ ] access-token TTL / `expires_in` — tokens expire as a grant timeout, introspect honours it
|
||||
- [ ] scope as a set + scope narrowing on refresh (RFC 6749 §6)
|
||||
- [x] scope as a set + scope narrowing on refresh (RFC 6749 §6)
|
||||
- [ ] client registry: public vs confidential clients, client authentication (RFC 6749 §2)
|
||||
- [ ] client-credentials grant (RFC 6749 §4.4) and device grant (RFC 8628)
|
||||
- [ ] acl-on-sx delegation: wire `verify`/membership projection → an acl decision, integration test
|
||||
@@ -88,6 +88,15 @@ lib/identity/api.sx ── (identity/login) (identity/grant?) (identity/revoke)
|
||||
- [ ] unify `api.sx` over oauth + membership + audit (one facade, audited login/consent)
|
||||
|
||||
## Progress log
|
||||
- 2026-06-07 — scope narrowing (ext): each access token now carries its own
|
||||
EFFECTIVE scope (<= the grant's max). `refresh/3` requests a narrower scope;
|
||||
the request must be a subset of the grant scope (RFC 6749 §6) else
|
||||
`{error, invalid_scope}` and the refresh token is NOT consumed (client may
|
||||
retry, §5.2). `refresh/2` keeps full scope; scope stays opaque (atom or list)
|
||||
for issue, so all prior atom-scope tests pass unchanged. token 18→24, 130/130.
|
||||
Also filed Blocker: PKCE S256 needs SHA256+binary compare, both broken in the
|
||||
erlang substrate (binary `=:=` always true; crypto:hash ignores binary
|
||||
content) — deferred, plain method stays.
|
||||
- 2026-06-07 — `federation.sx`: trust-gated, advisory federated identity.
|
||||
A peer assertion is accepted only from an explicitly trusted peer
|
||||
(else `{error, untrusted}`) and is flagged `{peer_asserted, Peer}`, never
|
||||
@@ -171,4 +180,18 @@ lib/identity/api.sx ── (identity/login) (identity/grant?) (identity/revoke)
|
||||
`tests/session.sx`). 11/11.
|
||||
|
||||
## Blockers
|
||||
(loop fills this in)
|
||||
- 2026-06-07 — **PKCE S256 blocked: erlang binary bugs.** Two substrate bugs
|
||||
in `lib/erlang` make a correct/secure S256 impossible (S256 needs
|
||||
`BASE64URL(SHA256(verifier))` compared against the stored challenge):
|
||||
1. **Binary `=:=` always true.** `<<"v1">> =:= <<"v2">>` → `true`;
|
||||
`<<"abc">> =:= <<"abd">>` → `true`. So a hash comparison can't reject a
|
||||
wrong verifier.
|
||||
2. **`crypto:hash` ignores binary-literal content.**
|
||||
`crypto:hash(sha256, <<"v1">>)` and `crypto:hash(sha256, <<"v2">>)` return
|
||||
the *identical* 32-byte digest (`6e 34 0b 9c …`), which is also ≠ the
|
||||
correct SX-level `(crypto-sha256 "abc")` (`ba 78 16 bf …`). The binary
|
||||
payload isn't reaching the hash. (Atom input → badarg→nil, separate issue.)
|
||||
Minimal repro (epoch protocol, after loading lib/erlang/runtime.sx):
|
||||
`(erlang-eval-ast "case <<\"a\">> =:= <<\"b\">> of true -> bug; false -> ok end")`
|
||||
→ `bug`. Not in scope to fix (lib/erlang is a substrate). PKCE `plain`
|
||||
remains correct and in use; S256 deferred until the binary path is fixed.
|
||||
|
||||
Reference in New Issue
Block a user