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.rolesis populated fromLocalUser.rolesget_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->Adminuser->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
- Command implementation:
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.tsfrontend/src/providers/authProvider.tsfrontend/src/pages/LoginPage.tsxfrontend/src/pages/ListView.tsxfrontend/src/pages/FormView.tsxfrontend/src/hooks/useDocTypeMeta.ts
-
Shared desk package + template:
libs/framework-m-desk/src/data.tslibs/framework-m-desk/src/auth.tslibs/framework-m/src/framework_m/templates/frontend/src/App.tsxlibs/framework-m/src/framework_m/templates/frontend/src/pages/LoginPage.tsx
6) DocType
Meta.permissionsand role matching behaviorRole checks for DocType actions are resolved from
Meta.permissionsand 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). Allis 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
Userunless 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-adminstill 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.