Skip to content

Long-lived identity automation has a habit of outgrowing the API contract it started with.

At first, a broad update endpoint is convenient. Read a DTO, change the fields you care about, send the object back. That works while one team owns the integration and the resource shape is small. It becomes harder when different teams manage tenants, client applications, groups, user-store records, credentials, and administrative roles through separate pipelines.

ProAuth 3 introduces Management API v2 and User Store API v2 because that operating model needs a more explicit contract. The new APIs are available under /api/management/v2/ and /api/userstore/v2/. They use command-based writes, type-safe PATCH commands, relationship endpoints, optimistic concurrency with ETag / If-Match, typed identifiers in the .NET clients, and the v2 RBAC model for administration.

The change is not about removing v1 during the ProAuth 3 upgrade. v1 still works in ProAuth 3.x, and existing v1 integrations can continue through the upgrade window. It is deprecated, though, and new automation should target v2.

flowchart LR
    subgraph V1["v1 broad update model"]
        v1read["GET entity DTO"]
        v1edit["Modify local copy"]
        v1put["PUT broad DTO with inline fields and relationships"]
        v1risk["Risk: unrelated fields or relationships travel with the write"]
        v1read --> v1edit --> v1put --> v1risk
    end

    subgraph V2["v2 command and relationship model"]
        v2read["GET resource and capture ETag"]
        v2command["POST or PUT typed command"]
        v2patch["PATCH typed patch command"]
        v2rel["PUT, POST, DELETE dedicated relationship endpoint"]
        v2match["Send If-Match for protected mutations"]
        v2result["Server returns updated resource and new ETag"]
        v2read --> v2command --> v2match --> v2result
        v2read --> v2patch --> v2match
        v2read --> v2rel --> v2match
    end

    V1 --> V2

Why broad updates became a problem

Management APIs in security products are rarely used by one script forever. They become part of deployment pipelines, onboarding tools, delegated administration flows, customer-specific maintenance jobs, and emergency operations.

That creates two practical problems.

First, intent gets blurred. If an integration sends a full ClientApp-shaped DTO back to the server to change a display name, the request also carries whatever relationship collections, security settings, and defaults happened to be present in that local copy. Even when the client did not mean to manage those fields, the request shape makes intent harder to prove.

Second, ownership gets blurred. A platform team may own client application configuration. A tenant operations team may own tenant-to-IdP relationships. A service desk workflow may manage user-store group membership. Those should not all feel like the same broad resource replacement.

The v2 contract separates those jobs.

Writes are commands, not recycled read models

The Management API v2 follows a CQRS-lite pattern. Read responses use resource names such as ClientApp, while writes use command DTOs such as CreateClientAppCommand, UpdateClientAppCommand, and PatchClientAppCommand.

The same pattern applies to User Store API v2. A user-store integration works with commands such as CreateUserCommand, UpdateUserCommand, PatchUserCommand, CreateGroupCommand, and PatchGroupCommand under /api/userstore/v2/{userstore}/administration/....

That difference matters for automation. A create command can express what must be supplied when a resource is introduced. An update command can express a complete replacement. A patch command can express a narrow change without forcing the client to round-trip the whole resource shape.

For .NET integrations, ProAuth ships the v2 client libraries as ProAuth.Clients.Management.V2 and ProAuth.Clients.UserStore.V2. These clients also use typed identifiers such as ClientAppId, SubscriptionId, TenantId, IdpInstanceId, UserId, and GroupId. The HTTP contract remains UUID-based, so direct HTTP clients and custom OpenAPI clients still send UUID strings in paths and JSON payloads.

PATCH is typed, not a loose bag of edits

The v2 APIs add PATCH operations with dedicated patch command types. For example, the migration guide shows a Management API v2 patch request like this:

PATCH /api/management/v2/clientapp/{id}
Content-Type: application/json

{
  "displayName": "New Name"
}

The important point is not just that PATCH exists. It is that PATCH is modeled through resource-specific command types, such as PatchClientAppCommand, PatchTenantCommand, PatchProAuthUserCommand, PatchUserCommand, and PatchGroupCommand. This keeps partial updates inside the generated contract instead of turning them into untyped ad hoc payloads.

For automation owners, that is a useful boundary. A script that changes a user profile field should not need to know how to safely preserve every group relationship. A job that updates a tenant label should not need to carry unrelated client application data through the request.

Relationships get their own endpoints

Many of the risky changes in an identity system are relationship changes: which tenants can use a client application, which IdP instances are linked to a tenant, which users belong to a group, or which groups are nested under another group.

In v2, those relationships are visible in the URL instead of being hidden inside a broad parent update. The generated Management API v2 spec includes relationship endpoints such as:

  • /api/management/v2/ClientApp/{id}/tenants
  • /api/management/v2/ClientApp/{id}/idpinstances
  • /api/management/v2/ClientApp/{id}/authorizedgroups
  • /api/management/v2/Tenant/{id}/clientapps
  • /api/management/v2/ProAuthGroup/{groupId}/users

The generated User Store API v2 spec follows the same idea for user-store administration:

  • /api/userstore/v2/{userstore}/administration/User/{userId}/groups
  • /api/userstore/v2/{userstore}/administration/Group/{groupId}/users
  • /api/userstore/v2/{userstore}/administration/Group/{groupId}/parentgroups
  • /api/userstore/v2/{userstore}/administration/Group/{groupId}/membergroups

Those endpoints expose separate operations for setting, adding, and removing relationships. That gives configuration-as-code workflows and delegated tools a cleaner unit of change: “add this group membership” or “replace this client app’s tenant relationship set” is different from “update the whole client app”.

Optimistic concurrency is part of the contract

Configuration APIs often have multiple writers: AdminApp, CLI imports, custom deployment jobs, support tooling, and customer automation. Without a concurrency contract, the last writer wins, even when the last writer started from stale data.

Management API v2 and User Store API v2 use HTTP optimistic concurrency for versioned resources. ProAuth emits strong ETag response headers for concurrency-aware resources. Mutating operations that replace, patch, or delete those resources require If-Match, following the standard HTTP conditional request model in RFC 9110.

The normal workflow is:

  1. GET the resource and capture the ETag.
  2. Send the write with If-Match set to that value.
  3. On success, capture the new ETag for the next write.
  4. On 412 Precondition Failed, refetch and reconcile.

Missing If-Match on a protected write returns 428 Precondition Required. A stale precondition returns 412 Precondition Failed. v1 endpoints do not enforce this concurrency model, so v1 keeps its legacy last-writer-wins behavior.

This is one of the most practical reasons to move automation to v2. It turns silent overwrite risk into an explicit conflict that a pipeline can handle.

RBAC becomes an API resource, not just a flag

The v2 API work also aligns with ProAuth 3’s RBAC model. The old v1 role model used the ProAuthSecurityRole enum and separate role tables for users, groups, and client applications. RBAC v2 uses role definitions, permission definitions, explicit principal types, and role assignments.

The Management API v2 exposes that model through endpoints such as:

  • /api/management/v2/ProAuthRole
  • /api/management/v2/ProAuthPermission
  • /api/management/v2/ProAuthRoleAssignment
  • /api/management/v2/EffectivePermissions/proauthuser/{principalId}
  • /api/management/v2/EffectivePermissions/proauthgroup/{principalId}
  • /api/management/v2/EffectivePermissions/clientapp/{principalId}

That has an integration consequence: delegated administration should be modeled through v2 concepts, not by extending v1 assumptions. A role assignment binds a user, group, or service principal to a role at a scope. The documented scope grammar represents the platform hierarchy, from global scope through customer, subscription, tenant, IdP instance, client application, group, and user scopes.

There is also a compatibility boundary. The v1 RBAC controllers remain available during the deprecation window and operate as adapters over the v2 store, so existing clients continue to work. But the v1 surface is frozen. New permissions, scopes, and principal types belong to the v2 model.

How to approach migration

Do not turn the ProAuth 3 infrastructure upgrade into a rushed API rewrite. The documented migration path keeps v1 working in ProAuth 3.x specifically so teams can separate the platform upgrade from integration modernization.

A practical sequence is:

  1. Inventory consumers of /api/management/v1/ and /api/userstore/v1/.
  2. Classify each consumer by what it writes: resources, partial fields, relationships, users, groups, credentials, or RBAC.
  3. Move .NET integrations to ProAuth.Clients.Management.V2 and ProAuth.Clients.UserStore.V2 where possible.
  4. Replace full DTO writes with create, update, or patch commands based on intent.
  5. Move relationship changes to the dedicated relationship endpoints.
  6. Add ETag / If-Match handling before enabling unattended writes.
  7. Revisit administrative permissions through RBAC v2 rather than copying v1 role assumptions.

The v2 APIs are more explicit by design. That means migration is not just a find-and-replace from /v1/ to /v2/. It is a chance to make automation say what it means, fail safely on concurrent changes, and use an authorization model that matches delegated administration more closely.

Start with the API reference, then use the Management API v2, User Store API v2, optimistic concurrency, and migration guide pages as the implementation source of truth. The release-level summary is in the ProAuth 3.0.0 release notes.