Skip to content

User Management

View .md

Beacon has a built-in user store: internal users who sign in with a password stored in Beacon, external users who sign in through your identity provider, global roles, the six functional roles, and server-checked sessions. It is off by default. Who may do what once a user exists is described on Authorization.

  • Internal users sign in with a user name (or e-mail address) and a password Beacon stores as a PBKDF2-SHA256 hash (100,000 iterations, 32-byte salt).
  • External users sign in through single sign-on (OIDC), a REST bearer token, or a login API that returns a JWT. Their roles always come from Beacon, never from the token.
  • Global roles Admin, Editor and Viewer; functional roles Approver, Auditor, SecurityAdmin, UserAdmin, DataSourceAdmin and MigrationOperator, one administrative duty each, without project data.
  • First-run setup creates the super admin with a setup token.
  • Sessions are checked against the user store on the server and end after an absolute lifetime.
  • Soft delete: archiving a user keeps their history.

User management is configured in code, in the AddBeaconServices lambda:

builder.Services.AddBeaconServices(builder.Configuration, options =>
{
options.AddBeaconScheduler<BeaconScheduler>();
options.Authorization.Enabled = true;
options.Authentication.EnableLoginForm = true;
options.AddAuthenticationProvider<DatabaseAuthenticationProvider>();
options.EnableUserManagement(x =>
{
x.AllowInternalUsers = true;
x.MinimumPasswordLength = 12;
});
})
.UsePostgreSql();
// The SPA is served at /beacon: the login page is /beacon/login
builder.Services.AddBeaconCookieAuthentication("/beacon");
// Data Protection keys for the session cookie (or configure your own). Outside Development set
// Beacon:DataProtection:ApplicationName and chain ProtectKeysWith*, or the host does not start.
builder.Services.AddBeaconDataProtection(builder.Configuration, builder.Environment);

The host also maps the login and setup endpoints (app.MapLoginEndpoints("/beacon", beaconConfiguration), app.MapSetupEndpoints("/beacon")) and the login-form and first-run middleware; see Host wiring and Sign-in & identity.

Option (options.UserManagement.*)DefaultWhat it does
EnabledfalseTurns on the user store, the Users page, first-run setup and DatabaseAuthorizationProvider
AllowInternalUserstrueAllows password users
MinimumPasswordLength8Shortest password accepted
RequirePasswordComplexitytrueRequires an upper-case letter, a lower-case letter, a digit and a character that is neither
SetupTokenunsetThe first-run setup token; also bound from Beacon:UserManagement:SetupToken (see First-run setup)
LocalLoginonPassword sign-in; also bound from Beacon:UserManagement:LocalLogin (see Local password sign-in)

While no super admin has ever existed in the database (an archived super admin counts), every page redirects to the setup page, /beacon/setup:

  1. Open Beacon. You are redirected to setup.
  2. Enter the setup token: the value of Beacon:UserManagement:SetupToken, or, when none is configured, the token Beacon prints once to the process console (standard error) at startup. The generated token is never written to the log; a log Warning only says that it was printed.
  3. Create the super admin (user name, e-mail address, password). The system roles and the functional roles are seeded.
  4. Sign in with the new account.

A configured SetupToken must be at least 32 characters, or the host does not start. Each replica generates its own token, so a deployment with several replicas must configure one. The token is compared in constant time; creation is atomic (a request that loses a race gets 409 and can retry). A wrong or missing token answers 403.

The setup endpoints are anonymous and throttled per client address, separately from the login form. Until the super admin exists Beacon provisions no other user: an SSO sign-in or MCP caller that would create a user is refused. GET /beacon/api/setup/roles is anonymous only while setup is open.

An Admin or a UserAdmin manages users on Users (/beacon/users):

  • Add user creates an internal or an external user with roles. Internal users are an Admin’s to create; a UserAdmin creates external users only. A password an Admin sets must be replaced at first sign-in (see Passwords set by an administrator).
  • Edit user changes the display name, enabled state and roles. The user name and e-mail address are an Admin’s to change.
  • Sign out everywhere ends every session of the user.
  • Disabling or archiving a user revokes their API keys; re-enabling revokes any key still active instead of restoring one.

Set a display name for everyone who should be added to projects: project member search matches display names only, and other users see people by display name (or “User {id}”), never by user name or e-mail address.

RoleLevelReadWrite (create, edit, run ad hoc)
Admin3YesYes
Editor2YesYes
Viewer1YesNo
Functional roles0NoNo

A super admin (IsSuperAdmin, the first-run account) passes every check.

The global role is a ceiling, not a grant: project access decides where a user may use it. Deleting and archiving are the project action Manage, which a project admin holds, an Editor holds in an open project, and the recorded owner of a resource holds. A Viewer reads and runs saved SQL only in projects they may use.

Some resources belong to a person: an alert task is worked by its assignee, a data contract is changed by its owner, an AI actor by its creator. Only an Admin or a UserAdmin gives a subscription, data contract or AI actor a new owner.

Each functional role grants one functional permission and no project data: it is a level-0 role with neither read nor write. A user who should also use projects needs a Viewer or Editor role beside it. Admins hold every permission.

RoleThe duty
ApproverApproves and rejects changes under the approval workflow; reads every project’s approval requests
AuditorReads the MCP audit, the MCP and admin settings (secrets masked) and every user’s API keys
SecurityAdminChanges the MCP and admin settings (not the LLM connection or base URL), recipients, MCP tool publishing and repository tokens
UserAdminManages users (external users only), their roles, sessions and API keys, resource owners and the members of any project; restricts projects
DataSourceAdminCreates, tests and deletes data sources; links a data source it can read into a project it administers
MigrationOperatorRuns data migration between data sources it may query and write

Assigning them. GET /beacon/api/users/roles lists every role, with the permission a functional role grants. Roles are assigned when a user is created or with PUT /beacon/api/users/{id}/roles ({ "roleIds": [1, 4] }, the complete set; 204, 404 for an unknown user, 400 for an unknown role or a separation-of-duties conflict). Role names follow Beacon:Authorization:Roles (see Configuration).

On the Users page the dialogs list roles in two groups, Global roles and Functional roles. For a UserAdmin who is not an Admin, Add user offers external users only, the user name and e-mail address are read-only, the Admin and functional roles are disabled, and the dialog opens read-only for their own account and for protected users (Admins, super admins and functional role holders).

What a UserAdmin cannot do (403): assign or remove the Admin role or a functional role; create an internal user or change a user name or e-mail address; change their own account, roles or project membership; touch a protected user’s account, roles, memberships or API keys; open a restricted project; make themselves the owner of scheduled work. Every user created logs UserCreated (7114) and every role change UserRolesChanged (7113), with ids only. See Authorization → Rules for what a UserAdmin can do and how to watch it.

From the identity provider. With Beacon:Authorization:IdpRoles:Enabled, app roles mapped in IdpRoles:Map add their permission to an SSO session at sign-in. They are not stored, count for IdpRoles:ClaimLifetimeMinutes (default 60), do not show on the Users page and never reach an API key.

Default roles. The role SSO and MCP provisioning give new users (DefaultRoleName) may not be the Admin role or a functional role; startup fails otherwise.

The global role caps what a user may do; project access decides where. New projects are created restricted, with their creator as project admin, when Beacon manages membership; projects that existed before project access stay open until restricted. An open project is used by everyone with a role; a restricted one only by its members and Admins.

Project roleIn the project
Viewerreads its resources and runs its saved SQL
Editoralso works ad hoc and creates and changes resources; deletes what they own (in an open project, anything)
Project adminalso deletes any of its resources, manages its members and links data sources

A global Viewer is a Viewer in every project, whatever their membership; Editors and Admins act with their membership role (and in an open project with the higher of that and their global role). Project admins, UserAdmins and Admins manage members on the project page’s Members tab. An Admin or a UserAdmin restricts a project; only an Admin opens a restricted one. A disabled or archived user keeps their membership rows, which grant nothing.

Memberships live in Beacon’s database while user management is on. A host can supply its own IProjectMembershipSource (for example from identity-provider groups); Beacon’s membership routes then answer 409 and the UI hides the Members tab. See Authorization → Host seams.

ProviderSigns in
DatabaseAuthenticationProviderinternal users against Beacon’s user store
JwtExternalApiAuthenticationProviderexternal users through your login API, which returns a JWT
HybridAuthenticationProviderinternal users first, then the external login API

Register one with options.AddAuthenticationProvider<T>(). Single sign-on (OIDC) is separate: builder.Services.AddBeaconOidcAuthentication(builder.Configuration) with the Beacon:Authentication:Oidc section, see Single sign-on admission.

The login form posts the credentials to your login API and validates the token it returns. JWT options are code-only (there is no configuration section):

builder.Services.AddBeaconJwtAuthentication(jwt =>
{
jwt.ExternalLoginEndpoint = "https://idp.example.com/api/auth/login";
jwt.EnableBearerAuthentication = true; // also accept Authorization: Bearer on the REST API
jwt.Validation.JwksEndpoint = "https://idp.example.com/.well-known/jwks.json"; // or Validation.SigningKey
jwt.Validation.ValidIssuer = "https://idp.example.com";
jwt.Validation.ValidAudience = "beacon";
jwt.AccessTokenClaim = "scope"; // only for a provider that does not set typ: at+jwt
});

With ExternalLoginEndpoint or EnableBearerAuthentication set, the host does not start without a signing key or JWKS endpoint, at least one issuer and one audience; issuer, audience and lifetime validation must stay on, and ClockSkew may be at most five minutes.

The token must be an access token, shown by positive evidence: a Microsoft Entra token names its client (azp or appid) and carries scp or roles; a token from any other issuer has the header typ: at+jwt or carries the claim named by AccessTokenClaim. A token with nonce, at_hash or c_hash is an ID token and is refused. AccessTokenClaim may not name a claim ID tokens carry (sub, aud, nonce, azp, roles, …); startup fails otherwise. When the SSO authority issued the token, it must also pass the admission rules.

A token proves identity only. Sign-in succeeds only for an existing, enabled, external Beacon user the token names, and the session carries that user’s Beacon roles. Without user management every login-form JWT and REST bearer token is refused. Every failure answers “Invalid username or password.” and the log never carries the user name.

Option 2: External identity provider (JWT/OAuth)

Section titled “Option 2: External identity provider (JWT/OAuth)”

External users must exist in Beacon before they sign in with a token (single sign-on provisions them on first sign-in). Pre-register them on the Users page, with POST /beacon/api/users/external, or in code:

var users = serviceProvider.GetRequiredService<IUserManagementService>();
await users.CreateExternalUserAsync(new CreateExternalUserRequest
{
ExternalId = "subject-from-the-token", // the token's sub (or ClaimsMapping.UserIdClaim)
UserName = "john.doe",
Email = "john@example.com",
DisplayName = "John Doe",
RoleIds = [editorRoleId]
});

Beacon finds the user by the token subject together with the token issuer:

  • The subject is ClaimsMapping.UserIdClaim (sub by default). When a custom claim is configured and missing, the token is refused; Beacon never falls back to sub.
  • A user stored under the token issuer (as SSO stores users) matches. A user pre-registered without an identity provider matches on the subject alone only when exactly one issuer is configured.
  • Internal users and super admins never match a token. A disabled or archived match refuses the token.
  • Users created by MCP caller provisioning (keyed on the Entra oid) get no REST session from a bearer token.

SSO-provisioned users match a bearer token only when its iss and sub equal the ID token’s. For Entra, a v2.0 access token issued to the same application.

public sealed class MyAuthenticationProvider(IMyAuthService auth) : IBeaconAuthenticationProvider
{
public async Task<AuthenticationResult> AuthenticateAsync(string username, string password, CancellationToken cancellationToken = default)
{
var result = await auth.ValidateCredentialsAsync(username, password, cancellationToken);
if (!result.Success)
{
return AuthenticationResult.Failed("Invalid username or password.");
}
return AuthenticationResult.Succeeded(new AuthenticatedUser
{
UserId = result.User.Id,
UserName = result.User.UserName,
Email = result.User.Email,
DisplayName = result.User.FullName,
Roles = result.User.Roles
});
}
[Obsolete("Beacon never calls it.")]
public Task<bool> ValidateSessionAsync(string userId, CancellationToken cancellationToken = default) => Task.FromResult(true);
public Task SignOutAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
}

While local login is off, whichever provider authenticated a password, only a break-glass account signs in (see below).

With OIDC enabled, Beacon admits only subjects that pass its rules, before it provisions anything: the tenant allow-list (AllowedTenants, or an explicit AllowAnyTenant), the guest block (BlockGuests, on by default), then the optional RequiredRoles/RequiredGroups. A bearer token on the REST API, and a token returned to the login form, that the SSO authority issued passes the same rules. A refused sign-in is logged at Warning with the reason, tenant, issuer and a hash of the subject.

With RequireMfa: true (off by default) an interactive SSO sign-in must show multi-factor authentication in its ID token (amr containing mfa, or an acr listed in AcceptedAcrValues) or it is refused and the login page says so. With Microsoft Entra ID, request amr as an optional ID-token claim. RequireMfa applies to the interactive SSO sign-in. Bearer tokens (REST and /beacon/mcp), API keys, the login-form JWT flow and password sign-in (break-glass included) are authenticated by their own credentials; set the MFA requirement for tokens in the identity provider that issues them.

SSO provisions new users without a role unless DefaultRoleName is set; they see “Your account has no access yet” until a role is assigned. The full option table is on Configuration → OIDC / SSO.

Password sign-in through the login form (POST /beacon/api/auth/login) is on by default. A deployment where people sign in through single sign-on should turn it off, so every sign-in passes the identity provider’s MFA, conditional access and offboarding, and keep one emergency account:

{
"Beacon": {
"UserManagement": {
"LocalLogin": {
"Enabled": false,
"BreakGlassUserNames": [ "superadmin" ]
}
}
}
}

With Enabled: false:

  • POST /beacon/api/auth/login is not mapped, unless a break-glass account is listed. Then every name gets the same password check (so timing reveals nothing), and only a stored, enabled, internal user whose stored user name is on the list signs in, whichever provider authenticated the password. Other accounts get the generic answer and the attempt logs PasswordSignInRefused (7120), without the name typed.
  • The login page shows only the single sign-on button. A break-glass account opens the password form at /beacon/login?local=1.
  • A break-glass sign-in logs BreakGlassSignIn (7119) at Warning. Treat every one as an incident.

BreakGlassUserNames holds up to 3 names, each compared exactly (case included) with the stored user name; the holder may type that name or the account’s e-mail address. A blank entry or a longer list fails startup, and so does turning local login off with no OIDC, no BeaconApiOptions.ExternalLogin (a host-owned login page) and no break-glass account. The JWT login API (ExternalLoginEndpoint) does not count for this check. The first-run super admin is a password account: list it (or another internal Admin) before turning local login off.

Password sign-in, and the first-run setup, are throttled per client address: 10 login attempts per 60 seconds, then 429 with Retry-After. Behind a reverse proxy configure forwarded headers, or the limit applies to the proxy’s address.

Whoever sets a password can sign in with it, so a password an Admin sets (a new internal user, or a reset through IUserManagementService.ResetPasswordAsync in host code) must be replaced at the user’s first sign-in. There is no REST route or UI action that sets another user’s password.

  • The right user name and password answer 401 with passwordChangeRequired: true (no session); a wrong password answers as usual.
  • The login page asks for a new password and sends the same sign-in again with newPassword. When the rules accept it (and it differs from the one set), it becomes the user’s password, the requirement clears, the security stamp changes and the user is signed in. A refused password answers 400 with the reason.
  • Changing your own password (POST /beacon/api/user-settings/change-password) also clears the requirement.

A Beacon.Auth session is checked on the server, not only by its cookie:

  • Revalidation. With user management on, a session is checked against the user store at most every Beacon:Authentication:Session:RevalidateMinutes (default 5; 0 checks every request). The user must exist, be enabled, not archived, and carry the security stamp the session signed in with; otherwise the session is signed out (401 on the API). The session’s roles are replaced by the stored roles (plus any identity-provider mapped permissions within their lifetime). A host without user management keeps its own principals: only the absolute lifetime applies.
  • Security stamp. It changes when a password is changed or reset, when the user is disabled, re-enabled or archived, when their roles, user name or e-mail address change, and on Sign out everywhere. Changing your own password keeps the current session and ends the others.
  • Sign out everywhere. An Admin, or a UserAdmin for a user they may change, ends every session of a user from the user dialog (POST /beacon/api/users/{id}/sign-out-everywhere). A user ends all their own sessions from the account menu (POST /beacon/api/auth/logout?everywhere=true), offered when /auth/me reports signOutEverywhereEnabled. When the sessions cannot be signed out (user management off, or an API key or MCP caller) the answer is 409. API keys are not affected.
  • Absolute lifetime. A session ends Beacon:Authentication:Session:AbsoluteLifetimeHours (default 12) after sign-in, however often sliding expiration or “remember me” renews its cookie. The requested cookie lifetimes (options.Authentication.CookieExpirationHours, default 24, and RememberMeExpirationDays, default 30, for “remember me”) are capped by it.
  • Realtime. A SignalR connection is checked when opened and closed when the session’s next check is due (at least one minute); the shell reconnects through a new check.
{
"Beacon": {
"Authentication": {
"Session": {
"RevalidateMinutes": 5,
"AbsoluteLifetimeHours": 12
}
}
}
}

Both are validated at startup (RevalidateMinutes 0–1440, AbsoluteLifetimeHours 1–8760). A session a check ends logs SessionEnded (7118); a sign-out everywhere logs UserSessionsRevoked (7117).

The cookie is HttpOnly, SameSite=Lax and Secure when the request is HTTPS. It is protected by ASP.NET Core Data Protection, which belongs to the host: see Configuration → Data Protection, including what to do after restoring a Beacon database into another environment.

Every user_roles row records when the role was assigned (AssignedAt) and who assigned it (AssignedByUserId, the assigner’s external id): when an Admin or UserAdmin creates a user, changes a user’s roles, or calls IUserManagementService.AssignRoleAsync. It is empty where Beacon grants the role itself: the default role at a first SSO or MCP sign-in, and the super admin’s Admin role at setup. Identity-provider mapped permissions are never stored.

Before a user leaves, find what they hold: open the user on the User Management page and choose Show owned resources (an Auditor or a UserAdmin; nothing is read until you do). It lists, one kind per tab and ten to a page, the live queries, subscriptions, data contracts, recipients and migration jobs the user owns (the user whose access scheduled runs act with; recipients and migration jobs have no owner), created, or changed last, with the dates. Reassign what they own (see Subscriptions: Change the owner) before you disable them, since scheduled runs of a disabled owner are skipped. Archived resources are not listed; an archived user is found too.

The same list is GET /beacon/api/users/{id}/owned-resources?kind={kind} (paged and sortable like other lists, oldest first; 404 for an unknown user). kind is a number: 0 queries, 1 subscriptions, 2 data contracts, 3 recipients, 4 migration jobs. Each item carries id, kind, name, isOwner, isCreator, isLastModifier, nameWithheld, createdTime and modifiedTime. Who created or last changed a resource is recorded from the version that added the security audit trail on; resources from before show neither.

The list crosses projects, so names follow what the caller may read: an Auditor sees every name; a UserAdmin sees the names of the resources its projects reach (as the resource’s own list shows them; recipients belong to no project and are always named), and every other resource by id and kind only: name empty and nameWithheld: true, shown as Name withheld. Every read is recorded first in the security audit trail (identity.owned_resources_read, the kind and the page) and fails when that record cannot be written.

Sign-ins (password, single sign-on and break-glass, successful, failed and refused), sign-outs, sessions Beacon ended, first-run setup (the super admin, and setup tokens refused), users created, provisioned, updated, enabled, disabled and archived, passwords changed or set by an administrator, and role changes are recorded in the security audit trail, by user id, never by name, e-mail address, the sign-in name typed or an external id. A failed password sign-in names the user the sign-in name resolved to, when it did, and the attempt by a keyed hash of the name typed, so many attempts on one name can be told from attempts spread over many names without the name being stored. A right password refused because an administrator set it and the user must choose a new one is recorded as a refused sign-in (password_change_required). An Auditor reads them on the Security Audit page.

FieldDescription
ExternalIda GUID for internal users; the token subject for external users
UserNameunique user name
Email, DisplayNameoptional
IsInternalUserthe password is stored in Beacon
PasswordHashPBKDF2-SHA256 hash; null for external users
IsSuperAdminpasses every check
IsEnabledenabled or disabled
LastLoginAtlast sign-in with the user’s own identity (password, SSO, an identity-provider token; never an API key)
SecurityStampchanges whenever every session of the user must end
MustChangePasswordthe user must choose a new password before signing in
UserRolesassigned roles, with AssignedAt and AssignedByUserId

The users, roles and user_roles tables live in the beacon schema and are created by Beacon’s migrations.

All routes are under /beacon/api. The OpenAPI document is at /openapi/v1.json; see also Routes & endpoints.

RouteDescription
POST /auth/loginSign in with user name and password. 401 with passwordChangeRequired: true when an Admin set the password (send newPassword). Not mapped while local login is off and no break-glass account is listed. Throttled per address
POST /auth/logoutEnd the current session; needs the antiforgery token (X-XSRF-TOKEN), 400 without it. ?everywhere=true ends every session of the user
GET /auth/meThe current user, and flags such as realtimeEnabled and signOutEverywhereEnabled. Anonymous
GET /auth/permissionscanRead, canWrite, isAdmin and the functional permissions held now
GET /auth/ssoWhether SSO is offered, its button label and localLoginEnabled. Anonymous
GET /auth/sso/challengeStarts the SSO sign-in
GET /csrfIssues the antiforgery token for a browser session (400 for callers authenticated by an Authorization header)
GET /user-settingsThe caller’s own settings
POST /user-settings/change-passwordChange your own password (needs the current one); ends your other sessions

For an Admin or a UserAdmin (see Functional roles for a UserAdmin’s limits).

RouteDescription
GET /usersthe user directory
GET /users/rolesevery role, with the permission each functional role grants
POST /users/internalcreate an internal user with roles (Admin only)
POST /users/externalcreate an external user with roles
PUT /users/{id}change a user’s display name, e-mail address, user name or enabled state (user name and e-mail address: Admin only)
POST /users/{id}/toggle-enabledenable or disable a user
PUT /users/{id}/rolesreplace a user’s roles (ends their sessions when they change)
POST /users/{id}/sign-out-everywhereend every session of the user (204; 409 when user management is off)
GET /users/{id}/owned-resourcesthe user’s live resources of one kind, paged; for an Auditor or a UserAdmin (see Owned resources)
RouteDescription
GET /setup/statuswhether first-run setup is needed
POST /setup/superadmincreate the super admin (first run only; needs setupToken, 403 when missing or wrong)
GET /setup/rolesthe roles, anonymous only while setup is open
SymptomCheck
No login pageEnableLoginForm = true, AddBeaconCookieAuthentication("/beacon"), the login endpoints and middleware mapped; the page is /beacon/login
Setup page does not appearit appears only while no super admin has ever existed (an archived one counts); other users never close it
Setup token rejectedthe configured token, or the one printed to the process console of the replica that served the page
External user gets 401the user exists, is enabled and is external; issuer and audience configured; the token is an access token. The server logs a reason code (NoBeaconUser, DisabledUser, IdToken, NotAccessToken, NotAdmitted) with tenant, issuer and a subject hash
NotAccessToken from a non-Entra providerset AccessTokenClaim to a claim the provider puts in access tokens only
SSO user sees “no access yet”assign a role, or set DefaultRoleName; the user then selects Check again
Login answers 42910 attempts per 60 seconds per address; behind a proxy, configure forwarded headers
  • First-run setup needs a token (Beacon:UserManagement:SetupToken, at least 32 characters, or the one printed to the process console). Automation calling POST /beacon/api/setup/superadmin must send setupToken. Setup is “no super admin has ever existed”, and until then SSO and MCP provisioning create no user.
  • SSO requires an admission decision. With OIDC enabled set AllowedTenants or AllowAnyTenant: true, or the host does not start. Guests are blocked by default. New SSO users get no role unless DefaultRoleName is set. Review the users earlier versions provisioned with Viewer.
  • JWT validation is mandatory and strict, tokens must prove they are access tokens (set AccessTokenClaim if your provider’s access tokens carry neither typ: at+jwt nor an Entra client claim), and REST bearer tokens must name an existing, enabled, external Beacon user.
  • Sign-out is POST /beacon/api/auth/logout with the antiforgery token; GET /beacon/api/auth/signout was removed, and the OIDC front-channel sign-out path is served only with EnableFrontChannelLogout: true.
  • Disabling, re-enabling and archiving revoke API keys.
  • Sessions are checked on the server and end 12 hours after sign-in by default; “remember me” no longer outlasts the absolute lifetime. The AddUserSecurityStamp migration adds security_stamp and must_change_password.
  • New projects are restricted, and a stored user is an Admin by current roles only. See Authorization → Upgrading.
  • The encryption key is checked in every environment, and Data Protection is the host’s (call AddBeaconDataProtection or configure your own; everyone signs in again once).
  • Local login stays on by default; with SSO, turn it off and list a break-glass account.
  • Passwords an Admin sets must be replaced at first sign-in; a custom login client must handle passwordChangeRequired and send newPassword.
  • Functional roles are created when roles are next listed; a role of yours with one of their names grants nothing until renamed.
  • Public API. IUserManagementService.GetOrCreateExternalUserAsync takes a nullable defaultRoleName; CompleteRequiredPasswordChangeAsync is a new default member; LoginRequest gains NewPassword and LoginResponse PasswordChangeRequired (positional records: recompile); CreateInternalUserRequest/CreateExternalUserRequest gain AssignedByUserId.