Skip to main content

Authentication & Role Management Updates

This document summarizes the recent authentication and role-management changes across backend, CLI, and Desk/custom frontend UX.

What changed

1) Persistent roles on local users

LocalUser now stores explicit roles in the user record.

  • Field: roles: list[str]
  • Default: ["User"]
  • Location: libs/framework-m-core/src/framework_m_core/doctypes/user.py

This enables role checks to use persisted role values instead of empty/default runtime placeholders.

2) Role propagation through identity and tokens

LocalIdentityAdapter now propagates roles end-to-end:

  • UserContext.roles is populated from LocalUser.roles
  • get_attributes(...)["roles"] returns persisted roles
  • JWT payload includes roles
  • Location: libs/framework-m-standard/src/framework_m_standard/adapters/auth/local_identity.py

This makes RBAC decisions, attribute-based checks, and downstream consumers consistent with stored user roles.

3) OAuth/social provisioning persists roles

Local user creation in web adapter now persists incoming roles and defaults to User when not provided.

  • OAuth first-login creation now writes roles
  • Default for social-created users remains User
  • Location: libs/framework-m-standard/src/framework_m_standard/adapters/web/app.py

4) New CLI flow: create-user

Added a first-class user creation command with role input.

  • New command: m create-user
  • Existing command retained: m create-admin (backward-compatible alias)
  • Role normalization:
    • admin, administrator -> Admin
    • user -> User
    • other inputs title-cased
  • Locations:
    • Command implementation: libs/framework-m-standard/src/framework_m_standard/cli/admin.py
    • Entry points: libs/framework-m-standard/pyproject.toml

CLI usage

# explicit
m create-user --email user@example.com --password "strong-pass" --role User --name "App User"

# interactive role prompt
m create-user --email admin@example.com --password "strong-pass"

# backward-compatible alias (forces Admin role)
m create-admin --email admin@example.com --password "strong-pass"

5) Desk and template auth/error UX improvements

Authentication and permission failures are now surfaced as user-visible, contextual messages in both the main frontend and scaffolded custom UI/template paths.

Key improvements:

  • Login page displays backend-provided error messages (message/detail) instead of generic failures
  • Permission/auth failures (401/403) get clearer action-aware text in data providers
  • List/form/meta load failures show inline error banners instead of only console/alert behavior

Primary locations:

  • Main frontend:

    • frontend/src/providers/frameworkMDataProvider.ts
    • frontend/src/providers/authProvider.ts
    • frontend/src/pages/LoginPage.tsx
    • frontend/src/pages/ListView.tsx
    • frontend/src/pages/FormView.tsx
    • frontend/src/hooks/useDocTypeMeta.ts
  • Shared desk package + template:

    • libs/framework-m-desk/src/data.ts
    • libs/framework-m-desk/src/auth.ts
    • libs/framework-m/src/framework_m/templates/frontend/src/App.tsx
    • libs/framework-m/src/framework_m/templates/frontend/src/pages/LoginPage.tsx

    6) DocType Meta.permissions and role matching behavior

    Role checks for DocType actions are resolved from Meta.permissions and matched against persisted user roles.

    Example:

    class Meta:
    permissions: ClassVar[dict[str, list[str]]] = {
    "create": ["Admin"],
    "read": ["All"],
    "write": ["Manager"],
    "delete": ["Admin", "Manager"],
    }

    Important behavior:

    • Matching for role names is exact string match (set(user_roles) & set(allowed_roles)).
    • Use canonical role names consistently (recommended: Admin, Manager, User).
    • All is treated as wildcard for that action (case-insensitive).
    • Admin bypass is handled by RBAC adapter defaults (Admin, System).

    Practical implication:

    • If you configure "write": ["manager"] but user role is "Manager", write will be denied.
    • Since CLI role creation normalizes to canonical casing, docs/examples should also use canonical role names.

Behavior notes

  • RBAC role checks operate on role names, not email addresses.
  • Matching semantics remain role-based and policy-driven; persisted roles now supply reliable input.
  • New users created via local CLI default to supplied role; social-created users default to User unless another role list is passed at creation path.
  • For DocType permissions, role names are case-sensitive matches; keep role casing consistent in both user records and Meta.permissions.

Migration / compatibility

  • m create-admin still works and now delegates to the new flow.
  • Existing users without explicit role data may still resolve to default behavior where model defaults apply.
  • New records created after this change consistently include role data.

Validation completed for these changes

Targeted auth tests were updated and run for role propagation:

python -m pytest \
libs/framework-m-standard/tests/adapters/auth/test_local_identity.py \
libs/framework-m-standard/tests/adapters/auth/test_local_identity_extended.py

Result: passing in local validation during implementation.