Skip to content

No readable authorization state outside a dispatch (UI cannot gate on roles) #24

Description

@Yaraslaut

Summary

SessionToken already carries a roles vector, but it is only consulted inside a
dispatch — SigningAuthorizer verifies the token and decides whether the call is allowed.
There is no way to ask, outside a dispatch, "who is signed in and what may they do?"

Why it matters

UI code needs that answer to shape itself, not just to be refused afterwards. A button
the current user is not permitted to use should be hidden or disabled, not enabled and
then rejected when clicked. Today the only way to know is to attempt the action and catch
the failure, which is the wrong interaction.

Applications therefore end up maintaining a second, parallel notion of "the signed-in
user and their permissions" — read by the UI — beside the one morph verifies on the
server. Two sources for the same fact, and no guarantee they agree.

Sketch

Something readable outside a dispatch, refreshed on login:

namespace morph::session {

/// The verified identity of the signed-in user, readable outside a dispatch.
struct Principal {
    std::string id;                     // verified, not client-asserted
    std::vector<std::string> roles;
    std::unordered_map<std::string, std::string> claims;

    [[nodiscard]] bool hasRole(std::string_view r) const;
};

/// Set once the token is verified; cleared on sign-out.
void setPrincipal(Principal);
[[nodiscard]] Principal const& currentPrincipal();

} // namespace morph::session

so a view can do:

deleteButton.setEnabled(morph::session::currentPrincipal().hasRole("editor"));

against the same source the server authorizes with.

Open questions

  • Where does it live? A process-wide value is convenient for a desktop client but
    wrong for a server handling many connections concurrently — probably per-connection
    there, which suggests this belongs on a session object rather than in a global.
  • Trust. It must be populated only from a verified token, never from what the client
    claimed, or it becomes a client-controlled permission set.
  • Staleness. Roles can change server-side mid-session; the API should make clear that
    this is a cache and the server remains the authority.
  • Relationship to Context. The per-call session::Context already travels with each
    dispatch; this would be the longer-lived counterpart, not a replacement.

Raising it as a design question rather than a concrete proposal — the scoping decision
seems like the part that needs agreement first.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions