Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 36s
push_authorization_request lodges the authorization params under a
single-use request_uri; authorize_pushed redeems it into the normal consent
flow. Pushed requests reuse the pending store ({pushed, Rec} keyed by the
request_uri ref — distinct from consent req_ids, so no collision and no new
loop state). The pushed binding (client + redirect + PKCE) is still enforced
at exchange. New tests/par.sx. 217/217.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
85 lines
4.5 KiB
Plaintext
85 lines
4.5 KiB
Plaintext
;; identity/tests/par.sx — pushed authorization requests (PAR, RFC 9126):
|
|
;; lodge the authorization params up front under a single-use request_uri,
|
|
;; then redeem it into the normal consent flow. The binding (client,
|
|
;; redirect, PKCE) carried by the pushed request is enforced at exchange.
|
|
|
|
(define id-par-test-count 0)
|
|
(define id-par-test-pass 0)
|
|
(define id-par-test-fails (list))
|
|
|
|
(define
|
|
id-par-test
|
|
(fn
|
|
(name actual expected)
|
|
(set! id-par-test-count (+ id-par-test-count 1))
|
|
(if
|
|
(= actual expected)
|
|
(set! id-par-test-pass (+ id-par-test-pass 1))
|
|
(append! id-par-test-fails {:name name :expected expected :actual actual}))))
|
|
|
|
(define idp-ev erlang-eval-ast)
|
|
(define idpnm (fn (v) (get v :name)))
|
|
|
|
(identity-load-oauth!)
|
|
|
|
;; ── pushed request redeems into consent ──────────────────────────
|
|
|
|
(id-par-test
|
|
"authorize_pushed on a fresh request_uri asks for consent"
|
|
(idpnm
|
|
(idp-ev
|
|
"O = identity_oauth:start(),\n {ok, Ru} = identity_oauth:push_authorization_request(O, web, uri1, read, alice, v),\n case identity_oauth:authorize_pushed(O, Ru) of\n {consent_required, _} -> consent_required;\n {error, W} -> W\n end"))
|
|
"consent_required")
|
|
|
|
;; ── full PAR flow ────────────────────────────────────────────────
|
|
|
|
(id-par-test
|
|
"the full PAR flow yields a working token"
|
|
(idpnm
|
|
(idp-ev
|
|
"O = identity_oauth:start(),\n {ok, Ru} = identity_oauth:push_authorization_request(O, web, uri1, read, alice, v),\n {consent_required, Rq} = identity_oauth:authorize_pushed(O, Ru),\n {code, Cd} = identity_oauth:consent(O, Rq, allow),\n {ok, A, _R} = identity_oauth:exchange(O, Cd, web, uri1, v),\n case identity_oauth:introspect(O, A) of\n {active, _, _, _} -> active;\n {inactive} -> inactive\n end"))
|
|
"active")
|
|
|
|
(id-par-test
|
|
"the PAR token carries the pushed subject"
|
|
(idpnm
|
|
(idp-ev
|
|
"O = identity_oauth:start(),\n {ok, Ru} = identity_oauth:push_authorization_request(O, web, uri1, read, alice, v),\n {consent_required, Rq} = identity_oauth:authorize_pushed(O, Ru),\n {code, Cd} = identity_oauth:consent(O, Rq, allow),\n {ok, A, _R} = identity_oauth:exchange(O, Cd, web, uri1, v),\n case identity_oauth:introspect(O, A) of\n {active, Subject, _, _} -> Subject\n end"))
|
|
"alice")
|
|
|
|
;; ── request_uri is single-use ────────────────────────────────────
|
|
|
|
(id-par-test
|
|
"a request_uri cannot be redeemed twice"
|
|
(idpnm
|
|
(idp-ev
|
|
"O = identity_oauth:start(),\n {ok, Ru} = identity_oauth:push_authorization_request(O, web, uri1, read, alice, v),\n identity_oauth:authorize_pushed(O, Ru),\n case identity_oauth:authorize_pushed(O, Ru) of\n {consent_required, _} -> reused;\n {error, W} -> W\n end"))
|
|
"invalid_request_uri")
|
|
|
|
(id-par-test
|
|
"an unknown request_uri is rejected"
|
|
(idpnm
|
|
(idp-ev
|
|
"O = identity_oauth:start(),\n Bogus = make_ref(),\n case identity_oauth:authorize_pushed(O, Bogus) of\n {consent_required, _} -> ok;\n {error, W} -> W\n end"))
|
|
"invalid_request_uri")
|
|
|
|
;; ── the pushed binding is still enforced at exchange ─────────────
|
|
|
|
(id-par-test
|
|
"a PAR-issued code still enforces PKCE"
|
|
(idpnm
|
|
(idp-ev
|
|
"O = identity_oauth:start(),\n {ok, Ru} = identity_oauth:push_authorization_request(O, web, uri1, read, alice, v),\n {consent_required, Rq} = identity_oauth:authorize_pushed(O, Ru),\n {code, Cd} = identity_oauth:consent(O, Rq, allow),\n case identity_oauth:exchange(O, Cd, web, uri1, wrongverif) of\n {ok, _, _} -> ok;\n {error, W} -> W\n end"))
|
|
"invalid_grant")
|
|
|
|
(id-par-test
|
|
"a PAR-issued code still enforces client binding"
|
|
(idpnm
|
|
(idp-ev
|
|
"O = identity_oauth:start(),\n {ok, Ru} = identity_oauth:push_authorization_request(O, web, uri1, read, alice, v),\n {consent_required, Rq} = identity_oauth:authorize_pushed(O, Ru),\n {code, Cd} = identity_oauth:consent(O, Rq, allow),\n case identity_oauth:exchange(O, Cd, attacker, uri1, v) of\n {ok, _, _} -> ok;\n {error, W} -> W\n end"))
|
|
"invalid_grant")
|
|
|
|
(define
|
|
id-par-test-summary
|
|
(str "par " id-par-test-pass "/" id-par-test-count))
|