;; identity/tests/grants.sx — the client-credentials grant (RFC 6749 ;; §4.4): a confidential client authenticates and gets a token acting on ;; its own behalf — no end-user, no refresh token (§4.4.3). Public clients ;; cannot use it. (define id-grants-test-count 0) (define id-grants-test-pass 0) (define id-grants-test-fails (list)) (define id-grants-test (fn (name actual expected) (set! id-grants-test-count (+ id-grants-test-count 1)) (if (= actual expected) (set! id-grants-test-pass (+ id-grants-test-pass 1)) (append! id-grants-test-fails {:name name :expected expected :actual actual})))) (define idg-ev erlang-eval-ast) (define idgnm (fn (v) (get v :name))) (identity-load-oauth!) ;; ── confidential client-credentials happy path ─────────────────── (id-grants-test "a confidential client obtains a working token" (idgnm (idg-ev "O = identity_oauth:start(),\n identity_oauth:register_client(O, svc, confidential, sk, [uri1]),\n {ok, T} = identity_oauth:client_credentials(O, svc, sk, batch),\n case identity_oauth:introspect(O, T) of\n {active, _, _, _} -> active;\n {inactive} -> inactive\n end")) "active") (id-grants-test "the client-credentials token's subject is the client itself" (idgnm (idg-ev "O = identity_oauth:start(),\n identity_oauth:register_client(O, svc, confidential, sk, [uri1]),\n {ok, T} = identity_oauth:client_credentials(O, svc, sk, batch),\n case identity_oauth:introspect(O, T) of\n {active, Subject, _, _} -> Subject\n end")) "svc") (id-grants-test "the client-credentials token carries the requested scope" (idgnm (idg-ev "O = identity_oauth:start(),\n identity_oauth:register_client(O, svc, confidential, sk, [uri1]),\n {ok, T} = identity_oauth:client_credentials(O, svc, sk, reports),\n case identity_oauth:introspect(O, T) of\n {active, _, _, Scope} -> Scope\n end")) "reports") (id-grants-test "client-credentials issues no refresh token (single value)" (idgnm (idg-ev "O = identity_oauth:start(),\n identity_oauth:register_client(O, svc, confidential, sk, [uri1]),\n case identity_oauth:client_credentials(O, svc, sk, batch) of\n {ok, _, _} -> pair;\n {ok, _} -> single;\n {error, W} -> W\n end")) "single") ;; ── authentication failures ────────────────────────────────────── (id-grants-test "a wrong client secret is invalid_client" (idgnm (idg-ev "O = identity_oauth:start(),\n identity_oauth:register_client(O, svc, confidential, sk, [uri1]),\n case identity_oauth:client_credentials(O, svc, wrong, batch) of\n {ok, _} -> issued;\n {error, W} -> W\n end")) "invalid_client") (id-grants-test "a public client cannot use client-credentials" (idgnm (idg-ev "O = identity_oauth:start(),\n identity_oauth:register_client(O, spa, public, none, [uri1]),\n case identity_oauth:client_credentials(O, spa, none, batch) of\n {ok, _} -> issued;\n {error, W} -> W\n end")) "unauthorized_client") (id-grants-test "an unregistered client cannot use client-credentials" (idgnm (idg-ev "O = identity_oauth:start(),\n case identity_oauth:client_credentials(O, ghost, x, batch) of\n {ok, _} -> issued;\n {error, W} -> W\n end")) "invalid_client") ;; ── independence + real revocation for client tokens ───────────── (id-grants-test "two confidential clients get independent tokens" (idgnm (idg-ev "O = identity_oauth:start(),\n identity_oauth:register_client(O, svc1, confidential, k1, [uri1]),\n identity_oauth:register_client(O, svc2, confidential, k2, [uri1]),\n {ok, _T1} = identity_oauth:client_credentials(O, svc1, k1, batch),\n {ok, T2} = identity_oauth:client_credentials(O, svc2, k2, batch),\n case identity_oauth:introspect(O, T2) of\n {active, Subject, _, _} -> Subject\n end")) "svc2") (id-grants-test "a client-credentials token can be revoked" (idgnm (idg-ev "O = identity_oauth:start(),\n identity_oauth:register_client(O, svc, confidential, sk, [uri1]),\n {ok, T} = identity_oauth:client_credentials(O, svc, sk, batch),\n identity_oauth:revoke(O, T),\n case identity_oauth:introspect(O, T) of\n {active, _, _, _} -> still_valid;\n {inactive} -> inactive\n end")) "inactive") (define id-grants-test-summary (str "grants " id-grants-test-pass "/" id-grants-test-count))