OutlabsAuth
Auth

User Management API

Admin and self-service user HTTP surface.
HTTP surface for user admin and self-service. Mount details: Routers & Prefixes.

Paths below assume the common example mount prefix="/v1/users". Your host may use /iam/users or another base — only the suffix after that prefix matters.

When this page and a running app disagree, trust live OpenAPI at {your-api}/docs and outlabs_auth/routers/users.py.


What to mount

FactoryWhen
get_users_router(auth, prefix="/v1/users")Full admin + self-service (examples, OutlabsAuth UI)
get_self_service_users_router(...)Tiny embed: own profile + permission names only
from outlabs_auth.routers import get_users_router

app.include_router(get_users_router(auth, prefix="/v1/users"))

Optional: requires_verification=True tightens auth on /me* routes.

Invites are mostly on the auth router — see User Invitations. This router only exposes POST /{user_id}/resend-invite. Entity membership CRUD lives on the memberships router — see Entity Memberships.


Permissions at a glance

ActionTypical permission
Create useruser:create
List / get / roles / audit / sessions (admin)user:read
Update profile, status, roles, revoke sessions/keysuser:update
Soft-deleteuser:delete
Grant/revoke is_superuserSuperuser only (require_superuser)
/me* self-serviceAuthenticated (optional email verification)

Simple vs Enterprise scope

Same router for both presets. Behavior depends on flags:

ConcernBehavior
SimpleRBAC (enable_entity_hierarchy=False)Scope filtering is effectively off — list/get are system-wide for permitted actors
Enterprise + enforce_user_scope=True (default)Non-global actors only see/mutate users in their entity trees. Out of scope → 404 (not 403). Self always allowed
root_entity_id on listNarrows within the actor’s scope; never widens it
/orphaned, /{id}/membership-historyMeaningful when membership service exists (Enterprise); otherwise empty pages

Set enforce_user_scope=False only as a transitional escape hatch.


Admin CRUD and lifecycle

MethodPathPermissionNotes
POST/user:createAdmin create (not public register). Only superusers may set is_superuser
GET/user:readPaginated list. Query: page, limit, search, status, is_superuser, root_entity_id
GET/{user_id}user:readSingle user (scoped)
PATCH/{user_id}user:updateAdmin profile update
DELETE/{user_id}user:deleteSoft delete
PATCH/{user_id}/passworduser:updateAdmin set password (no current password)
PATCH/{user_id}/statususer:updateactive / suspended / banned only — not deleted
POST/{user_id}/restoreuser:updateRestore a deleted identity (does not restore grants/credentials)
PATCH/{user_id}/superuserSuperuserGrant/revoke platform superuser; cannot revoke yourself
POST/{user_id}/resend-inviteuser:updateNew invite token for INVITED users

List status filter: active | suspended | banned | deleted.

Create body (UserCreateRequest): email, password, optional names / phone / is_superuser.
Update body (UserUpdateRequest): email, names, phone (partial).
Responses: UserResponse (UUID id, profile fields, status flags).
List: PaginatedResponse[UserResponse] (items, total, page, limit, pages).


Self-service (/me)

MethodPathAuthNotes
GET/meAuthenticatedOwn profile
PATCH/meAuthenticatedOwn profile (UserUpdateRequest)
POST/me/change-passwordAuthenticatedChangePasswordRequest (current + new) → 204
POST/me/phone/request-codeAuthenticatedOTP to registered phone (rate-limited) → 204
POST/me/phone/verify-codeAuthenticatedPhoneVerifyCodeRequestUserResponse

Roles and effective permissions

Direct role memberships (flat RBAC and Enterprise “direct” roles):

MethodPathPermissionNotes
GET/{user_id}/rolesuser:read?include_inactiveRoleResponse[]
GET/{user_id}/role-membershipsuser:readMembership rows + embedded role
POST/{user_id}/rolesuser:updateAssignRoleRequest → membership (201). Actor must hold all permissions on the role
DELETE/{user_id}/roles/{role_id}user:updateSoft-revoke
PATCH/{user_id}/role-memberships/{membership_id}user:updateValidity window / status
GET/{user_id}/permissionsSelf or user:readEffective perms: direct roles and entity-membership roles when present → UserPermissionSource[]

Entity-scoped role assignment (membership + roles on an entity) is not here — use Entity Memberships.


Orphans and membership history (Enterprise)

MethodPathPermissionNotes
GET/orphaneduser:readUsers with no active entity memberships. Empty for non-global scoped actors or without membership service
GET/{user_id}/membership-historyuser:readAppend-only entity membership lifecycle events

Personal API keys (admin view)

MethodPathPermissionNotes
GET/{user_id}/api-keysuser:readList personal keys (no secrets)
DELETE/{user_id}/api-keys/{key_id}user:updateAdmin revoke

Self-service key minting and system/integration keys: API Key Host Integration.


Sessions, social accounts, audit (elsewhere)

Do not duplicate those tables here:

AreaGuide
List / revoke sessions (/me/sessions, /{user_id}/sessions)Sessions & Audit
Per-user audit (/{user_id}/audit-events) + cross-user searchsame
Link / unlink social (/me/social-accounts)OAuth & Social Login

Minimal self-service router

For hosts that only need “who am I?”:

from outlabs_auth.routers import get_self_service_users_router

app.include_router(get_self_service_users_router(auth, prefix="/v1/account"))
MethodPathResponse
GET/meUserResponse
GET/me/permissionslist[str] — permission names only (not UserPermissionSource)

Schemas quick reference

ModelRole
UserResponseProfile payload
UserCreateRequest / UserUpdateRequestAdmin create / patch
ChangePasswordRequest / AdminResetPasswordRequestPassword changes
UserStatusUpdateRequest / UserSuperuserUpdateRequestStatus / superuser
PhoneVerifyCodeRequestPhone OTP confirm
AssignRoleRequest / UserRoleMembershipUpdateDirect roles
UserRoleMembershipResponse / UserRoleMembershipDetailResponseMembership rows
UserPermissionSourceEffective permission + source metadata
OrphanedUserResponse / MembershipHistoryEventResponseEnterprise helpers
PaginatedResponse[T]List envelopes