;; identity/token.sx — opaque, grant-backed tokens (RFC 7662 / 7009). ;; ;; The token table is a process; the token itself is an opaque handle ;; (make_ref) that carries NO information. introspect(Token) is a live ;; lookup against the table every time — the token is never decoded. ;; Because every introspection consults the live table, revocation is ;; real: a revoked token reads inactive on the very next introspection, ;; with no window where it still validates (RFC 7009 §2). ;; ;; introspect replies model RFC 7662 §2.2: ;; {active, Subject, Client, Scope} — token is currently valid ;; {inactive} — unknown OR revoked; never says why ;; ;; Authorization is NOT decided here. {active, ...} states WHO and WHAT ;; was granted; whether that subject may do a thing is acl's question. (define identity-token-source "-module(identity_tokens).\n\n start() ->\n spawn(fun () -> loop([]) end).\n\n issue(Reg, Subject, Client, Scope) ->\n Reg ! {issue, Subject, Client, Scope, self()},\n receive {token_reply, R} -> R end.\n\n introspect(Reg, Token) ->\n Reg ! {introspect, Token, self()},\n receive {token_reply, R} -> R end.\n\n revoke(Reg, Token) ->\n Reg ! {revoke, Token, self()},\n receive {token_reply, R} -> R end.\n\n stop(Reg) ->\n Reg ! {stop, self()},\n receive {token_reply, R} -> R end.\n\n loop(Tokens) ->\n receive\n {issue, Subject, Client, Scope, From} ->\n Token = make_ref(),\n From ! {token_reply, {ok, Token}},\n loop([{Token, {Subject, Client, Scope, active}} | Tokens]);\n {introspect, Token, From} ->\n From ! {token_reply, find(Token, Tokens)},\n loop(Tokens);\n {revoke, Token, From} ->\n From ! {token_reply, ok},\n loop(revoke_token(Token, Tokens));\n {stop, From} ->\n From ! {token_reply, ok}\n end.\n\n find(_, []) -> {inactive};\n find(Token, [{T, {Subject, Client, Scope, active}} | Rest]) ->\n case T =:= Token of\n true -> {active, Subject, Client, Scope};\n false -> find(Token, Rest)\n end;\n find(Token, [{T, {_, _, _, revoked}} | Rest]) ->\n case T =:= Token of\n true -> {inactive};\n false -> find(Token, Rest)\n end.\n\n revoke_token(_, []) -> [];\n revoke_token(Token, [{T, {Su, Cl, Sc, St}} | Rest]) ->\n case T =:= Token of\n true -> [{T, {Su, Cl, Sc, revoked}} | Rest];\n false -> [{T, {Su, Cl, Sc, St}} | revoke_token(Token, Rest)]\n end.") (define identity-load-token! (fn () (erlang-load-module identity-token-source)))